diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/App.scss | 2 | ||||
| -rw-r--r-- | src/components/login_form/login_form.js | 3 | ||||
| -rw-r--r-- | src/components/oauth_callback/oauth_callback.js | 3 | ||||
| -rw-r--r-- | src/components/poll/poll.js | 39 | ||||
| -rw-r--r-- | src/components/poll/poll.vue | 16 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.js | 7 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.vue | 32 | ||||
| -rw-r--r-- | src/components/status/status.vue | 2 | ||||
| -rw-r--r-- | src/lib/persisted_state.js | 3 | ||||
| -rw-r--r-- | src/main.js | 4 | ||||
| -rw-r--r-- | src/modules/chat.js | 2 | ||||
| -rw-r--r-- | src/modules/oauth.js | 8 | ||||
| -rw-r--r-- | src/modules/polls.js | 70 | ||||
| -rw-r--r-- | src/modules/statuses.js | 12 | ||||
| -rw-r--r-- | src/modules/users.js | 2 | ||||
| -rw-r--r-- | src/services/new_api/user_search.js | 3 |
16 files changed, 159 insertions, 49 deletions
diff --git a/src/App.scss b/src/App.scss index db9f62f0..e4c764bf 100644 --- a/src/App.scss +++ b/src/App.scss @@ -200,6 +200,7 @@ input, textarea, .select { } } + label::before { + flex-shrink: 0; display: inline-block; content: ''; transition: box-shadow 200ms; @@ -236,6 +237,7 @@ input, textarea, .select { } } + label::before { + flex-shrink: 0; display: inline-block; content: '✔'; transition: color 200ms; diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js index 93214646..4a5b1965 100644 --- a/src/components/login_form/login_form.js +++ b/src/components/login_form/login_form.js @@ -26,9 +26,10 @@ const LoginForm = { this.isTokenAuth ? this.submitToken() : this.submitPassword() }, submitToken () { - const { clientId } = this.oauth + const { clientId, clientSecret } = this.oauth const data = { clientId, + clientSecret, instance: this.instance.server, commit: this.$store.commit } diff --git a/src/components/oauth_callback/oauth_callback.js b/src/components/oauth_callback/oauth_callback.js index 2c6ca235..a3c7b7f9 100644 --- a/src/components/oauth_callback/oauth_callback.js +++ b/src/components/oauth_callback/oauth_callback.js @@ -4,10 +4,11 @@ const oac = { props: ['code'], mounted () { if (this.code) { - const { clientId } = this.$store.state.oauth + const { clientId, clientSecret } = this.$store.state.oauth oauth.getToken({ clientId, + clientSecret, instance: this.$store.state.instance.server, code: this.code }).then((result) => { diff --git a/src/components/poll/poll.js b/src/components/poll/poll.js index ecacbc35..98db5582 100644 --- a/src/components/poll/poll.js +++ b/src/components/poll/poll.js @@ -3,26 +3,39 @@ import { forEach, map } from 'lodash' export default { name: 'Poll', - props: ['poll', 'statusId'], + props: ['basePoll'], components: { Timeago }, data () { return { loading: false, - choices: [], - refreshInterval: null + choices: [] } }, created () { - this.refreshInterval = setTimeout(this.refreshPoll, 30 * 1000) - // Initialize choices to booleans and set its length to match options - this.choices = this.poll.options.map(_ => false) + if (!this.$store.state.polls.pollsObject[this.pollId]) { + this.$store.dispatch('mergeOrAddPoll', this.basePoll) + } + this.$store.dispatch('trackPoll', this.pollId) }, destroyed () { - clearTimeout(this.refreshInterval) + this.$store.dispatch('untrackPoll', this.pollId) }, 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 () { - return Date.now() > Date.parse(this.poll.expires_at) + return (this.poll && this.poll.expired) || false }, loggedIn () { return this.$store.state.users.currentUser @@ -33,9 +46,6 @@ export default { totalVotesCount () { return this.poll.votes_count }, - expiresAt () { - return Date.parse(this.poll.expires_at).toLocaleString() - }, containerClass () { return { loading: this.loading @@ -55,11 +65,6 @@ export default { } }, methods: { - refreshPoll () { - if (this.expired) return - this.fetchPoll() - this.refreshInterval = setTimeout(this.refreshPoll, 30 * 1000) - }, percentageForOption (count) { return this.totalVotesCount === 0 ? 0 : Math.round(count / this.totalVotesCount * 100) }, @@ -104,4 +109,4 @@ export default { }) } } -}
\ No newline at end of file +} diff --git a/src/components/poll/poll.vue b/src/components/poll/poll.vue index 28e9f4a8..bb67101a 100644 --- a/src/components/poll/poll.vue +++ b/src/components/poll/poll.vue @@ -2,7 +2,7 @@ <div class="poll" v-bind:class="containerClass"> <div class="poll-option" - v-for="(option, index) in poll.options" + v-for="(option, index) in options" :key="index" > <div v-if="showResults" :title="resultTitle(option)" class="option-result"> @@ -31,8 +31,8 @@ :disabled="loading" :value="index" > - <label> - {{option.title}} + <label class="option-vote"> + <div>{{option.title}}</div> </label> </div> </div> @@ -50,7 +50,7 @@ {{totalVotesCount}} {{ $t("polls.votes") }} · </div> <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> </div> </div> @@ -68,8 +68,7 @@ margin: 0 0 0.5em; } .poll-option { - margin: 0.5em 0; - height: 1.5em; + margin: 0.75em 0.5em; } .option-result { height: 100%; @@ -87,6 +86,7 @@ } .result-percentage { width: 3.5em; + flex-shrink: 0; } .result-fill { height: 100%; @@ -99,6 +99,10 @@ left: 0; transition: width 0.5s; } + .option-vote { + display: flex; + align-items: center; + } input { width: 3.5em; } diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 5dbb1c9d..ef6b0fce 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -269,8 +269,11 @@ const PostStatusForm = { resize (e) { const target = e.target || e if (!(target instanceof window.Element)) { return } - const vertPadding = Number(window.getComputedStyle(target)['padding-top'].substr(0, 1)) + - Number(window.getComputedStyle(target)['padding-bottom'].substr(0, 1)) + const topPaddingStr = window.getComputedStyle(target)['padding-top'] + 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 target.style.height = 'auto' target.style.height = `${target.scrollHeight - vertPadding}px` diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index fbeaf39b..67cdc721 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -48,7 +48,7 @@ <EmojiInput :suggest="emojiUserSuggestor" v-model="newStatus.status" - class="form-control" + class="form-control main-input" > <textarea ref="textarea" @@ -65,6 +65,13 @@ class="form-post-body" > </textarea> + <p + v-if="hasStatusLengthLimit" + class="character-counter faint" + :class="{ error: isOverLengthLimit }" + > + {{ charactersLeft }} + </p> </EmojiInput> <div class="visibility-tray"> <div class="text-format" v-if="postFormats.length > 1"> @@ -109,8 +116,6 @@ /> </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-else-if="isOverLengthLimit" disabled class="btn btn-default">{{$t('general.submit')}}</button> @@ -304,10 +309,12 @@ } .form-post-body { - line-height:16px; + height: 16px; // Only affects the empty-height + line-height: 16px; resize: none; overflow: hidden; transition: min-height 200ms 100ms; + padding-bottom: 1.75em; min-height: 1px; box-sizing: content-box; } @@ -316,6 +323,23 @@ 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 { cursor: pointer; } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 58402f7e..440e1957 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -124,7 +124,7 @@ </div> <div v-if="status.poll && status.poll.options"> - <poll :poll="status.poll" :status-id="status.id" /> + <poll :base-poll="status.poll" /> </div> <div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body"> diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js index 7ab89c12..cad7ea25 100644 --- a/src/lib/persisted_state.js +++ b/src/lib/persisted_state.js @@ -19,7 +19,8 @@ const saveImmedeatelyActions = [ 'setHighlight', 'setOption', 'setClientData', - 'setToken' + 'setToken', + 'clearToken' ] const defaultStorage = (() => { diff --git a/src/main.js b/src/main.js index d0f2674b..3287fa2b 100644 --- a/src/main.js +++ b/src/main.js @@ -14,6 +14,7 @@ import authFlowModule from './modules/auth_flow.js' import mediaViewerModule from './modules/media_viewer.js' import oauthTokensModule from './modules/oauth_tokens.js' import reportsModule from './modules/reports.js' +import pollsModule from './modules/polls.js' import VueI18n from 'vue-i18n' @@ -72,7 +73,8 @@ const persistedStateOptions = { authFlow: authFlowModule, mediaViewer: mediaViewerModule, oauthTokens: oauthTokensModule, - reports: reportsModule + reports: reportsModule, + polls: pollsModule }, plugins: [persistedState, pushNotifications], strict: false // Socket modifies itself, let's ignore this for now. diff --git a/src/modules/chat.js b/src/modules/chat.js index 2804e577..4d8d6699 100644 --- a/src/modules/chat.js +++ b/src/modules/chat.js @@ -21,7 +21,7 @@ const chat = { }, actions: { disconnectFromChat (store) { - store.state.socket.disconnect() + store.state.socket && store.state.socket.disconnect() }, initializeChat (store, socket) { const channel = socket.channel('chat:public') diff --git a/src/modules/oauth.js b/src/modules/oauth.js index 11cb10fe..a2a83450 100644 --- a/src/modules/oauth.js +++ b/src/modules/oauth.js @@ -1,3 +1,5 @@ +import { delete as del } from 'vue' + const oauth = { state: { clientId: false, @@ -22,6 +24,12 @@ const oauth = { }, setToken (state, 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: { diff --git a/src/modules/polls.js b/src/modules/polls.js new file mode 100644 index 00000000..e6158b63 --- /dev/null +++ b/src/modules/polls.js @@ -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 diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 262ff80a..9b11a13e 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -583,18 +583,6 @@ const statuses = { ]).then(([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 diff --git a/src/modules/users.js b/src/modules/users.js index 22340271..1e0b16f5 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -399,7 +399,7 @@ const users = { logout (store) { store.commit('clearCurrentUser') store.dispatch('disconnectFromChat') - store.commit('setToken', false) + store.commit('clearToken') store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken())) store.dispatch('stopFetching', 'notifications') diff --git a/src/services/new_api/user_search.js b/src/services/new_api/user_search.js index 869afa9c..c5a87cce 100644 --- a/src/services/new_api/user_search.js +++ b/src/services/new_api/user_search.js @@ -6,7 +6,8 @@ const search = ({query, store}) => { store, url: '/api/v1/accounts/search', params: { - q: query + q: query, + resolve: true } }) .then((data) => data.json()) |
