Merge remote-tracking branch 'upstream/develop' into shigusegubu
* upstream/develop: review lint fix fix all known problems with clicks on autocomplete emojis fix specificity that made attachments misalign Keep statuses always enabled add resolve param to user search api request change isPinned to noIdUpdate Fix: problems with polls state Move character counter into the input box fix for #553 Make scss change for tab switcher only delete state.token instead of setting false use clientSecret in login flow fix error breaking logout flow make sure to clear old token when logout Move poll state handling to its own module Fix/messed up long polls A small sass fix for #577 reset margin property of form controls
This commit is contained in:
commit
6aa57d377d
20 changed files with 183 additions and 60 deletions
|
@ -131,6 +131,7 @@ input, textarea, .select {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-family: var(--inputFont, sans-serif);
|
font-family: var(--inputFont, sans-serif);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
padding: 8px .5em;
|
padding: 8px .5em;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -199,6 +200,7 @@ input, textarea, .select {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+ label::before {
|
+ label::before {
|
||||||
|
flex-shrink: 0;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
content: '';
|
content: '';
|
||||||
transition: box-shadow 200ms;
|
transition: box-shadow 200ms;
|
||||||
|
@ -235,6 +237,7 @@ input, textarea, .select {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+ label::before {
|
+ label::before {
|
||||||
|
flex-shrink: 0;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
content: '✔';
|
content: '✔';
|
||||||
transition: color 200ms;
|
transition: color 200ms;
|
||||||
|
|
|
@ -59,7 +59,8 @@ const EmojiInput = {
|
||||||
input: undefined,
|
input: undefined,
|
||||||
highlighted: 0,
|
highlighted: 0,
|
||||||
caret: 0,
|
caret: 0,
|
||||||
focused: false
|
focused: false,
|
||||||
|
blurTimeout: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -122,12 +123,12 @@ const EmojiInput = {
|
||||||
this.$emit('input', newValue)
|
this.$emit('input', newValue)
|
||||||
this.caret = 0
|
this.caret = 0
|
||||||
},
|
},
|
||||||
replaceText (e) {
|
replaceText (e, suggestion) {
|
||||||
const len = this.suggestions.length || 0
|
const len = this.suggestions.length || 0
|
||||||
if (this.textAtCaret.length === 1) { return }
|
if (this.textAtCaret.length === 1) { return }
|
||||||
if (len > 0) {
|
if (len > 0 || suggestion) {
|
||||||
const suggestion = this.suggestions[this.highlighted]
|
const chosenSuggestion = suggestion || this.suggestions[this.highlighted]
|
||||||
const replacement = suggestion.replacement
|
const replacement = chosenSuggestion.replacement
|
||||||
const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement)
|
const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement)
|
||||||
this.$emit('input', newValue)
|
this.$emit('input', newValue)
|
||||||
this.highlighted = 0
|
this.highlighted = 0
|
||||||
|
@ -173,13 +174,21 @@ const EmojiInput = {
|
||||||
onBlur (e) {
|
onBlur (e) {
|
||||||
// Clicking on any suggestion removes focus from autocomplete,
|
// Clicking on any suggestion removes focus from autocomplete,
|
||||||
// preventing click handler ever executing.
|
// preventing click handler ever executing.
|
||||||
setTimeout(() => {
|
this.blurTimeout = setTimeout(() => {
|
||||||
this.focused = false
|
this.focused = false
|
||||||
this.setCaret(e)
|
this.setCaret(e)
|
||||||
this.resize()
|
this.resize()
|
||||||
}, 200)
|
}, 200)
|
||||||
},
|
},
|
||||||
|
onClick (e, suggestion) {
|
||||||
|
this.replaceText(e, suggestion)
|
||||||
|
},
|
||||||
onFocus (e) {
|
onFocus (e) {
|
||||||
|
if (this.blurTimeout) {
|
||||||
|
clearTimeout(this.blurTimeout)
|
||||||
|
this.blurTimeout = null
|
||||||
|
}
|
||||||
|
|
||||||
this.focused = true
|
this.focused = true
|
||||||
this.setCaret(e)
|
this.setCaret(e)
|
||||||
this.resize()
|
this.resize()
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div
|
<div
|
||||||
v-for="(suggestion, index) in suggestions"
|
v-for="(suggestion, index) in suggestions"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click.stop.prevent="replaceText"
|
@click.stop.prevent="onClick($event, suggestion)"
|
||||||
class="autocomplete-item"
|
class="autocomplete-item"
|
||||||
:class="{ highlighted: suggestion.highlighted }"
|
:class="{ highlighted: suggestion.highlighted }"
|
||||||
>
|
>
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
|
|
||||||
.attachments, .attachment {
|
// FIXME: specificity problem with this and .attachments.attachment
|
||||||
|
// we shouldn't have the need for .image here
|
||||||
|
.attachment.image {
|
||||||
margin: 0 0.5em 0 0;
|
margin: 0 0.5em 0 0;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -26,9 +26,10 @@ const LoginForm = {
|
||||||
this.isTokenAuth ? this.submitToken() : this.submitPassword()
|
this.isTokenAuth ? this.submitToken() : this.submitPassword()
|
||||||
},
|
},
|
||||||
submitToken () {
|
submitToken () {
|
||||||
const { clientId } = this.oauth
|
const { clientId, clientSecret } = this.oauth
|
||||||
const data = {
|
const data = {
|
||||||
clientId,
|
clientId,
|
||||||
|
clientSecret,
|
||||||
instance: this.instance.server,
|
instance: this.instance.server,
|
||||||
commit: this.$store.commit
|
commit: this.$store.commit
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,11 @@ const oac = {
|
||||||
props: ['code'],
|
props: ['code'],
|
||||||
mounted () {
|
mounted () {
|
||||||
if (this.code) {
|
if (this.code) {
|
||||||
const { clientId } = this.$store.state.oauth
|
const { clientId, clientSecret } = this.$store.state.oauth
|
||||||
|
|
||||||
oauth.getToken({
|
oauth.getToken({
|
||||||
clientId,
|
clientId,
|
||||||
|
clientSecret,
|
||||||
instance: this.$store.state.instance.server,
|
instance: this.$store.state.instance.server,
|
||||||
code: this.code
|
code: this.code
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
|
|
|
@ -3,26 +3,39 @@ import { forEach, map } from 'lodash'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Poll',
|
name: 'Poll',
|
||||||
props: ['poll', 'statusId'],
|
props: ['basePoll'],
|
||||||
components: { Timeago },
|
components: { Timeago },
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
choices: [],
|
choices: []
|
||||||
refreshInterval: null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.refreshInterval = setTimeout(this.refreshPoll, 30 * 1000)
|
if (!this.$store.state.polls.pollsObject[this.pollId]) {
|
||||||
// Initialize choices to booleans and set its length to match options
|
this.$store.dispatch('mergeOrAddPoll', this.basePoll)
|
||||||
this.choices = this.poll.options.map(_ => false)
|
}
|
||||||
|
this.$store.dispatch('trackPoll', this.pollId)
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
clearTimeout(this.refreshInterval)
|
this.$store.dispatch('untrackPoll', this.pollId)
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
pollId () {
|
||||||
|
return this.basePoll.id
|
||||||
|
},
|
||||||
|
poll () {
|
||||||
|
const storePoll = this.$store.state.polls.pollsObject[this.pollId]
|
||||||
|
return storePoll || {}
|
||||||
|
},
|
||||||
|
options () {
|
||||||
|
return (this.poll && this.poll.options) || []
|
||||||
|
},
|
||||||
|
expiresAt () {
|
||||||
|
return (this.poll && this.poll.expires_at) || 0
|
||||||
|
},
|
||||||
expired () {
|
expired () {
|
||||||
return Date.now() > Date.parse(this.poll.expires_at)
|
return (this.poll && this.poll.expired) || false
|
||||||
},
|
},
|
||||||
loggedIn () {
|
loggedIn () {
|
||||||
return this.$store.state.users.currentUser
|
return this.$store.state.users.currentUser
|
||||||
|
@ -33,9 +46,6 @@ export default {
|
||||||
totalVotesCount () {
|
totalVotesCount () {
|
||||||
return this.poll.votes_count
|
return this.poll.votes_count
|
||||||
},
|
},
|
||||||
expiresAt () {
|
|
||||||
return Date.parse(this.poll.expires_at).toLocaleString()
|
|
||||||
},
|
|
||||||
containerClass () {
|
containerClass () {
|
||||||
return {
|
return {
|
||||||
loading: this.loading
|
loading: this.loading
|
||||||
|
@ -55,11 +65,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
refreshPoll () {
|
|
||||||
if (this.expired) return
|
|
||||||
this.fetchPoll()
|
|
||||||
this.refreshInterval = setTimeout(this.refreshPoll, 30 * 1000)
|
|
||||||
},
|
|
||||||
percentageForOption (count) {
|
percentageForOption (count) {
|
||||||
return this.totalVotesCount === 0 ? 0 : Math.round(count / this.totalVotesCount * 100)
|
return this.totalVotesCount === 0 ? 0 : Math.round(count / this.totalVotesCount * 100)
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="poll" v-bind:class="containerClass">
|
<div class="poll" v-bind:class="containerClass">
|
||||||
<div
|
<div
|
||||||
class="poll-option"
|
class="poll-option"
|
||||||
v-for="(option, index) in poll.options"
|
v-for="(option, index) in options"
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<div v-if="showResults" :title="resultTitle(option)" class="option-result">
|
<div v-if="showResults" :title="resultTitle(option)" class="option-result">
|
||||||
|
@ -31,8 +31,8 @@
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:value="index"
|
:value="index"
|
||||||
>
|
>
|
||||||
<label>
|
<label class="option-vote">
|
||||||
{{option.title}}
|
<div>{{option.title}}</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
{{totalVotesCount}} {{ $t("polls.votes") }} ·
|
{{totalVotesCount}} {{ $t("polls.votes") }} ·
|
||||||
</div>
|
</div>
|
||||||
<i18n :path="expired ? 'polls.expired' : 'polls.expires_in'">
|
<i18n :path="expired ? 'polls.expired' : 'polls.expires_in'">
|
||||||
<Timeago :time="this.poll.expires_at" :auto-update="60" :now-threshold="0" />
|
<Timeago :time="this.expiresAt" :auto-update="60" :now-threshold="0" />
|
||||||
</i18n>
|
</i18n>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -68,8 +68,7 @@
|
||||||
margin: 0 0 0.5em;
|
margin: 0 0 0.5em;
|
||||||
}
|
}
|
||||||
.poll-option {
|
.poll-option {
|
||||||
margin: 0.5em 0;
|
margin: 0.75em 0.5em;
|
||||||
height: 1.5em;
|
|
||||||
}
|
}
|
||||||
.option-result {
|
.option-result {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -87,6 +86,7 @@
|
||||||
}
|
}
|
||||||
.result-percentage {
|
.result-percentage {
|
||||||
width: 3.5em;
|
width: 3.5em;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.result-fill {
|
.result-fill {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -99,6 +99,10 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
transition: width 0.5s;
|
transition: width 0.5s;
|
||||||
}
|
}
|
||||||
|
.option-vote {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
input {
|
input {
|
||||||
width: 3.5em;
|
width: 3.5em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,8 +269,11 @@ const PostStatusForm = {
|
||||||
resize (e) {
|
resize (e) {
|
||||||
const target = e.target || e
|
const target = e.target || e
|
||||||
if (!(target instanceof window.Element)) { return }
|
if (!(target instanceof window.Element)) { return }
|
||||||
const vertPadding = Number(window.getComputedStyle(target)['padding-top'].substr(0, 1)) +
|
const topPaddingStr = window.getComputedStyle(target)['padding-top']
|
||||||
Number(window.getComputedStyle(target)['padding-bottom'].substr(0, 1))
|
const bottomPaddingStr = window.getComputedStyle(target)['padding-bottom']
|
||||||
|
// Remove "px" at the end of the values
|
||||||
|
const vertPadding = Number(topPaddingStr.substr(0, topPaddingStr.length - 2)) +
|
||||||
|
Number(bottomPaddingStr.substr(0, bottomPaddingStr.length - 2))
|
||||||
// Auto is needed to make textbox shrink when removing lines
|
// Auto is needed to make textbox shrink when removing lines
|
||||||
target.style.height = 'auto'
|
target.style.height = 'auto'
|
||||||
target.style.height = `${target.scrollHeight - vertPadding}px`
|
target.style.height = `${target.scrollHeight - vertPadding}px`
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
<EmojiInput
|
<EmojiInput
|
||||||
:suggest="emojiUserSuggestor"
|
:suggest="emojiUserSuggestor"
|
||||||
v-model="newStatus.status"
|
v-model="newStatus.status"
|
||||||
class="form-control"
|
class="form-control main-input"
|
||||||
>
|
>
|
||||||
<textarea
|
<textarea
|
||||||
ref="textarea"
|
ref="textarea"
|
||||||
|
@ -65,6 +65,13 @@
|
||||||
class="form-post-body"
|
class="form-post-body"
|
||||||
>
|
>
|
||||||
</textarea>
|
</textarea>
|
||||||
|
<p
|
||||||
|
v-if="hasStatusLengthLimit"
|
||||||
|
class="character-counter faint"
|
||||||
|
:class="{ error: isOverLengthLimit }"
|
||||||
|
>
|
||||||
|
{{ charactersLeft }}
|
||||||
|
</p>
|
||||||
</EmojiInput>
|
</EmojiInput>
|
||||||
<div class="visibility-tray">
|
<div class="visibility-tray">
|
||||||
<div class="text-format" v-if="postFormats.length > 1">
|
<div class="text-format" v-if="postFormats.length > 1">
|
||||||
|
@ -109,8 +116,6 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="isOverLengthLimit" class="error">{{ charactersLeft }}</p>
|
|
||||||
<p class="faint" v-else-if="hasStatusLengthLimit">{{ charactersLeft }}</p>
|
|
||||||
|
|
||||||
<button v-if="posting" disabled class="btn btn-default">{{$t('post_status.posting')}}</button>
|
<button v-if="posting" disabled class="btn btn-default">{{$t('post_status.posting')}}</button>
|
||||||
<button v-else-if="isOverLengthLimit" disabled class="btn btn-default">{{$t('general.submit')}}</button>
|
<button v-else-if="isOverLengthLimit" disabled class="btn btn-default">{{$t('general.submit')}}</button>
|
||||||
|
@ -304,10 +309,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-post-body {
|
.form-post-body {
|
||||||
|
height: 16px; // Only affects the empty-height
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
resize: none;
|
resize: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: min-height 200ms 100ms;
|
transition: min-height 200ms 100ms;
|
||||||
|
padding-bottom: 1.75em;
|
||||||
min-height: 1px;
|
min-height: 1px;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
}
|
}
|
||||||
|
@ -316,6 +323,23 @@
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main-input {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-counter {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0.5em;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
color: $fallback--cRed;
|
||||||
|
color: var(--cRed, $fallback--cRed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="status.poll && status.poll.options">
|
<div v-if="status.poll && status.poll.options">
|
||||||
<poll :poll="status.poll" :status-id="status.id" />
|
<poll :base-poll="status.poll" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body">
|
<div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body">
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div v-if="user" class="user-profile panel panel-default">
|
<div v-if="user" class="user-profile panel panel-default">
|
||||||
<UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/>
|
<UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/>
|
||||||
<tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
|
<tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
|
||||||
<div :label="$t('user_card.statuses')" :disabled="!user.statuses_count">
|
<div :label="$t('user_card.statuses')">
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
<template v-for="statusId in user.pinnedStatuseIds">
|
<template v-for="statusId in user.pinnedStatuseIds">
|
||||||
<Conversation
|
<Conversation
|
||||||
|
|
|
@ -19,7 +19,8 @@ const saveImmedeatelyActions = [
|
||||||
'setHighlight',
|
'setHighlight',
|
||||||
'setOption',
|
'setOption',
|
||||||
'setClientData',
|
'setClientData',
|
||||||
'setToken'
|
'setToken',
|
||||||
|
'clearToken'
|
||||||
]
|
]
|
||||||
|
|
||||||
const defaultStorage = (() => {
|
const defaultStorage = (() => {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import authFlowModule from './modules/auth_flow.js'
|
||||||
import mediaViewerModule from './modules/media_viewer.js'
|
import mediaViewerModule from './modules/media_viewer.js'
|
||||||
import oauthTokensModule from './modules/oauth_tokens.js'
|
import oauthTokensModule from './modules/oauth_tokens.js'
|
||||||
import reportsModule from './modules/reports.js'
|
import reportsModule from './modules/reports.js'
|
||||||
|
import pollsModule from './modules/polls.js'
|
||||||
|
|
||||||
import VueI18n from 'vue-i18n'
|
import VueI18n from 'vue-i18n'
|
||||||
|
|
||||||
|
@ -72,7 +73,8 @@ const persistedStateOptions = {
|
||||||
authFlow: authFlowModule,
|
authFlow: authFlowModule,
|
||||||
mediaViewer: mediaViewerModule,
|
mediaViewer: mediaViewerModule,
|
||||||
oauthTokens: oauthTokensModule,
|
oauthTokens: oauthTokensModule,
|
||||||
reports: reportsModule
|
reports: reportsModule,
|
||||||
|
polls: pollsModule
|
||||||
},
|
},
|
||||||
plugins: [persistedState, pushNotifications],
|
plugins: [persistedState, pushNotifications],
|
||||||
strict: false // Socket modifies itself, let's ignore this for now.
|
strict: false // Socket modifies itself, let's ignore this for now.
|
||||||
|
|
|
@ -21,7 +21,7 @@ const chat = {
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
disconnectFromChat (store) {
|
disconnectFromChat (store) {
|
||||||
store.state.socket.disconnect()
|
store.state.socket && store.state.socket.disconnect()
|
||||||
},
|
},
|
||||||
initializeChat (store, socket) {
|
initializeChat (store, socket) {
|
||||||
const channel = socket.channel('chat:public')
|
const channel = socket.channel('chat:public')
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { delete as del } from 'vue'
|
||||||
|
|
||||||
const oauth = {
|
const oauth = {
|
||||||
state: {
|
state: {
|
||||||
clientId: false,
|
clientId: false,
|
||||||
|
@ -22,6 +24,12 @@ const oauth = {
|
||||||
},
|
},
|
||||||
setToken (state, token) {
|
setToken (state, token) {
|
||||||
state.userToken = token
|
state.userToken = token
|
||||||
|
},
|
||||||
|
clearToken (state) {
|
||||||
|
state.userToken = false
|
||||||
|
// state.token is userToken with older name, coming from persistent state
|
||||||
|
// let's clear it as well, since it is being used as a fallback of state.userToken
|
||||||
|
del(state, 'token')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
|
70
src/modules/polls.js
Normal file
70
src/modules/polls.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { merge } from 'lodash'
|
||||||
|
import { set } from 'vue'
|
||||||
|
|
||||||
|
const polls = {
|
||||||
|
state: {
|
||||||
|
// Contains key = id, value = number of trackers for this poll
|
||||||
|
trackedPolls: {},
|
||||||
|
pollsObject: {}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
mergeOrAddPoll (state, poll) {
|
||||||
|
const existingPoll = state.pollsObject[poll.id]
|
||||||
|
// Make expired-state change trigger re-renders properly
|
||||||
|
poll.expired = Date.now() > Date.parse(poll.expires_at)
|
||||||
|
if (existingPoll) {
|
||||||
|
set(state.pollsObject, poll.id, merge(existingPoll, poll))
|
||||||
|
} else {
|
||||||
|
set(state.pollsObject, poll.id, poll)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trackPoll (state, pollId) {
|
||||||
|
const currentValue = state.trackedPolls[pollId]
|
||||||
|
if (currentValue) {
|
||||||
|
set(state.trackedPolls, pollId, currentValue + 1)
|
||||||
|
} else {
|
||||||
|
set(state.trackedPolls, pollId, 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
untrackPoll (state, pollId) {
|
||||||
|
const currentValue = state.trackedPolls[pollId]
|
||||||
|
if (currentValue) {
|
||||||
|
set(state.trackedPolls, pollId, currentValue - 1)
|
||||||
|
} else {
|
||||||
|
set(state.trackedPolls, pollId, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
mergeOrAddPoll ({ commit }, poll) {
|
||||||
|
commit('mergeOrAddPoll', poll)
|
||||||
|
},
|
||||||
|
updateTrackedPoll ({ rootState, dispatch, commit }, pollId) {
|
||||||
|
rootState.api.backendInteractor.fetchPoll(pollId).then(poll => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (rootState.polls.trackedPolls[pollId]) {
|
||||||
|
dispatch('updateTrackedPoll', pollId)
|
||||||
|
}
|
||||||
|
}, 30 * 1000)
|
||||||
|
commit('mergeOrAddPoll', poll)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
trackPoll ({ rootState, commit, dispatch }, pollId) {
|
||||||
|
if (!rootState.polls.trackedPolls[pollId]) {
|
||||||
|
setTimeout(() => dispatch('updateTrackedPoll', pollId), 30 * 1000)
|
||||||
|
}
|
||||||
|
commit('trackPoll', pollId)
|
||||||
|
},
|
||||||
|
untrackPoll ({ commit }, pollId) {
|
||||||
|
commit('untrackPoll', pollId)
|
||||||
|
},
|
||||||
|
votePoll ({ rootState, commit }, { id, pollId, choices }) {
|
||||||
|
return rootState.api.backendInteractor.vote(pollId, choices).then(poll => {
|
||||||
|
commit('mergeOrAddPoll', poll)
|
||||||
|
return poll
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default polls
|
|
@ -146,7 +146,8 @@ const removeStatusFromGlobalStorage = (state, status) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false, userId }) => {
|
const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {},
|
||||||
|
noIdUpdate = false, userId }) => {
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if (!isArray(statuses)) {
|
if (!isArray(statuses)) {
|
||||||
return false
|
return false
|
||||||
|
@ -543,7 +544,7 @@ const statuses = {
|
||||||
},
|
},
|
||||||
fetchPinnedStatuses ({ rootState, dispatch }, userId) {
|
fetchPinnedStatuses ({ rootState, dispatch }, userId) {
|
||||||
rootState.api.backendInteractor.fetchPinnedStatuses(userId)
|
rootState.api.backendInteractor.fetchPinnedStatuses(userId)
|
||||||
.then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true }))
|
.then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true, noIdUpdate: true }))
|
||||||
},
|
},
|
||||||
pinStatus ({ rootState, commit }, statusId) {
|
pinStatus ({ rootState, commit }, statusId) {
|
||||||
return rootState.api.backendInteractor.pinOwnStatus(statusId)
|
return rootState.api.backendInteractor.pinOwnStatus(statusId)
|
||||||
|
@ -582,18 +583,6 @@ const statuses = {
|
||||||
]).then(([favoritedByUsers, rebloggedByUsers]) =>
|
]).then(([favoritedByUsers, rebloggedByUsers]) =>
|
||||||
commit('addFavsAndRepeats', { id, favoritedByUsers, rebloggedByUsers })
|
commit('addFavsAndRepeats', { id, favoritedByUsers, rebloggedByUsers })
|
||||||
)
|
)
|
||||||
},
|
|
||||||
votePoll ({ rootState, commit }, { id, pollId, choices }) {
|
|
||||||
return rootState.api.backendInteractor.vote(pollId, choices).then(poll => {
|
|
||||||
commit('updateStatusWithPoll', { id, poll })
|
|
||||||
return poll
|
|
||||||
})
|
|
||||||
},
|
|
||||||
refreshPoll ({ rootState, commit }, { id, pollId }) {
|
|
||||||
return rootState.api.backendInteractor.fetchPoll(pollId).then(poll => {
|
|
||||||
commit('updateStatusWithPoll', { id, poll })
|
|
||||||
return poll
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mutations
|
mutations
|
||||||
|
|
|
@ -399,7 +399,7 @@ const users = {
|
||||||
logout (store) {
|
logout (store) {
|
||||||
store.commit('clearCurrentUser')
|
store.commit('clearCurrentUser')
|
||||||
store.dispatch('disconnectFromChat')
|
store.dispatch('disconnectFromChat')
|
||||||
store.commit('setToken', false)
|
store.commit('clearToken')
|
||||||
store.dispatch('stopFetching', 'friends')
|
store.dispatch('stopFetching', 'friends')
|
||||||
store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))
|
store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))
|
||||||
store.dispatch('stopFetching', 'notifications')
|
store.dispatch('stopFetching', 'notifications')
|
||||||
|
|
|
@ -6,7 +6,8 @@ const search = ({query, store}) => {
|
||||||
store,
|
store,
|
||||||
url: '/api/v1/accounts/search',
|
url: '/api/v1/accounts/search',
|
||||||
params: {
|
params: {
|
||||||
q: query
|
q: query,
|
||||||
|
resolve: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((data) => data.json())
|
.then((data) => data.json())
|
||||||
|
|
Loading…
Add table
Reference in a new issue