diff options
| author | Henry Jameson <me@hjkos.com> | 2018-12-05 10:43:03 +0300 |
|---|---|---|
| committer | Henry Jameson <me@hjkos.com> | 2018-12-05 10:43:03 +0300 |
| commit | 51cf4dc298303127c9d59015f4eef52f6f6e1103 (patch) | |
| tree | d9e13fd7069f9af3985c39b2b938b64ea8ac0217 | |
| parent | e95b6c7e538e5b65ee81fd5e5d3cb905609c6bab (diff) | |
| parent | 914386270796fa2ec460b828710ee31ef870ea69 (diff) | |
Merge remote-tracking branch 'upstream/develop' into feature/theming2
* upstream/develop:
Fix iOS Safari from making videos play fullscreen by default
added PR comments
resolved the lint
used the deleted data param as condition in status template
Switch to "timeline" when pressing user-settings
Added user setting tooltip
made links in user bio always open in new tabs
addressed PR comments
added tooltip
Add userId property to timelines so that we don't overwrite user timeline meant for another user
Added option to auto-hide subject field when it's empty.
removes hacks from notifications storage, adds api call to let server update is_seen attribute
fixes vimium not giving retweet button a hint
Do not use underscore at the beginning of the method
Logout user on password change
Route user to the correct profile URL
Typo
Fix filetype detection
Switch to settings when touching settings
Switch to timeline on nav panel actions
35 files changed, 134 insertions, 52 deletions
diff --git a/src/App.vue b/src/App.vue index 059460f9..9d66b9d4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -11,7 +11,7 @@ </div> <div class='item right'> <user-finder class="nav-icon"></user-finder> - <router-link :to="{ name: 'settings'}"><i class="icon-cog nav-icon"></i></router-link> + <router-link @click.native="activatePanel('timeline')" :to="{ name: 'settings'}"><i class="icon-cog nav-icon" :title="$t('nav.preferences')"></i></router-link> <a href="#" v-if="currentUser" @click.prevent="logout"><i class="icon-logout nav-icon" :title="$t('login.logout')"></i></a> </div> </div> @@ -25,8 +25,8 @@ <div class="sidebar-bounds"> <div class="sidebar-scroller"> <div class="sidebar"> - <user-panel></user-panel> - <nav-panel></nav-panel> + <user-panel :activatePanel="activatePanel"></user-panel> + <nav-panel :activatePanel="activatePanel"></nav-panel> <instance-specific-panel v-if="showInstanceSpecificPanel"></instance-specific-panel> <features-panel v-if="!currentUser"></features-panel> <who-to-follow-panel v-if="currentUser && suggestionsEnabled"></who-to-follow-panel> diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 8795b131..40e2cf1b 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -14,7 +14,7 @@ <StillImage :class="{'small': isSmall}" referrerpolicy="no-referrer" :mimetype="attachment.mimetype" :src="attachment.large_thumb_url || attachment.url"/> </a> - <video :class="{'small': isSmall}" v-if="type === 'video' && !hidden" @loadeddata="onVideoDataLoad" :src="attachment.url" controls :loop="loopVideo"></video> + <video :class="{'small': isSmall}" v-if="type === 'video' && !hidden" @loadeddata="onVideoDataLoad" :src="attachment.url" controls :loop="loopVideo" playsinline></video> <audio v-if="type === 'audio'" :src="attachment.url" controls></audio> diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue index 71cb875e..1decd070 100644 --- a/src/components/favorite_button/favorite_button.vue +++ b/src/components/favorite_button/favorite_button.vue @@ -1,10 +1,10 @@ <template> <div v-if="loggedIn"> - <i :class='classes' class='favorite-button fav-active' @click.prevent='favorite()'/> + <i :class='classes' class='favorite-button fav-active' @click.prevent='favorite()' :title="$t('tool_tip.favorite')"/> <span v-if='!hidePostStatsLocal && status.fave_num > 0'>{{status.fave_num}}</span> </div> <div v-else> - <i :class='classes' class='favorite-button'/> + <i :class='classes' class='favorite-button' :title="$t('tool_tip.favorite')"/> <span v-if='!hidePostStatsLocal && status.fave_num > 0'>{{status.fave_num}}</span> </div> </template> diff --git a/src/components/media_upload/media_upload.vue b/src/components/media_upload/media_upload.vue index 88094ebb..768d3565 100644 --- a/src/components/media_upload/media_upload.vue +++ b/src/components/media_upload/media_upload.vue @@ -1,6 +1,6 @@ <template> <div class="media-upload" @drop.prevent @dragover.prevent="fileDrag" @drop="fileDrop"> - <label class="btn btn-default"> + <label class="btn btn-default" :title="$t('tool_tip.media_upload')"> <i class="icon-spin4 animate-spin" v-if="uploading"></i> <i class="icon-upload" v-if="!uploading"></i> <input type="file" style="position: fixed; top: -100em" multiple="true"></input> diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index ea5d7ea4..19ce56c3 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -1,4 +1,5 @@ const NavPanel = { + props: [ 'activatePanel' ], computed: { currentUser () { return this.$store.state.users.currentUser diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 93deaf97..b224c5f3 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -3,32 +3,32 @@ <div class="panel panel-default"> <ul> <li v-if='currentUser'> - <router-link to='/main/friends'> + <router-link @click.native="activatePanel('timeline')" to='/main/friends'> {{ $t("nav.timeline") }} </router-link> </li> <li v-if='currentUser'> - <router-link :to="{ name: 'mentions', params: { username: currentUser.screen_name } }"> + <router-link @click.native="activatePanel('timeline')" :to="{ name: 'mentions', params: { username: currentUser.screen_name } }"> {{ $t("nav.mentions") }} </router-link> </li> <li v-if='currentUser'> - <router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }"> + <router-link @click.native="activatePanel('timeline')" :to="{ name: 'dms', params: { username: currentUser.screen_name } }"> {{ $t("nav.dms") }} </router-link> </li> <li v-if='currentUser && currentUser.locked'> - <router-link to='/friend-requests'> + <router-link @click.native="activatePanel('timeline')" to='/friend-requests'> {{ $t("nav.friend_requests") }} </router-link> </li> <li> - <router-link to='/main/public'> + <router-link @click.native="activatePanel('timeline')" to='/main/public'> {{ $t("nav.public_tl") }} </router-link> </li> <li> - <router-link to='/main/all'> + <router-link @click.native="activatePanel('timeline')" to='/main/all'> {{ $t("nav.twkn") }} </router-link> </li> diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index f98afbe0..f773d9de 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -17,7 +17,7 @@ <small>{{$t('notifications.favorited_you')}}</small> </span> <span v-if="notification.type === 'repeat'"> - <i class="fa icon-retweet lit"></i> + <i class="fa icon-retweet lit" :title="$t('tool_tip.repeat')"></i> <small>{{$t('notifications.repeated_you')}}</small> </span> <span v-if="notification.type === 'follow'"> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 58956f98..945ffd1f 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -52,7 +52,7 @@ const Notifications = { }, methods: { markAsSeen () { - this.$store.commit('markNotificationsAsSeen', this.visibleNotifications) + this.$store.dispatch('markNotificationsAsSeen', this.visibleNotifications) }, fetchOlderNotifications () { const store = this.$store diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index fa86ee59..789243cf 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -150,6 +150,15 @@ const PostStatusForm = { scopeOptionsEnabled () { return this.$store.state.instance.scopeOptionsEnabled }, + alwaysShowSubject () { + if (typeof this.$store.state.config.alwaysShowSubjectInput !== 'undefined') { + return this.$store.state.config.alwaysShowSubjectInput + } else if (typeof this.$store.state.instance.alwaysShowSubjectInput !== 'undefined') { + return this.$store.state.instance.alwaysShowSubjectInput + } else { + return this.$store.state.instance.scopeOptionsEnabled + } + }, formattingOptionsEnabled () { return this.$store.state.instance.formattingOptionsEnabled } diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 1c79cab3..fcf5c873 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -11,7 +11,7 @@ </i18n> <p v-if="this.newStatus.visibility == 'direct'" class="visibility-notice">{{ $t('post_status.direct_warning') }}</p> <input - v-if="scopeOptionsEnabled" + v-if="newStatus.spoilerText || alwaysShowSubject" type="text" :placeholder="$t('post_status.content_warning')" v-model="newStatus.spoilerText" diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue index 5b1e64b8..c957fb77 100644 --- a/src/components/retweet_button/retweet_button.vue +++ b/src/components/retweet_button/retweet_button.vue @@ -1,7 +1,7 @@ <template> <div v-if="loggedIn"> <template v-if="visibility !== 'private' && visibility !== 'direct'"> - <i :class='classes' class='icon-retweet rt-active' v-on:click.prevent='retweet()'></i> + <i :class='classes' class='retweet-button icon-retweet rt-active' v-on:click.prevent='retweet()' :title="$t('tool_tip.repeat')"></i> <span v-if='!hidePostStatsLocal && status.repeat_num > 0'>{{status.repeat_num}}</span> </template> <template v-else> @@ -9,7 +9,7 @@ </template> </div> <div v-else-if="!loggedIn"> - <i :class='classes' class='icon-retweet'></i> + <i :class='classes' class='icon-retweet' :title="$t('tool_tip.repeat')"></i> <span v-if='!hidePostStatsLocal && status.repeat_num > 0'>{{status.repeat_num}}</span> </div> </template> diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js index 910eea63..91a2014a 100644 --- a/src/components/settings/settings.js +++ b/src/components/settings/settings.js @@ -38,6 +38,10 @@ const settings = { ? instance.subjectLineBehavior : user.subjectLineBehavior, subjectLineBehaviorDefault: instance.subjectLineBehavior, + alwaysShowSubjectInputLocal: typeof user.alwaysShowSubjectInput === 'undefined' + ? instance.alwaysShowSubjectInput + : user.alwaysShowSubjectInput, + alwaysShowSubjectInputDefault: instance.alwaysShowSubjectInput, scopeCopyLocal: user.scopeCopy, scopeCopyDefault: this.$t('settings.values.' + instance.scopeCopy), stopGifs: user.stopGifs, @@ -122,6 +126,9 @@ const settings = { scopeCopyLocal (value) { this.$store.dispatch('setOption', { name: 'scopeCopy', value }) }, + alwaysShowSubjectInputLocal (value) { + this.$store.dispatch('setOption', { name: 'alwaysShowSubjectInput', value }) + }, subjectLineBehaviorLocal (value) { this.$store.dispatch('setOption', { name: 'subjectLineBehavior', value }) }, diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index 7a955203..64b8f231 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -65,6 +65,12 @@ </label> </li> <li> + <input type="checkbox" id="subjectHide" v-model="alwaysShowSubjectInputLocal"> + <label for="subjectHide"> + {{$t('settings.subject_input_always_show')}} {{$t('settings.instance_default', { value: alwaysShowSubjectInputDefault })}} + </label> + </li> + <li> <div> {{$t('settings.subject_line_behavior')}} <label for="subjectLineBehavior" class="select"> diff --git a/src/components/status/status.js b/src/components/status/status.js index 725bc3f8..cb125e45 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -54,6 +54,9 @@ const Status = { const user = this.retweet ? (this.statusoid.retweeted_status.user) : this.statusoid.user return highlightClass(user) }, + deleted () { + return this.statusoid.deleted + }, repeaterStyle () { const user = this.statusoid.user const highlight = this.$store.state.config.highlight diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 428383e3..c28c9473 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -1,5 +1,5 @@ <template> - <div class="status-el" v-if="!hideReply" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]"> + <div class="status-el" v-if="!hideReply && !deleted" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]"> <template v-if="muted && !noReplyLinks"> <div class="media status container muted"> <small><router-link :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small> @@ -13,7 +13,7 @@ <div class="media-body faint"> <a v-if="retweeterHtml" :href="statusoid.user.statusnet_profile_url" class="user-name" :title="'@'+statusoid.user.screen_name" v-html="retweeterHtml"></a> <a v-else :href="statusoid.user.statusnet_profile_url" class="user-name" :title="'@'+statusoid.user.screen_name">{{retweeter}}</a> - <i class='fa icon-retweet retweeted'></i> + <i class='fa icon-retweet retweeted' :title="$t('tool_tip.repeat')"></i> {{$t('timeline.repeated')}} </div> </div> @@ -41,7 +41,7 @@ {{status.in_reply_to_screen_name}} </router-link> </span> - <a v-if="isReply && !noReplyLinks" href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"> + <a v-if="isReply && !noReplyLinks" href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)" :title="$t('tool_tip.reply')"> <i class="icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i> </a> </span> @@ -94,7 +94,7 @@ <div v-if="!noHeading && !noReplyLinks" class='status-actions media-body'> <div v-if="loggedIn"> - <a href="#" v-on:click.prevent="toggleReplying"> + <a href="#" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')"> <i class="icon-reply" :class="{'icon-reply-active': replying}"></i> </a> </div> diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 48f272ca..5a8e5531 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -19,7 +19,9 @@ {{ $t('user_card.follows_you') }} </span> </div> - <a :href="user.statusnet_profile_url" target="blank"><div class="user-screen-name">@{{ user.screen_name }}</div></a> + <router-link class='user-screen-name' :to="{ name: 'user-profile', params: { id: user.id } }"> + @{{user.screen_name}} + </router-link> </div> <div class="approval" v-if="showApproval"> <button class="btn btn-default" @click="approveUser">{{ $t('user_card.approve') }}</button> diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 97cd4983..e7f19953 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -2,7 +2,7 @@ import StillImage from '../still-image/still-image.vue' import { hex2rgb } from '../../services/color_convert/color_convert.js' export default { - props: [ 'user', 'switcher', 'selected', 'hideBio' ], + props: [ 'user', 'switcher', 'selected', 'hideBio', 'activatePanel' ], data () { return { hideUserStatsLocal: typeof this.$store.state.config.hideUserStats === 'undefined' @@ -102,6 +102,14 @@ export default { const store = this.$store store.commit('setProfileView', { v }) } + }, + linkClicked ({target}) { + if (target.tagName === 'SPAN') { + target = target.parentNode + } + if (target.tagName === 'A') { + window.open(target.href, '_blank') + } } } } diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index e8b0ebf9..4f171a56 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -2,8 +2,8 @@ <div id="heading" class="profile-panel-background" :style="headingStyle"> <div class="panel-heading text-center"> <div class='user-info'> - <router-link to='/user-settings' style="float: right; margin-top:16px;" v-if="!isOtherUser"> - <i class="icon-cog usersettings"></i> + <router-link @click.native="activatePanel('timeline')" to='/user-settings' style="float: right; margin-top:16px;" v-if="!isOtherUser"> + <i class="icon-cog usersettings" :title="$t('tool_tip.user_settings')"></i> </router-link> <a :href="user.statusnet_profile_url" target="_blank" class="floater" v-if="isOtherUser"> <i class="icon-link-ext usersettings"></i> @@ -105,7 +105,7 @@ <span v-if="!hideUserStatsLocal">{{user.followers_count}}</span> </div> </div> - <p v-if="!hideBio && user.description_html" class="profile-bio" v-html="user.description_html"></p> + <p @click.prevent="linkClicked" v-if="!hideBio && user.description_html" class="profile-bio" v-html="user.description_html"></p> <p v-else-if="!hideBio" class="profile-bio">{{ user.description }}</p> </div> </div> diff --git a/src/components/user_finder/user_finder.vue b/src/components/user_finder/user_finder.vue index f2556569..8786f6c7 100644 --- a/src/components/user_finder/user_finder.vue +++ b/src/components/user_finder/user_finder.vue @@ -1,7 +1,7 @@ <template> <span class="user-finder-container"> <i class="icon-spin4 user-finder-icon animate-spin-slow" v-if="loading" /> - <a href="#" v-if="hidden"><i class="icon-user-plus user-finder-icon" @click.prevent.stop="toggleHidden"/></a> + <a href="#" v-if="hidden" :title="$t('finder.find_user')" ><i class="icon-user-plus user-finder-icon" @click.prevent.stop="toggleHidden" /></a> <span v-else> <input class="user-finder-input" @keyup.enter="findUser(username)" v-model="username" :placeholder="$t('finder.find_user')" id="user-finder-input" type="text"/> <i class="icon-cancel user-finder-icon" @click.prevent.stop="toggleHidden"/> diff --git a/src/components/user_panel/user_panel.js b/src/components/user_panel/user_panel.js index 15804b88..eb7cb09c 100644 --- a/src/components/user_panel/user_panel.js +++ b/src/components/user_panel/user_panel.js @@ -3,6 +3,7 @@ import PostStatusForm from '../post_status_form/post_status_form.vue' import UserCardContent from '../user_card_content/user_card_content.vue' const UserPanel = { + props: [ 'activatePanel' ], computed: { user () { return this.$store.state.users.currentUser } }, diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue index 2d5cb500..83eb099f 100644 --- a/src/components/user_panel/user_panel.vue +++ b/src/components/user_panel/user_panel.vue @@ -1,7 +1,7 @@ <template> <div class="user-panel"> <div v-if='user' class="panel panel-default" style="overflow: visible;"> - <user-card-content :user="user" :switcher="false" :hideBio="true"></user-card-content> + <user-card-content :activatePanel="activatePanel" :user="user" :switcher="false" :hideBio="true"></user-card-content> <div class="panel-footer"> <post-status-form v-if='user'></post-status-form> </div> diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 26be1801..1d79713d 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -27,6 +27,7 @@ const UserProfile = { }, watch: { userId () { + this.$store.dispatch('stopFetching', 'user') this.$store.commit('clearTimeline', { timeline: 'user' }) this.$store.dispatch('startFetching', ['user', this.userId]) } diff --git a/src/components/user_search/user_search.vue b/src/components/user_search/user_search.vue index 1624ebef..20ae84fc 100644 --- a/src/components/user_search/user_search.vue +++ b/src/components/user_search/user_search.vue @@ -1,5 +1,5 @@ <template> - <div class="user-seach panel panel-default"> + <div class="user-search panel panel-default"> <div class="panel-heading"> {{$t('nav.user_search')}} </div> diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js index a6203962..761e674a 100644 --- a/src/components/user_settings/user_settings.js +++ b/src/components/user_settings/user_settings.js @@ -235,6 +235,7 @@ const UserSettings = { if (res.status === 'success') { this.changedPassword = true this.changePasswordError = false + this.logout() } else { this.changedPassword = false this.changePasswordError = res.error @@ -243,6 +244,10 @@ const UserSettings = { }, activateTab (tabName) { this.activeTab = tabName + }, + logout () { + this.$store.dispatch('logout') + this.$router.replace('/') } } } diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue index a2a101dc..234a7d86 100644 --- a/src/components/user_settings/user_settings.vue +++ b/src/components/user_settings/user_settings.vue @@ -19,10 +19,10 @@ <div v-if="scopeOptionsEnabled"> <label for="default-vis">{{$t('settings.default_vis')}}</label> <div id="default-vis" class="visibility-tray"> - <i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct"></i> - <i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private"></i> - <i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted"></i> - <i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public"></i> + <i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct" :title="$t('post_status.scope.direct')" ></i> + <i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private" :title="$t('post_status.scope.private')"></i> + <i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted" :title="$t('post_status.scope.unlisted')"></i> + <i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public" :title="$t('post_status.scope.public')"></i> </div> </div> <p> diff --git a/src/i18n/en.json b/src/i18n/en.json index 56179868..890ed6c4 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -36,7 +36,8 @@ "public_tl": "Public Timeline", "timeline": "Timeline", "twkn": "The Whole Known Network", - "user_search": "User Search" + "user_search": "User Search", + "preferences": "Preferences" }, "notifications": { "broken_favorite": "Unknown status, searching for it...", @@ -160,6 +161,7 @@ "set_new_profile_background": "Set new profile background", "set_new_profile_banner": "Set new profile banner", "settings": "Settings", + "subject_input_always_show": "Always show subject field", "subject_line_behavior": "Copy subject when replying", "subject_line_email": "Like email: \"re: subject\"", "subject_line_mastodon": "Like mastodon: copy as is", @@ -318,5 +320,12 @@ "who_to_follow": { "more": "More", "who_to_follow": "Who to follow" + }, + "tool_tip": { + "media_upload": "Upload Media", + "repeat": "Repeat", + "reply": "Reply", + "favorite": "Favorite", + "user_settings": "User Settings" } } diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 58a6eae5..391fe797 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -134,6 +134,7 @@ "set_new_profile_background": "Загрузить новый фон профиля", "set_new_profile_banner": "Загрузить новый баннер профиля", "settings": "Настройки", + "subject_input_always_show": "Всегда показывать поле ввода темы", "stop_gifs": "Проигрывать GIF анимации только при наведении", "streaming": "Включить автоматическую загрузку новых сообщений при прокрутке вверх", "text": "Текст", diff --git a/src/main.js b/src/main.js index 9f094ada..378fe95c 100644 --- a/src/main.js +++ b/src/main.js @@ -47,7 +47,6 @@ const persistedStateOptions = { paths: [ 'config', 'users.lastLoginName', - 'statuses.notifications.maxSavedId', 'oauth' ] } diff --git a/src/modules/config.js b/src/modules/config.js index 04859fe3..fb9b3ca6 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -27,7 +27,8 @@ const defaultState = { highlight: {}, interfaceLanguage: browserLocale, scopeCopy: undefined, // instance default - subjectLineBehavior: undefined // instance default + subjectLineBehavior: undefined, // instance default + alwaysShowSubjectInput: undefined // instance default } const config = { diff --git a/src/modules/instance.js b/src/modules/instance.js index 8fd1a459..7c27d52a 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -17,6 +17,7 @@ const defaultState = { showInstanceSpecificPanel: false, scopeOptionsEnabled: true, formattingOptionsEnabled: false, + alwaysShowSubjectInput: true, collapseMessageWithSubject: false, hidePostStats: false, hideUserStats: false, diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 2c3d2550..8cdd4e28 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,5 +1,4 @@ import { includes, remove, slice, sortBy, toInteger, each, find, flatten, maxBy, minBy, merge, last, isArray } from 'lodash' -import { set } from 'vue' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -16,6 +15,7 @@ const emptyTl = () => ({ followers: [], friends: [], viewing: 'statuses', + userId: 0, flushMarker: 0 }) @@ -26,7 +26,6 @@ export const defaultState = { notifications: { desktopNotificationSilence: true, maxId: 0, - maxSavedId: 0, minId: Number.POSITIVE_INFINITY, data: [], error: false, @@ -132,7 +131,7 @@ const sortTimeline = (timeline) => { return timeline } -const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false }) => { +const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false, userId }) => { // Sanity check if (!isArray(statuses)) { return false @@ -149,6 +148,13 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us timelineObject.maxId = maxNew } + // This makes sure that user timeline won't get data meant for other + // user. I.e. opening different user profiles makes request which could + // return data late after user already viewing different user profile + if (timeline === 'user' && timelineObject.userId !== userId) { + return + } + const addStatus = (status, showImmediately, addToTimeline = true) => { const result = mergeOrAdd(allStatuses, allStatusesObject, status) status = result.item @@ -297,7 +303,7 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot state.notifications.maxId = Math.max(notification.id, state.notifications.maxId) state.notifications.minId = Math.min(notification.id, state.notifications.minId) - const fresh = !older && !notification.is_seen && notification.id > state.notifications.maxSavedId + const fresh = !notification.is_seen const status = notification.ntype === 'like' ? find(allStatuses, { id: action.in_reply_to_status_id }) : action @@ -306,7 +312,6 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot type: notification.ntype, status, action, - // Always assume older notifications as seen seen: !fresh } @@ -404,9 +409,8 @@ export const mutations = { addFollowers (state, { followers }) { state.timelines['user'].followers = followers }, - markNotificationsAsSeen (state, notifications) { - set(state.notifications, 'maxSavedId', state.notifications.maxId) - each(notifications, (notification) => { + markNotificationsAsSeen (state) { + each(state.notifications.data, (notification) => { notification.seen = true }) }, @@ -418,8 +422,8 @@ export const mutations = { const statuses = { state: defaultState, actions: { - addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false }) { - commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser }) + addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) { + commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId }) }, addNewNotifications ({ rootState, commit, dispatch }, { notifications, older }) { commit('addNewNotifications', { visibleNotificationTypes: visibleNotificationTypes(rootState), dispatch, notifications, older }) @@ -484,6 +488,13 @@ const statuses = { }, queueFlush ({ rootState, commit }, { timeline, id }) { commit('queueFlush', { timeline, id }) + }, + markNotificationsAsSeen ({ rootState, commit }) { + commit('markNotificationsAsSeen') + apiService.markNotificationsAsSeen({ + id: rootState.statuses.notifications.maxId, + credentials: rootState.users.currentUser.credentials + }) } }, mutations diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 714c0fc7..ae876b7f 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -29,6 +29,7 @@ const PROFILE_UPDATE_URL = '/api/account/update_profile.json' const EXTERNAL_PROFILE_URL = '/api/externalprofile/show.json' const QVITTER_USER_TIMELINE_URL = '/api/qvitter/statuses/user_timeline.json' const QVITTER_USER_NOTIFICATIONS_URL = '/api/qvitter/statuses/notifications.json' +const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' const BLOCKING_URL = '/api/blocks/create.json' const UNBLOCKING_URL = '/api/blocks/destroy.json' const USER_URL = '/api/users/show.json' @@ -460,6 +461,18 @@ const suggestions = ({credentials}) => { }).then((data) => data.json()) } +const markNotificationsAsSeen = ({id, credentials}) => { + const body = new FormData() + + body.append('latest_id', id) + + return fetch(QVITTER_USER_NOTIFICATIONS_READ_URL, { + body, + headers: authHeaders(credentials), + method: 'POST' + }).then((data) => data.json()) +} + const apiService = { verifyCredentials, fetchTimeline, @@ -494,7 +507,8 @@ const apiService = { fetchFollowRequests, approveUser, denyUser, - suggestions + suggestions, + markNotificationsAsSeen } export default apiService diff --git a/src/services/file_type/file_type.service.js b/src/services/file_type/file_type.service.js index f9d3b466..f543ec79 100644 --- a/src/services/file_type/file_type.service.js +++ b/src/services/file_type/file_type.service.js @@ -9,11 +9,11 @@ const fileType = (typeString) => { type = 'image' } - if (typeString.match(/video\/(webm|mp4)/)) { + if (typeString.match(/video/)) { type = 'video' } - if (typeString.match(/audio|ogg/)) { + if (typeString.match(/audio/)) { type = 'audio' } diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 0e3e32d2..c2a7de56 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -2,13 +2,14 @@ import { camelCase } from 'lodash' import apiService from '../api/api.service.js' -const update = ({store, statuses, timeline, showImmediately}) => { +const update = ({store, statuses, timeline, showImmediately, userId}) => { const ccTimeline = camelCase(timeline) store.dispatch('setError', { value: false }) store.dispatch('addNewStatuses', { timeline: ccTimeline, + userId, statuses, showImmediately }) @@ -33,7 +34,7 @@ const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false if (!older && statuses.length >= 20 && !timelineData.loading) { store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId }) } - update({store, statuses, timeline, showImmediately}) + update({store, statuses, timeline, showImmediately, userId}) }, () => store.dispatch('setError', { value: true })) } @@ -41,6 +42,7 @@ const startFetching = ({timeline = 'friends', credentials, store, userId = false const rootState = store.rootState || store.state const timelineData = rootState.statuses.timelines[camelCase(timeline)] const showImmediately = timelineData.visibleStatuses.length === 0 + timelineData.userId = userId fetchAndUpdate({timeline, credentials, store, showImmediately, userId, tag}) const boundFetchAndUpdate = () => fetchAndUpdate({ timeline, credentials, store, userId, tag }) return setInterval(boundFetchAndUpdate, 10000) diff --git a/static/config.json b/static/config.json index 67d84579..c49930ef 100644 --- a/static/config.json +++ b/static/config.json @@ -13,6 +13,7 @@ "collapseMessageWithSubject": false, "scopeCopy": false, "subjectLineBehavior": "email", + "alwaysShowSubjectInput": true, "hidePostStats": false, "hideUserStats": false, "loginMethod": "password" |
