From b87a9d6675cf7cd8d37d45e62013fab27cbc46e6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 8 Jun 2021 16:14:01 +0300 Subject: Rearranged settings, moved more stuff to filtering where apllicable. Changed how filering works. --- src/components/status/status.js | 44 +++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index 470c01f1..5c7caa28 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -157,26 +157,32 @@ const Status = { return muteWordHits(this.status, this.muteWords) }, muted () { + if (this.statusoid.user.id === this.currentUser.id) return false + const reasonsToMute = this.userIsMuted || + // Thread is muted + status.thread_muted || + // Wordfiltered + this.muteWordHits.length > 0 + return !this.unmuted && !this.shouldNotMute && reasonsToMute + }, + userIsMuted () { if (this.statusoid.user.id === this.currentUser.id) return false const { status } = this const { reblog } = status const relationship = this.$store.getters.relationship(status.user.id) const relationshipReblog = reblog && this.$store.getters.relationship(reblog.user.id) - const reasonsToMute = ( - // Post is muted according to BE - status.muted || + return status.muted || // Reprööt of a muted post according to BE (reblog && reblog.muted) || // Muted user relationship.muting || // Muted user of a reprööt - (relationshipReblog && relationshipReblog.muting) || - // Thread is muted - status.thread_muted || - // Wordfiltered - this.muteWordHits.length > 0 - ) - const excusesNotToMute = ( + (relationshipReblog && relationshipReblog.muting) + }, + shouldNotMute () { + const { status } = this + const { reblog } = status + return ( ( this.inProfile && ( // Don't mute user's posts on user timeline (except reblogs) @@ -189,14 +195,26 @@ const Status = { (this.inConversation && status.thread_muted) // No excuses if post has muted words ) && !this.muteWordHits.length > 0 - - return !this.unmuted && !excusesNotToMute && reasonsToMute + }, + hideMutedUsers () { + return this.mergedConfig.hideMutedPosts + }, + hideMutedThreads () { + return this.mergedConfig.hideMutedThreads }, hideFilteredStatuses () { return this.mergedConfig.hideFilteredStatuses }, + hideWordFilteredPosts () { + return this.mergedConfig.hideWordFilteredPosts + }, hideStatus () { - return (this.muted && this.hideFilteredStatuses) || this.virtualHidden + return (this.virtualHidden || !this.shouldNotMute) && ( + (this.muted && this.hideFilteredStatuses) || + (this.userIsMuted && this.hideMutedUsers) || + (this.status.thread_muted && this.hideMutedThreads) || + (this.muteWordHits.length > 0 && this.hideWordFilteredPosts) + ) }, isFocused () { // retweet or root of an expanded conversation -- cgit v1.2.3-70-g09d2 From fe0ed7e8f0941195547b924f99f6c0be707bf964 Mon Sep 17 00:00:00 2001 From: Alexander Tumin Date: Mon, 28 Feb 2022 23:07:20 +0300 Subject: Mute bot posts --- src/components/settings_modal/tabs/filtering_tab.vue | 5 +++++ src/components/status/status.js | 10 +++++++++- src/components/timeline/timeline_quick_settings.js | 7 +++++++ src/components/timeline/timeline_quick_settings.vue | 9 +++++++++ src/i18n/en.json | 1 + src/modules/config.js | 1 + src/modules/instance.js | 1 + 7 files changed, 33 insertions(+), 1 deletion(-) (limited to 'src/components/status/status.js') diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue index 50ee20e0..87567bef 100644 --- a/src/components/settings_modal/tabs/filtering_tab.vue +++ b/src/components/settings_modal/tabs/filtering_tab.vue @@ -37,6 +37,11 @@ +
  • + + {{ $t('settings.mute_bot_posts') }} + +
  • {{ $t('settings.hide_post_stats') }} diff --git a/src/components/status/status.js b/src/components/status/status.js index d8f94926..ba85114a 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -166,6 +166,9 @@ const Status = { muteWordHits () { return muteWordHits(this.status, this.muteWords) }, + botStatus () { + return this.status.user.bot + }, mentionsLine () { if (!this.headTailLinks) return [] const writtenSet = new Set(this.headTailLinks.writtenMentions.map(_ => _.url)) @@ -191,7 +194,9 @@ const Status = { // Thread is muted status.thread_muted || // Wordfiltered - this.muteWordHits.length > 0 + this.muteWordHits.length > 0 || + // bot status + (this.muteBotStatuses && this.botStatus) return !this.unmuted && !this.shouldNotMute && reasonsToMute }, userIsMuted () { @@ -293,6 +298,9 @@ const Status = { hidePostStats () { return this.mergedConfig.hidePostStats }, + muteBotStatuses () { + return this.mergedConfig.muteBotStatuses + }, currentUser () { return this.$store.state.users.currentUser }, diff --git a/src/components/timeline/timeline_quick_settings.js b/src/components/timeline/timeline_quick_settings.js index 7b4931ce..92d5ac14 100644 --- a/src/components/timeline/timeline_quick_settings.js +++ b/src/components/timeline/timeline_quick_settings.js @@ -53,6 +53,13 @@ const TimelineQuickSettings = { const value = !this.hideMutedPosts this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value }) } + }, + muteBotStatuses: { + get () { return this.mergedConfig.muteBotStatuses }, + set () { + const value = !this.muteBotStatuses + this.$store.dispatch('setOption', { name: 'muteBotStatuses', value }) + } } } } diff --git a/src/components/timeline/timeline_quick_settings.vue b/src/components/timeline/timeline_quick_settings.vue index 98996ebd..4d67e06b 100644 --- a/src/components/timeline/timeline_quick_settings.vue +++ b/src/components/timeline/timeline_quick_settings.vue @@ -39,6 +39,15 @@ class="dropdown-divider" /> +
  • +
  • + + {{ $t('settings.hide_bot_indication') }} + +
  • _.url)) @@ -301,6 +304,9 @@ const Status = { muteBotStatuses () { return this.mergedConfig.muteBotStatuses }, + hideBotIndication () { + return this.mergedConfig.hideBotIndication + }, currentUser () { return this.$store.state.users.currentUser }, diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 3bb29db6..8f51a778 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -77,6 +77,7 @@ @@ -124,6 +125,7 @@ @click.stop.prevent.capture.native="toggleUserExpanded" >
    - +
    diff --git a/src/components/still-image/still-image.vue b/src/components/still-image/still-image.vue index cca75fcb..4ea21506 100644 --- a/src/components/still-image/still-image.vue +++ b/src/components/still-image/still-image.vue @@ -19,6 +19,7 @@ @load="onLoad" @error="onError" > +
    diff --git a/src/components/user_avatar/user_avatar.js b/src/components/user_avatar/user_avatar.js index 94653004..33d9a258 100644 --- a/src/components/user_avatar/user_avatar.js +++ b/src/components/user_avatar/user_avatar.js @@ -1,10 +1,21 @@ import StillImage from '../still-image/still-image.vue' +import { library } from '@fortawesome/fontawesome-svg-core' + +import { + faRobot +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faRobot +) + const UserAvatar = { props: [ 'user', 'betterShadow', - 'compact' + 'compact', + 'bot' ], data () { return { diff --git a/src/components/user_avatar/user_avatar.vue b/src/components/user_avatar/user_avatar.vue index 4040e263..29e03bcb 100644 --- a/src/components/user_avatar/user_avatar.vue +++ b/src/components/user_avatar/user_avatar.vue @@ -7,7 +7,9 @@ :src="imgSrc(user.profile_image_url_original)" :class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }" :image-load-error="imageLoadError" - /> + > + +
    .bot-indicator { + position: absolute; + bottom: 0; + right: 0; + } + &.better-shadow { box-shadow: var(--_avatarShadowInset); filter: var(--_avatarShadowFilter); diff --git a/src/i18n/en.json b/src/i18n/en.json index a109ab3c..61087ec2 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -352,6 +352,7 @@ "hide_media_previews": "Hide media previews", "hide_muted_posts": "Hide posts of muted users", "mute_bot_posts": "Mute bot posts", + "hide_bot_indication": "Hide bot indication in posts", "hide_all_muted_posts": "Hide muted posts", "max_thumbnails": "Maximum amount of thumbnails per post (empty = no limit)", "hide_isp": "Hide instance-specific panel", diff --git a/src/modules/config.js b/src/modules/config.js index 79f76561..1aa2878e 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -80,6 +80,7 @@ export const defaultState = { mentionLinkShowYous: undefined, // instance default mentionLinkBoldenYou: undefined, // instance default hidePostStats: undefined, // instance default + hideBotIndication: undefined, // instance default hideUserStats: undefined, // instance default virtualScrolling: undefined, // instance default sensitiveByDefault: undefined // instance default diff --git a/src/modules/instance.js b/src/modules/instance.js index d0bf10e3..41bcf329 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -33,6 +33,7 @@ const defaultState = { hideMutedThreads: true, hideWordFilteredPosts: false, hidePostStats: false, + hideBotIndication: false, hideSitename: false, hideUserStats: false, muteBotStatuses: false, -- cgit v1.2.3-70-g09d2 From 0f2fd8a3523e9e2cd1ca6fe287eb7304895f2cba Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 7 Aug 2021 00:33:06 -0400 Subject: Implement thread folding/expanding --- src/components/conversation/conversation.js | 105 +++++++++++++++++++++++++-- src/components/conversation/conversation.vue | 7 ++ src/components/status/status.js | 22 +++++- src/components/status/status.vue | 13 ++++ src/components/thread_tree/thread_tree.js | 12 ++- src/components/thread_tree/thread_tree.vue | 35 ++++++++- 6 files changed, 180 insertions(+), 14 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index e663ba15..0abb7abd 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -38,7 +38,8 @@ const conversation = { data () { return { highlight: null, - expanded: false + expanded: false, + threadDisplayStatusObject: {} // id => 'showing' | 'hidden' } }, props: [ @@ -56,6 +57,9 @@ const conversation = { } }, computed: { + maxDepthToShowByDefault () { + return 4 + }, displayStyle () { return this.$store.state.config.conversationDisplay }, @@ -112,15 +116,14 @@ const conversation = { const id = cur.id a.forest[id] = this.getReplies(id) .map(s => s.id) - .sort((a, b) => reverseLookupTable[a] - reverseLookupTable[b]) - a.topLevel = a.topLevel.filter(k => a.forest[id].contains(k)) return a }, { forest: {}, - topLevel: this.conversation.map(s => s.id) }) + debug('threads = ', threads) + const walk = (forest, topLevel, depth = 0, processed = {}) => topLevel.map(id => { if (processed[id]) { return [] @@ -131,18 +134,63 @@ const conversation = { status: this.conversation[reverseLookupTable[id]], id, depth - }, walk(forest, forest[child], depth + 1, processed)].reduce((a, b) => a.concat(b), []) + }, walk(forest, forest[id], depth + 1, processed)].reduce((a, b) => a.concat(b), []) }).reduce((a, b) => a.concat(b), []) - const linearized = walk(threads.forest, threads.topLevel) + const linearized = walk(threads.forest, this.topLevel.map(k => k.id)) return linearized }, + replyIds () { + return this.conversation.map(k => k.id) + .reduce((res, id) => { + res[id] = (this.replies[id] || []).map(k => k.id) + return res + }, {}) + }, + totalReplyCount () { + debug('replyIds=', this.replyIds) + const sizes = {} + const subTreeSizeFor = (id) => { + if (sizes[id]) { + return sizes[id] + } + sizes[id] = 1 + this.replyIds[id].map(cid => subTreeSizeFor(cid)).reduce((a, b) => a + b, 0) + return sizes[id] + } + this.conversation.map(k => k.id).map(subTreeSizeFor) + debug('totalReplyCount=', sizes) + return Object.keys(sizes).reduce((res, id) => { + res[id] = sizes[id] - 1 // exclude itself + return res + }, {}) + }, + totalReplyDepth () { + const depths = {} + const subTreeDepthFor = (id) => { + if (depths[id]) { + return depths[id] + } + depths[id] = 1 + this.replyIds[id].map(cid => subTreeDepthFor(cid)).reduce((a, b) => a > b ? a : b, 0) + return depths[id] + } + this.conversation.map(k => k.id).map(subTreeDepthFor) + return Object.keys(depths).reduce((res, id) => { + res[id] = depths[id] - 1 // exclude itself + return res + }, {}) + }, + depths () { + debug('threadTree', this.threadTree) + return this.threadTree.reduce((a, k) => { + a[k.id] = k.depth + return a + }, {}) + }, topLevel () { const topLevel = this.conversation.reduce((tl, cur) => tl.filter(k => this.getReplies(cur.id).map(v => v.id).indexOf(k.id) === -1), this.conversation) debug("toplevel =", topLevel) - debug("toplevel =", topLevel) return topLevel }, replies () { @@ -169,6 +217,25 @@ const conversation = { hiddenStyle () { const height = (this.status && this.status.virtualHeight) || '120px' return this.virtualHidden ? { height } : {} + }, + threadDisplayStatus () { + return this.conversation.reduce((a, k) => { + const id = k.id + const depth = this.depths[id] + const status = (() => { + if (this.threadDisplayStatusObject[id]) { + return this.threadDisplayStatusObject[id] + } + if (depth <= this.maxDepthToShowByDefault) { + return 'showing' + } else { + return 'hidden' + } + })() + + a[id] = status + return a + }, {}) } }, components: { @@ -235,6 +302,30 @@ const conversation = { getConversationId (statusId) { const status = this.$store.state.statuses.allStatusesObject[statusId] return get(status, 'retweeted_status.statusnet_conversation_id', get(status, 'statusnet_conversation_id')) + }, + setThreadDisplay (id, nextStatus) { + this.threadDisplayStatusObject = { + ...this.threadDisplayStatusObject, + [id]: nextStatus + } + }, + toggleThreadDisplay (id) { + const depth = this.depths[id] + debug('depth = ', depth) + debug( + 'threadDisplayStatus = ', this.threadDisplayStatus, + 'threadDisplayStatusObject = ', this.threadDisplayStatusObject) + const curStatus = this.threadDisplayStatus[id] + const nextStatus = curStatus === 'showing' ? 'hidden' : 'showing' + debug('toggling', id, 'to', nextStatus) + this.setThreadDisplay(id, nextStatus) + }, + setThreadDisplayRecursively (id, nextStatus) { + this.setThreadDisplay(id, nextStatus) + this.getReplies(id).map(k => k.id).map(id => this.setThreadDisplayRecursively(id, nextStatus)) + }, + showThreadRecursively (id) { + this.setThreadDisplayRecursively(id, 'showing') } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index cea5f88f..783cc894 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -23,6 +23,7 @@ v-for="status in topLevel" :key="status.id" ref="statusComponent" + :depth="0" :status="status" :in-profile="inProfile" @@ -37,6 +38,12 @@ :get-highlight="getHighlight" :set-highlight="setHighlight" :toggle-expanded="toggleExpanded" + + :toggle-thread-display="toggleThreadDisplay" + :thread-display-status="threadDisplayStatus" + :show-thread-recursively="showThreadRecursively" + :total-reply-count="totalReplyCount" + :total-reply-depth="totalReplyDepth" />
    diff --git a/src/components/status/status.js b/src/components/status/status.js index d8f94926..9d423631 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -35,7 +35,9 @@ import { faStar, faEyeSlash, faEye, - faThumbtack + faThumbtack, + faAngleDoubleUp, + faAngleDoubleDown } from '@fortawesome/free-solid-svg-icons' library.add( @@ -52,7 +54,9 @@ library.add( faEllipsisH, faEyeSlash, faEye, - faThumbtack + faThumbtack, + faAngleDoubleUp, + faAngleDoubleDown ) const Status = { @@ -89,7 +93,10 @@ const Status = { 'inlineExpanded', 'showPinned', 'inProfile', - 'profileUserId' + 'profileUserId', + + 'controlledThreadDisplayStatus', + 'controlledToggleThreadDisplay' ], data () { return { @@ -304,6 +311,12 @@ const Status = { }, isSuspendable () { return !this.replying && this.mediaPlaying.length === 0 + }, + inThreadForest () { + return !!this.controlledThreadDisplayStatus + }, + threadShowing () { + return this.controlledThreadDisplayStatus === 'showing' } }, methods: { @@ -353,6 +366,9 @@ const Status = { }, setHeadTailLinks (headTailLinks) { this.headTailLinks = headTailLinks + }, + toggleThreadDisplay () { + this.controlledToggleThreadDisplay() } }, watch: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 3bb29db6..2ebf5638 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -219,6 +219,19 @@ class="fa-scale-110" /> +
    this.statusById(id)) }, + threadShowing () { + return this.threadDisplayStatus[this.status.id] === 'showing' + } }, methods: { statusById (id) { diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index 8256eee6..9f591585 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -13,18 +13,23 @@ :replies="getReplies(status.id)" :in-profile="inProfile" :profile-user-id="profileUserId" - class="conversation-status status-fadein panel-body" + class="conversation-status conversation-status-treeview status-fadein panel-body" + + :controlled-thread-display-status="threadDisplayStatus[status.id]" + :controlled-toggle-thread-display="() => toggleThreadDisplay(status.id)" + @goto="setHighlight" @toggleExpanded="toggleExpanded" />
    +
    + +
    -- cgit v1.2.3-70-g09d2 From ace1f5067c90be2fa0b8da22d39b0e2c88f590fb Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 7 Aug 2021 10:28:45 -0400 Subject: Make status display controlled --- src/components/conversation/conversation.js | 37 +++++++++++++++- src/components/conversation/conversation.vue | 3 ++ src/components/status/status.js | 9 +++- src/components/status/status.vue | 6 +++ src/components/status_content/status_content.js | 59 ++++++++++++++++++++++++- src/components/thread_tree/thread_tree.js | 11 ++++- src/components/thread_tree/thread_tree.vue | 10 +++++ 7 files changed, 130 insertions(+), 5 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 0abb7abd..6fc86b2c 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -2,7 +2,8 @@ import { reduce, filter, findIndex, clone, get } from 'lodash' import Status from '../status/status.vue' import ThreadTree from '../thread_tree/thread_tree.vue' -const debug = console.log +// const debug = console.log +const debug = () => {} const sortById = (a, b) => { const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id @@ -39,7 +40,8 @@ const conversation = { return { highlight: null, expanded: false, - threadDisplayStatusObject: {} // id => 'showing' | 'hidden' + threadDisplayStatusObject: {}, // id => 'showing' | 'hidden' + statusContentPropertiesObject: {} } }, props: [ @@ -236,6 +238,25 @@ const conversation = { a[id] = status return a }, {}) + }, + statusContentProperties () { + return this.conversation.reduce((a, k) => { + const id = k.id + const depth = this.depths[id] + const props = (() => { + if (this.statusContentPropertiesObject[id]) { + return this.statusContentPropertiesObject[id] + } + return { + showingTall: false, + expandingSubject: false, + showingLongSubject: false, + } + })() + + a[id] = props + return a + }, {}) } }, components: { @@ -326,6 +347,18 @@ const conversation = { }, showThreadRecursively (id) { this.setThreadDisplayRecursively(id, 'showing') + }, + setStatusContentProperty (id, name, value) { + this.statusContentPropertiesObject = { + ...this.statusContentPropertiesObject, + [id]: { + ...this.statusContentPropertiesObject[id], + [name]: value + } + } + }, + toggleStatusContentProperty (id, name) { + this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name]) } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 783cc894..08cb72d0 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -44,6 +44,9 @@ :show-thread-recursively="showThreadRecursively" :total-reply-count="totalReplyCount" :total-reply-depth="totalReplyDepth" + :status-content-properties="statusContentProperties" + :set-status-content-property="setStatusContentProperty" + :toggle-status-content-property="toggleStatusContentProperty" />
    diff --git a/src/components/status/status.js b/src/components/status/status.js index 9d423631..d5ee7d4e 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -96,7 +96,14 @@ const Status = { 'profileUserId', 'controlledThreadDisplayStatus', - 'controlledToggleThreadDisplay' + 'controlledToggleThreadDisplay', + + 'controlledShowingTall', + 'controlledToggleShowingTall', + 'controlledExpandingSubject', + 'controlledToggleExpandingSubject', + 'controlledShowingLongSubject', + 'controlledToggleShowingLongSubject' ], data () { return { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 2ebf5638..8fdebe44 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -319,6 +319,12 @@ :no-heading="noHeading" :highlight="highlight" :focused="isFocused" + :controlled-showing-tall="controlledShowingTall" + :controlled-expanding-subject="controlledExpandingSubject" + :controlled-showing-long-subject="controlledShowingLongSubject" + :controlled-toggle-showing-tall="controlledToggleShowingTall" + :controlled-toggle-expanding-subject="controlledToggleExpandingSubject" + :controlled-toggle-showing-long-subject="controlledToggleShowingLongSubject" @mediaplay="addMediaPlaying($event)" @mediapause="removeMediaPlaying($event)" @parseReady="setHeadTailLinks" diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index dec8914a..65ec85c4 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -23,6 +23,31 @@ library.add( faPollH ) +const camelCase = name => name.charAt(0).toUpperCase() + name.slice(1) + +const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const controlledName = `controlled${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + res[name] = function () { + return this[toggle] ? this[controlledName] : this[uncontrolledName] + } + return res +}, {}) + +const controlledOrUncontrolledToggle = (obj, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const controlledName = `controlled${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + if (obj[toggle]) { + obj[toggle]() + } else { + obj[uncontrolledName] = !obj[uncontrolledName] + } +} + const StatusContent = { name: 'StatusContent', props: [ @@ -31,9 +56,22 @@ const StatusContent = { 'focused', 'noHeading', 'fullContent', - 'singleLine' + 'singleLine', + 'controlledShowingTall', + 'controlledExpandingSubject', + 'controlledToggleShowingTall', + 'controlledToggleExpandingSubject' ], + data () { + return { + uncontrolledShowingTall: this.fullContent || (this.inConversation && this.focused), + uncontrolledShowingLongSubject: false, + // not as computed because it sets the initial state which will be changed later + uncontrolledExpandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject + } + }, computed: { + ...controlledOrUncontrolledGetters(['showingTall', 'expandingSubject', 'showingLongSubject']), hideAttachments () { return (this.mergedConfig.hideAttachments && !this.inConversation) || (this.mergedConfig.hideAttachmentsInConv && this.inConversation) @@ -71,6 +109,25 @@ const StatusContent = { Gallery, LinkPreview, StatusBody + }, + methods: { + toggleShowingTall () { + controlledOrUncontrolledToggle(this, 'showingTall') + }, + toggleExpandingSubject () { + controlledOrUncontrolledToggle(this, 'expandingSubject') + }, + toggleShowMore () { + if (this.mightHideBecauseTall) { + this.toggleShowingTall() + } else if (this.mightHideBecauseSubject) { + this.toggleExpandingSubject() + } + }, + setMedia () { + const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments + return () => this.$store.dispatch('setMedia', attachments) + } } } diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js index 88b60109..46245bdf 100644 --- a/src/components/thread_tree/thread_tree.js +++ b/src/components/thread_tree/thread_tree.js @@ -28,7 +28,10 @@ const ThreadTree = { threadDisplayStatus: Object, showThreadRecursively: Function, totalReplyCount: Object, - totalReplyDepth: Object + totalReplyDepth: Object, + statusContentProperties: Object, + setStatusContentProperty: Function, + toggleStatusContentProperty: Function }, computed: { reverseLookupTable () { @@ -44,6 +47,9 @@ const ThreadTree = { }, threadShowing () { return this.threadDisplayStatus[this.status.id] === 'showing' + }, + currentProp () { + return this.statusContentProperties[this.status.id] } }, methods: { @@ -55,6 +61,9 @@ const ThreadTree = { showThread () { }, showAllSubthreads () { + }, + toggleCurrentProp (name) { + this.toggleStatusContentProperty(this.status.id, name) } } } diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index bbdda6fb..d7077bfd 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -18,6 +18,13 @@ :controlled-thread-display-status="threadDisplayStatus[status.id]" :controlled-toggle-thread-display="() => toggleThreadDisplay(status.id)" + :controlled-showing-tall="currentProp.showingTall" + :controlled-expanding-subject="currentProp.expandingSubject" + :controlled-showing-long-subject="currentProp.showingLongSubject" + :controlled-toggle-showing-tall="() => toggleCurrentProp('ShowingTall')" + :controlled-toggle-expanding-subject="() => toggleCurrentProp('expandingSubject')" + :controlled-toggle-showing-long-subject="() => toggleCurrentProp('showingLongSubject')" + @goto="setHighlight" @toggleExpanded="toggleExpanded" /> @@ -50,6 +57,9 @@ :show-thread-recursively="showThreadRecursively" :total-reply-count="totalReplyCount" :total-reply-depth="totalReplyDepth" + :status-content-properties="statusContentProperties" + :set-status-content-property="setStatusContentProperty" + :toggle-status-content-property="toggleStatusContentProperty" />
    Date: Sat, 7 Aug 2021 11:59:10 -0400 Subject: Support diving into one status in a conversation --- src/components/conversation/conversation.js | 32 +++++++++++++++++++++++++++- src/components/conversation/conversation.vue | 30 +++++++++++++++++++++++++- src/components/status/status.js | 13 ++++++----- src/components/status/status.vue | 14 +++++++++++- src/components/thread_tree/thread_tree.js | 3 ++- src/components/thread_tree/thread_tree.vue | 15 +++++++++---- 6 files changed, 94 insertions(+), 13 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 6fc86b2c..584f310c 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -2,6 +2,17 @@ import { reduce, filter, findIndex, clone, get } from 'lodash' import Status from '../status/status.vue' import ThreadTree from '../thread_tree/thread_tree.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faAngleDoubleDown, + faAngleDoubleLeft +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faAngleDoubleDown, + faAngleDoubleLeft +) + // const debug = console.log const debug = () => {} @@ -41,7 +52,8 @@ const conversation = { highlight: null, expanded: false, threadDisplayStatusObject: {}, // id => 'showing' | 'hidden' - statusContentPropertiesObject: {} + statusContentPropertiesObject: {}, + diveRoot: null } }, props: [ @@ -195,6 +207,18 @@ const conversation = { debug("toplevel =", topLevel) return topLevel }, + showingTopLevel () { + if (this.diveRoot) { + return [this.conversation.filter(k => this.diveRoot === k.id)[0]] + } + return this.topLevel + }, + diveDepth () { + return this.diveRoot ? this.depths[this.diveRoot] : 0 + }, + diveMode () { + return !!this.diveRoot + }, replies () { let i = 1 // eslint-disable-next-line camelcase @@ -359,6 +383,12 @@ const conversation = { }, toggleStatusContentProperty (id, name) { this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name]) + }, + diveIntoStatus (id) { + this.diveRoot = id + }, + undive () { + this.diveRoot = null } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 08cb72d0..84e8d8b2 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -18,9 +18,22 @@ {{ $t('timeline.collapse') }}
    +
    + + + +
    @@ -65,6 +79,16 @@ :in-profile="inProfile" :profile-user-id="profileUserId" class="conversation-status status-fadein panel-body" + + :toggle-thread-display="toggleThreadDisplay" + :thread-display-status="threadDisplayStatus" + :show-thread-recursively="showThreadRecursively" + :total-reply-count="totalReplyCount" + :total-reply-depth="totalReplyDepth" + :status-content-properties="statusContentProperties" + :set-status-content-property="setStatusContentProperty" + :toggle-status-content-property="toggleStatusContentProperty" + @goto="setHighlight" @toggleExpanded="toggleExpanded" /> @@ -82,6 +106,10 @@ @import '../../_variables.scss'; .Conversation { + .conversation-undive-box { + padding: 1em; + } + .conversation-undive-box, .conversation-status { border-bottom-width: 1px; border-bottom-style: solid; diff --git a/src/components/status/status.js b/src/components/status/status.js index d5ee7d4e..f119f42e 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -36,8 +36,9 @@ import { faEyeSlash, faEye, faThumbtack, - faAngleDoubleUp, - faAngleDoubleDown + faChevronUp, + faChevronDown, + faAngleDoubleRight } from '@fortawesome/free-solid-svg-icons' library.add( @@ -55,8 +56,9 @@ library.add( faEyeSlash, faEye, faThumbtack, - faAngleDoubleUp, - faAngleDoubleDown + faChevronUp, + faChevronDown, + faAngleDoubleRight ) const Status = { @@ -103,7 +105,8 @@ const Status = { 'controlledExpandingSubject', 'controlledToggleExpandingSubject', 'controlledShowingLongSubject', - 'controlledToggleShowingLongSubject' + 'controlledToggleShowingLongSubject', + 'dive' ], data () { return { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 8fdebe44..c2df6021 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -229,7 +229,19 @@ + + diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js index 46245bdf..3e8eedb1 100644 --- a/src/components/thread_tree/thread_tree.js +++ b/src/components/thread_tree/thread_tree.js @@ -31,7 +31,8 @@ const ThreadTree = { totalReplyDepth: Object, statusContentProperties: Object, setStatusContentProperty: Function, - toggleStatusContentProperty: Function + toggleStatusContentProperty: Function, + dive: Function }, computed: { reverseLookupTable () { diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index d7077bfd..adf7bcdf 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -24,6 +24,7 @@ :controlled-toggle-showing-tall="() => toggleCurrentProp('ShowingTall')" :controlled-toggle-expanding-subject="() => toggleCurrentProp('expandingSubject')" :controlled-toggle-showing-long-subject="() => toggleCurrentProp('showingLongSubject')" + :dive="dive ? () => dive(status.id) : undefined" @goto="setHighlight" @toggleExpanded="toggleExpanded" @@ -60,18 +61,24 @@ :status-content-properties="statusContentProperties" :set-status-content-property="setStatusContentProperty" :toggle-status-content-property="toggleStatusContentProperty" + :dive="dive" />
    - + + + {{ $tc('status.thread_show_full', totalReplyCount[status.id], { numStatus: totalReplyCount[status.id], depth: totalReplyDepth[status.id] }) }} + +
    -- cgit v1.2.3-70-g09d2 From d9a9f97751b2f51f55848652e5126700aea0f3fe Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 7 Aug 2021 18:53:23 -0400 Subject: Add simple tree style navigation --- src/components/conversation/conversation.js | 70 ++++++++++++++++++++--- src/components/conversation/conversation.vue | 3 +- src/components/settings_modal/tabs/general_tab.js | 2 +- src/components/status/status.js | 16 ++++-- src/components/status/status.vue | 4 +- src/components/thread_tree/thread_tree.js | 14 ++++- src/components/thread_tree/thread_tree.vue | 22 ++++++- src/modules/instance.js | 2 +- 8 files changed, 113 insertions(+), 20 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index b2af1d6c..a1991888 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -80,7 +80,10 @@ const conversation = { return this.$store.state.config.conversationDisplay }, isTreeView () { - return this.displayStyle === 'tree' + return this.displayStyle === 'tree' || this.displayStyle === 'simple_tree' + }, + treeViewIsSimple () { + return this.displayStyle === 'simple_tree' }, isLinearView () { return this.displayStyle === 'linear' @@ -297,6 +300,14 @@ const conversation = { }, canDive () { return this.isTreeView && this.isExpanded + }, + focused () { + return (id) => { + return (this.isExpanded) && id === this.highlight + } + }, + maybeHighlight () { + return this.isExpanded ? this.highlight : null } }, components: { @@ -316,6 +327,9 @@ const conversation = { expanded (value) { if (value) { this.fetchConversation() + } else { + // if we collapse it, we should reset the dive + this._diven = false } }, virtualHidden (value) { @@ -323,6 +337,9 @@ const conversation = { 'setVirtualHeight', { statusId: this.statusId, height: `${this.$el.clientHeight}px` } ) + }, + highlight (value, old) { + console.log('highlight:', old, ' => ', value) } }, methods: { @@ -341,7 +358,8 @@ const conversation = { 'this.threadDisplayStatus ', this.threadDisplayStatus, 'this.statusId', this.statusId) if (this.threadDisplayStatus[this.statusId] === 'hidden') { - this.diveIntoStatus(parentOrSelf) + this.diveIntoStatus(parentOrSelf, /* preventScroll */ true) + this.tryScrollTo(this.statusId) } } }, @@ -365,18 +383,16 @@ const conversation = { getReplies (id) { return this.replies[id] || [] }, - focused (id) { - return (this.isExpanded) && id === this.statusId + getHighlight () { + return this.isExpanded ? this.highlight : null }, setHighlight (id) { + console.log('setHighlight', id) if (!id) return this.highlight = id this.$store.dispatch('fetchFavsAndRepeats', id) this.$store.dispatch('fetchEmojiReactionsBy', id) }, - getHighlight () { - return this.isExpanded ? this.highlight : null - }, toggleExpanded () { this.expanded = !this.expanded }, @@ -420,14 +436,52 @@ const conversation = { toggleStatusContentProperty (id, name) { this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name]) }, - diveIntoStatus (id) { + leastShowingAncestor (id) { + let cur = id + let parent = this.parentOf(cur) + while (cur) { + // if the parent is showing it means cur is visible + if (this.threadDisplayStatus[parent] === 'showing') { + return cur + } + parent = this.parentOf(parent) + cur = this.parentOf(cur) + } + // nothing found, fall back to toplevel + return topLevel[0].id + }, + diveIntoStatus (id, preventScroll) { this.diveHistory = [...this.diveHistory, id] + if (!preventScroll) { + this.goToCurrent() + } }, diveBack () { + const oldHighlight = this.highlight this.diveHistory = [...this.diveHistory.slice(0, this.diveHistory.length - 1)] + if (oldHighlight) { + this.tryScrollTo(this.leastShowingAncestor(oldHighlight)) + } }, undive () { + const oldHighlight = this.highlight this.diveHistory = [] + if (oldHighlight) { + this.tryScrollTo(this.leastShowingAncestor(oldHighlight)) + } else { + this.goToCurrent() + } + }, + tryScrollTo (id) { + if (this.isPage) { + // set statusId + this.$router.push({ name: 'conversation', params: { id } }) + } + + this.setHighlight(id) + }, + goToCurrent () { + this.tryScrollTo(this.diveRoot || this.topLevel[0].id) }, statusById (id) { return this.statusMap[id] diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 99bc7bcc..0ffd8c37 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -61,10 +61,11 @@ :focused="focused" :get-replies="getReplies" - :get-highlight="getHighlight" + :highlight="maybeHighlight" :set-highlight="setHighlight" :toggle-expanded="toggleExpanded" + :simple="treeViewIsSimple" :toggle-thread-display="toggleThreadDisplay" :thread-display-status="threadDisplayStatus" :show-thread-recursively="showThreadRecursively" diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index abe8f6f7..c43abe8a 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -20,7 +20,7 @@ const GeneralTab = { value: mode, label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`) })), - conversationDisplayOptions: ['tree', 'linear'].map(mode => ({ + conversationDisplayOptions: ['tree', 'simple_tree', 'linear'].map(mode => ({ key: mode, value: mode, label: this.$t(`settings.conversation_display_${mode}`) diff --git a/src/components/status/status.js b/src/components/status/status.js index f119f42e..15c2d029 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -97,6 +97,7 @@ const Status = { 'inProfile', 'profileUserId', + 'simpleTree', 'controlledThreadDisplayStatus', 'controlledToggleThreadDisplay', @@ -379,10 +380,9 @@ const Status = { }, toggleThreadDisplay () { this.controlledToggleThreadDisplay() - } - }, - watch: { - 'highlight': function (id) { + }, + scrollIfHighlighted (highlightId) { + const id = highlightId if (this.status.id === id) { let rect = this.$el.getBoundingClientRect() if (rect.top < 100) { @@ -396,6 +396,14 @@ const Status = { window.scrollBy(0, rect.bottom - window.innerHeight + 50) } } + } + }, + mounted () { + this.scrollIfHighlighted(this.highlight) + }, + watch: { + 'highlight': function (id) { + this.scrollIfHighlighted(id) }, 'status.repeat_num': function (num) { // refetch repeats when repeat_num is changed in any way diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 47d35de8..e4812711 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -220,7 +220,7 @@ /> + + {{ $t('status.replies_list') }} + Date: Thu, 16 Sep 2021 00:29:14 -0400 Subject: Make replying and mediaPlaying controlled $refs is not a reliable way to deal with child components under tree threading as it is not reactive, but the children may change at any time. The only good way seems to be making these states aggregated on the conversation component. Ref: tree-threading --- src/components/conversation/conversation.js | 21 +++++++++--- src/components/conversation/conversation.vue | 4 +++ src/components/status/status.js | 50 +++++++++++++++++++++++++--- src/components/thread_tree/thread_tree.js | 3 ++ src/components/thread_tree/thread_tree.vue | 4 +++ 5 files changed, 72 insertions(+), 10 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 7f9f24b5..9aa7b183 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -99,6 +99,10 @@ const conversation = { return this.otherRepliesButtonPosition === 'inside' }, suspendable () { + if (this.isTreeView) { + return Object.entries(this.statusContentProperties) + .every(([k, prop]) => !prop.replying && prop.mediaPlaying.length === 0) + } if (this.$refs.statusComponent && this.$refs.statusComponent[0]) { return this.$refs.statusComponent.every(s => s.suspendable) } else { @@ -303,14 +307,21 @@ const conversation = { return this.conversation.reduce((a, k) => { const id = k.id const props = (() => { - if (this.statusContentPropertiesObject[id]) { - return this.statusContentPropertiesObject[id] - } - return { + const def = { showingTall: false, expandingSubject: false, - showingLongSubject: false + showingLongSubject: false, + isReplying: false, + mediaPlaying: [] + } + + if (this.statusContentPropertiesObject[id]) { + return { + ...def, + ...this.statusContentPropertiesObject[id] + } } + return def })() a[id] = props diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 0cd74539..4d64cf08 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -78,9 +78,13 @@ :controlled-showing-tall="statusContentProperties[status.id].showingTall" :controlled-expanding-subject="statusContentProperties[status.id].expandingSubject" :controlled-showing-long-subject="statusContentProperties[status.id].showingLongSubject" + :controlled-replying="statusContentProperties[status.id].replying" + :controlled-media-playing="statusContentProperties[status.id].mediaPlaying" :controlled-toggle-showing-tall="() => toggleStatusContentProperty(status.id, 'showingTall')" :controlled-toggle-expanding-subject="() => toggleStatusContentProperty(status.id, 'expandingSubject')" :controlled-toggle-showing-long-subject="() => toggleStatusContentProperty(status.id, 'showingLongSubject')" + :controlled-toggle-replying="() => toggleStatusContentProperty(status.id, 'replying')" + :controlled-set-media-playing="(newVal) => toggleStatusContentProperty(status.id, 'mediaPlaying', newVal)" @goto="setHighlight" @toggleExpanded="toggleExpanded" diff --git a/src/components/status/status.js b/src/components/status/status.js index 700b9764..73fad45f 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -61,6 +61,41 @@ library.add( faAngleDoubleRight ) +const camelCase = name => name.charAt(0).toUpperCase() + name.slice(1) + +const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const controlledName = `controlled${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + res[name] = function () { + return this[toggle] ? this[controlledName] : this[uncontrolledName] + } + return res +}, {}) + +const controlledOrUncontrolledToggle = (obj, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + if (obj[toggle]) { + obj[toggle]() + } else { + obj[uncontrolledName] = !obj[uncontrolledName] + } +} + +const controlledOrUncontrolledSet = (obj, name, val) => { + const camelized = camelCase(name) + const set = `controlledSet${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + if (obj[set]) { + obj[set](val) + } else { + obj[uncontrolledName] = val + } +} + const Status = { name: 'Status', components: { @@ -108,20 +143,25 @@ const Status = { 'controlledToggleExpandingSubject', 'controlledShowingLongSubject', 'controlledToggleShowingLongSubject', + 'controlledReplying', + 'controlledToggleReplying', + 'controlledMediaPlaying', + 'controlledSetMediaPlaying', 'dive' ], data () { return { - replying: false, + uncontrolledReplying: false, unmuted: false, userExpanded: false, - mediaPlaying: [], + uncontrolledMediaPlaying: [], suspendable: true, error: null, headTailLinks: null } }, computed: { + ...controlledOrUncontrolledGetters(['replying', 'mediaPlaying']), muteWords () { return this.mergedConfig.muteWords }, @@ -351,7 +391,7 @@ const Status = { this.error = undefined }, toggleReplying () { - this.replying = !this.replying + controlledOrUncontrolledToggle(this, 'replying') }, gotoOriginal (id) { if (this.inConversation) { @@ -371,10 +411,10 @@ const Status = { return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames) }, addMediaPlaying (id) { - this.mediaPlaying.push(id) + controlledOrUncontrolledSet(this, 'mediaPlaying', this.mediaPlaying.concat(id)) }, removeMediaPlaying (id) { - this.mediaPlaying = this.mediaPlaying.filter(mediaId => mediaId !== id) + controlledOrUncontrolledSet(this, 'mediaPlaying', this.mediaPlaying.filter(mediaId => mediaId !== id)) }, setHeadTailLinks (headTailLinks) { this.headTailLinks = headTailLinks diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js index 0e499b85..71e63725 100644 --- a/src/components/thread_tree/thread_tree.js +++ b/src/components/thread_tree/thread_tree.js @@ -80,6 +80,9 @@ const ThreadTree = { }, toggleCurrentProp (name) { this.toggleStatusContentProperty(this.status.id, name) + }, + setCurrentProp (name, newVal) { + this.setStatusContentProperty(this.status.id, name) } } } diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index dce03f27..cee223e8 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -22,9 +22,13 @@ :controlled-showing-tall="currentProp.showingTall" :controlled-expanding-subject="currentProp.expandingSubject" :controlled-showing-long-subject="currentProp.showingLongSubject" + :controlled-replying="currentProp.replying" + :controlled-media-playing="currentProp.mediaPlaying" :controlled-toggle-showing-tall="() => toggleCurrentProp('showingTall')" :controlled-toggle-expanding-subject="() => toggleCurrentProp('expandingSubject')" :controlled-toggle-showing-long-subject="() => toggleCurrentProp('showingLongSubject')" + :controlled-toggle-replying="() => toggleCurrentProp('replying')" + :controlled-set-media-playing="(newVal) => setCurrentProp('mediaPlaying', newVal)" :dive="dive ? () => dive(status.id) : undefined" @goto="setHighlight" -- cgit v1.2.3-70-g09d2 From f8c5cbcd0d5d092c1264032a1be003f828dfc499 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 16 Sep 2021 09:22:49 -0400 Subject: Fix timeline jump when scrolling Ref: tree-threading --- src/components/conversation/conversation.js | 19 ++++++++++++++++++- src/components/status/status.js | 3 --- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 9aa7b183..b9ebe7eb 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -469,7 +469,24 @@ const conversation = { } else { this.inlineDivePosition = id } - this.setHighlight(id) + // Because the conversation can be unmounted when out of sight + // and mounted again when it comes into sight, + // the `mounted` or `created` function in `status` should not + // contain scrolling calls, as we do not want the page to jump + // when we scroll with an expanded conversation. + // + // Now the method is to rely solely on the `highlight` watcher + // in `status` components. + // In linear views, all statuses are rendered at all times, but + // in tree views, it is possible that a change in active status + // removes and adds status components (e.g. an originally child + // status becomes an ancestor status, and thus they will be + // different). + // Here, let the components be rendered first, in order to trigger + // the `highlight` watcher. + this.$nextTick(() => { + this.setHighlight(id) + }) }, goToCurrent () { this.tryScrollTo(this.diveRoot || this.topLevel[0].id) diff --git a/src/components/status/status.js b/src/components/status/status.js index 73fad45f..7bdcb665 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -439,9 +439,6 @@ const Status = { } } }, - mounted () { - this.scrollIfHighlighted(this.highlight) - }, watch: { 'highlight': function (id) { this.scrollIfHighlighted(id) -- cgit v1.2.3-70-g09d2 From 450145dd6b0c91e80b957d7c39dbf6b809b056be Mon Sep 17 00:00:00 2001 From: Alexander Tumin Date: Wed, 9 Mar 2022 07:56:43 +0300 Subject: Do not mute bot posts in notifications --- src/components/status/status.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index 282e85fb..2c3e079f 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -199,7 +199,7 @@ const Status = { // Wordfiltered this.muteWordHits.length > 0 || // bot status - (this.muteBotStatuses && this.botStatus) + (this.muteBotStatuses && this.botStatus && !this.compact) return !this.unmuted && !this.shouldNotMute && reasonsToMute }, userIsMuted () { -- cgit v1.2.3-70-g09d2 From 4e2fd7baf9a2c1d1e02ada6100b8c9aa91ffb81a Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 21 Mar 2022 20:39:56 +0200 Subject: fix bot indicator appearing on retweeter avatar --- src/components/status/status.js | 6 ++++++ src/components/status/status.vue | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index 4c0ef3e0..e54be241 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -225,12 +225,18 @@ const Status = { muteWordHits () { return muteWordHits(this.status, this.muteWords) }, + rtBotStatus () { + return this.statusoid.user.bot + }, botStatus () { return this.status.user.bot }, botIndicator () { return this.botStatus && !this.hideBotIndication }, + rtBotIndicator () { + return this.rtBotStatus && !this.hideBotIndication + }, mentionsLine () { if (!this.headTailLinks) return [] const writtenSet = new Set(this.headTailLinks.writtenMentions.map(_ => _.url)) diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 1679834e..3de4c471 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -77,7 +77,7 @@ -- cgit v1.2.3-70-g09d2 From d524e983487edd45c2a6558878c21e78b9d1ff38 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 22 Mar 2022 20:42:29 +0200 Subject: fix capitalization (and localization of tooltips for scope icon) --- src/components/status/status.js | 8 +++----- src/components/status/status.vue | 2 +- src/i18n/en.json | 8 +++++++- 3 files changed, 11 insertions(+), 7 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index e54be241..088ddcc5 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -389,6 +389,9 @@ const Status = { }, threadShowing () { return this.controlledThreadDisplayStatus === 'showing' + }, + visibilityLocalized () { + return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility) } }, methods: { @@ -478,11 +481,6 @@ const Status = { 'isSuspendable': function (val) { this.suspendable = val } - }, - filters: { - capitalize: function (str) { - return str.charAt(0).toUpperCase() + str.slice(1) - } } } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 3f352f03..ceb5a95a 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -192,7 +192,7 @@ Date: Mon, 28 Mar 2022 17:37:26 +0300 Subject: fix warnings --- src/components/status/status.js | 4 ++-- src/components/status_content/status_content.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index 088ddcc5..ee0da4dc 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -69,7 +69,7 @@ const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { const controlledName = `controlled${camelized}` const uncontrolledName = `uncontrolled${camelized}` res[name] = function () { - return this[toggle] ? this[controlledName] : this[uncontrolledName] + return (Object.getOwnPropertyDescriptor(this, toggle) && this[toggle]) ? this[controlledName] : this[uncontrolledName] } return res }, {}) @@ -311,7 +311,7 @@ const Status = { return this.mergedConfig.hideWordFilteredPosts }, hideStatus () { - return (this.virtualHidden || !this.shouldNotMute) && ( + return (!this.shouldNotMute) && ( (this.muted && this.hideFilteredStatuses) || (this.userIsMuted && this.hideMutedUsers) || (this.status.thread_muted && this.hideMutedThreads) || diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index cf72ccb8..0200382b 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -31,7 +31,7 @@ const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { const controlledName = `controlled${camelized}` const uncontrolledName = `uncontrolled${camelized}` res[name] = function () { - return this[toggle] ? this[controlledName] : this[uncontrolledName] + return (Object.getOwnPropertyDescriptor(this, toggle) && this[toggle]) ? this[controlledName] : this[uncontrolledName] } return res }, {}) -- cgit v1.2.3-70-g09d2 From 3799983d4f89cad4dbf4595f49a768a66b72b07e Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 29 Mar 2022 12:41:34 +0300 Subject: fix production build's reply not working in tree mode --- src/components/status/status.js | 2 +- src/components/status_content/status_content.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index ee0da4dc..c6b24c72 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -69,7 +69,7 @@ const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { const controlledName = `controlled${camelized}` const uncontrolledName = `uncontrolled${camelized}` res[name] = function () { - return (Object.getOwnPropertyDescriptor(this, toggle) && this[toggle]) ? this[controlledName] : this[uncontrolledName] + return (this.$props[toggle] && this[toggle]) ? this[controlledName] : this[uncontrolledName] } return res }, {}) diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index 0200382b..f47d036a 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -31,7 +31,7 @@ const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { const controlledName = `controlled${camelized}` const uncontrolledName = `uncontrolled${camelized}` res[name] = function () { - return (Object.getOwnPropertyDescriptor(this, toggle) && this[toggle]) ? this[controlledName] : this[uncontrolledName] + return (this.$props[toggle] && this[toggle]) ? this[controlledName] : this[uncontrolledName] } return res }, {}) -- cgit v1.2.3-70-g09d2 From e1483488c7982daa9272c570f0a5a9296519ef31 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 29 Mar 2022 19:23:30 +0300 Subject: fix some issues with trees --- src/components/status/status.js | 2 +- src/components/status_content/status_content.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index c6b24c72..ec0bd25a 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -69,7 +69,7 @@ const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { const controlledName = `controlled${camelized}` const uncontrolledName = `uncontrolled${camelized}` res[name] = function () { - return (this.$props[toggle] && this[toggle]) ? this[controlledName] : this[uncontrolledName] + return (this.$props[toggle] !== undefined && this[toggle]) ? this[controlledName] : this[uncontrolledName] } return res }, {}) diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index f47d036a..68e75d9e 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -31,7 +31,7 @@ const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { const controlledName = `controlled${camelized}` const uncontrolledName = `uncontrolled${camelized}` res[name] = function () { - return (this.$props[toggle] && this[toggle]) ? this[controlledName] : this[uncontrolledName] + return (this.$props[toggle] !== undefined && this[toggle]) ? this[controlledName] : this[uncontrolledName] } return res }, {}) -- cgit v1.2.3-70-g09d2 From de2f9686452b2bcb6cfe08fe9a5b648ec6bd5ed2 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 29 Mar 2022 19:44:07 +0300 Subject: fix? --- src/components/status/status.js | 2 +- src/components/status_content/status_content.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index ec0bd25a..a925f30b 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -69,7 +69,7 @@ const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { const controlledName = `controlled${camelized}` const uncontrolledName = `uncontrolled${camelized}` res[name] = function () { - return (this.$props[toggle] !== undefined && this[toggle]) ? this[controlledName] : this[uncontrolledName] + return ((this.$data[toggle] !== undefined || this.$props[toggle] !== undefined) && this[toggle]) ? this[controlledName] : this[uncontrolledName] } return res }, {}) diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index 68e75d9e..e69c3afd 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -31,7 +31,7 @@ const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { const controlledName = `controlled${camelized}` const uncontrolledName = `uncontrolled${camelized}` res[name] = function () { - return (this.$props[toggle] !== undefined && this[toggle]) ? this[controlledName] : this[uncontrolledName] + return ((this.$data[toggle] !== undefined || this.$props[toggle] !== undefined) && this[toggle]) ? this[controlledName] : this[uncontrolledName] } return res }, {}) -- cgit v1.2.3-70-g09d2 From 7c633aa9525c8a0533281947c587ff1a5ded75ec Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 13 Jun 2022 13:45:04 +0300 Subject: user popovers WIP --- src/components/popover/popover.js | 91 ++++++++++++++++++++++---------------- src/components/popover/popover.vue | 2 +- src/components/status/status.js | 4 +- src/components/status/status.vue | 38 +++++++++++----- 4 files changed, 84 insertions(+), 51 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index dbd446a5..233572a4 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -34,7 +34,13 @@ const Popover = { removePadding: Boolean, // self-explanatory (i hope) - disabled: Boolean + disabled: Boolean, + + // Instead of putting popover next to anchor, overlay popover's center on top of anchor's center + overlayCenters: Boolean, + + // What selector (witin popover!) to use for determining center of popover + overlayCentersSelector: String }, data () { return { @@ -73,7 +79,9 @@ const Popover = { x: anchorScreenBox.left + anchorWidth * 0.5, y: anchorScreenBox.top + anchorHeight * 0.5 } - const content = this.$refs.content + const content = this.overlayCentersSelector + ? this.$refs.content.querySelector(this.overlayCentersSelector) + : this.$refs.content // Minor optimization, don't call a slow reflow call if we don't have to const parentScreenBox = this.boundTo && @@ -100,44 +108,51 @@ const Popover = { max: window.innerHeight - (margin.bottom || 5) } - let horizOffset = content.offsetWidth * -0.5 - const leftBorder = origin.x + horizOffset - const rightBorder = origin.x - horizOffset - // If overflowing from left, move it so that it doesn't - if (leftBorder < xBounds.min) { - horizOffset += xBounds.min - leftBorder - } + if (!this.overlayCenters) { + let horizOffset = content.offsetWidth * -0.5 + const leftBorder = origin.x + horizOffset + const rightBorder = origin.x - horizOffset + // If overflowing from left, move it so that it doesn't + if (leftBorder < xBounds.min) { + horizOffset += xBounds.min - leftBorder + } - // If overflowing from right, move it so that it doesn't - if (rightBorder > xBounds.max) { - horizOffset -= rightBorder - xBounds.max - } + // If overflowing from right, move it so that it doesn't + if (rightBorder > xBounds.max) { + horizOffset -= rightBorder - xBounds.max + } + + // Default to whatever user wished with placement prop + let usingTop = this.placement !== 'bottom' - // Default to whatever user wished with placement prop - let usingTop = this.placement !== 'bottom' - - // Handle special cases, first force to displaying on top if there's not space on bottom, - // regardless of what placement value was. Then check if there's not space on top, and - // force to bottom, again regardless of what placement value was. - const topBoundary = origin.y - anchorHeight * 0.5 + (this.removePadding ? topPadding : 0) - const bottomBoundary = origin.y + anchorHeight * 0.5 - (this.removePadding ? bottomPadding : 0) - if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true - if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false - - const yOffset = (this.offset && this.offset.y) || 0 - const translateY = usingTop - ? topBoundary - yOffset - content.offsetHeight - : bottomBoundary + yOffset - - const xOffset = (this.offset && this.offset.x) || 0 - const translateX = origin.x + horizOffset + xOffset - - // Note, separate translateX and translateY avoids blurry text on chromium, - // single translate or translate3d resulted in blurry text. - this.styles = { - left: `${Math.round(translateX)}px`, - top: `${Math.round(translateY)}px`, - position: 'fixed' + // Handle special cases, first force to displaying on top if there's not space on bottom, + // regardless of what placement value was. Then check if there's not space on top, and + // force to bottom, again regardless of what placement value was. + const topBoundary = origin.y - anchorHeight * 0.5 + (this.removePadding ? topPadding : 0) + const bottomBoundary = origin.y + anchorHeight * 0.5 - (this.removePadding ? bottomPadding : 0) + if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true + if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false + + const yOffset = (this.offset && this.offset.y) || 0 + const translateY = usingTop + ? topBoundary - yOffset - content.offsetHeight + : bottomBoundary + yOffset + + const xOffset = (this.offset && this.offset.x) || 0 + const translateX = origin.x + horizOffset + xOffset + + this.styles = { + left: `${Math.round(translateX)}px`, + top: `${Math.round(translateY)}px` + } + } else { + const translateY = origin.y - content.offsetHeight + const translateX = origin.x - content.offsetWidth + + this.styles = { + left: `${Math.round(translateX)}px`, + top: `${Math.round(translateY)}px` + } } if (parentScreenBox) { diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue index 25ce32fb..977c3559 100644 --- a/src/components/popover/popover.vue +++ b/src/components/popover/popover.vue @@ -44,7 +44,7 @@ .popover { z-index: 90000; - position: absolute; + position: fixed; min-width: 0; box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5); box-shadow: var(--popupShadow); diff --git a/src/components/status/status.js b/src/components/status/status.js index a925f30b..06496258 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -11,6 +11,7 @@ import Timeago from '../timeago/timeago.vue' import StatusContent from '../status_content/status_content.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' import StatusPopover from '../status_popover/status_popover.vue' +import Popover from '../popover/popover.vue' import UserListPopover from '../user_list_popover/user_list_popover.vue' import EmojiReactions from '../emoji_reactions/emoji_reactions.vue' import MentionsLine from 'src/components/mentions_line/mentions_line.vue' @@ -115,7 +116,8 @@ const Status = { StatusContent, RichContent, MentionLink, - MentionsLine + MentionsLine, + Popover }, props: [ 'statusoid', diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 67ce999a..624960fe 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -122,17 +122,33 @@ v-if="!noHeading" class="left-side" > - - + + + + +
    -- cgit v1.2.3-70-g09d2 From d84cda7009d486a047953b1ca2d27acf35b8ddc1 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 16 Jun 2022 16:30:05 +0300 Subject: unify user popovers into a separate component --- src/components/mention_link/mention_link.js | 3 +- src/components/mention_link/mention_link.vue | 115 ++++++++++++--------------- src/components/notification/notification.js | 4 +- src/components/notification/notification.vue | 36 +++------ src/components/popover/popover.js | 2 +- src/components/status/status.js | 6 +- src/components/status/status.vue | 43 +++------- src/components/user_popover/user_popover.js | 14 ++++ src/components/user_popover/user_popover.vue | 34 ++++++++ 9 files changed, 127 insertions(+), 130 deletions(-) create mode 100644 src/components/user_popover/user_popover.js create mode 100644 src/components/user_popover/user_popover.vue (limited to 'src/components/status/status.js') diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js index 4b088f88..7e47625a 100644 --- a/src/components/mention_link/mention_link.js +++ b/src/components/mention_link/mention_link.js @@ -16,8 +16,7 @@ const MentionLink = { name: 'MentionLink', components: { UserAvatar, - Popover: defineAsyncComponent(() => import('../popover/popover.vue')), - UserCard: defineAsyncComponent(() => import('../user_card/user_card.vue')) + UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) }, props: { url: { diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue index 686ad27f..0cff50ab 100644 --- a/src/components/mention_link/mention_link.vue +++ b/src/components/mention_link/mention_link.vue @@ -10,84 +10,71 @@ target="_blank" v-html="content" /> - - - - + + + diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 9408b217..77cdfa73 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -5,7 +5,7 @@ import UserAvatar from '../user_avatar/user_avatar.vue' import UserCard from '../user_card/user_card.vue' import Timeago from '../timeago/timeago.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' -import Popover from '../popover/popover.vue' +import UserPopover from '../user_popover/user_popover.vue' import { isStatusNotification } from '../../services/notification_utils/notification_utils.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -48,7 +48,7 @@ const Notification = { Timeago, Status, RichContent, - Popover + UserPopover }, methods: { toggleUserExpanded () { diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index a44481c9..6427d117 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -36,32 +36,18 @@ :href="$router.resolve(userProfileLink).href" @click.prevent > - - - - + +
    diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index 7f4c1cac..f6527624 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -80,7 +80,7 @@ const Popover = { y: anchorScreenBox.top + anchorHeight * 0.5 } const content = this.$refs.content - const overlayCenter = this.overlayCentersSelector + const overlayCenter = this.overlayCenters ? this.$refs.content.querySelector(this.overlayCentersSelector) : null diff --git a/src/components/status/status.js b/src/components/status/status.js index 06496258..053c9441 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -4,14 +4,13 @@ import ReactButton from '../react_button/react_button.vue' import RetweetButton from '../retweet_button/retweet_button.vue' import ExtraButtons from '../extra_buttons/extra_buttons.vue' import PostStatusForm from '../post_status_form/post_status_form.vue' -import UserCard from '../user_card/user_card.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import AvatarList from '../avatar_list/avatar_list.vue' import Timeago from '../timeago/timeago.vue' import StatusContent from '../status_content/status_content.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' import StatusPopover from '../status_popover/status_popover.vue' -import Popover from '../popover/popover.vue' +import UserPopover from '../user_popover/user_popover.vue' import UserListPopover from '../user_list_popover/user_list_popover.vue' import EmojiReactions from '../emoji_reactions/emoji_reactions.vue' import MentionsLine from 'src/components/mentions_line/mentions_line.vue' @@ -106,7 +105,6 @@ const Status = { RetweetButton, ExtraButtons, PostStatusForm, - UserCard, UserAvatar, AvatarList, Timeago, @@ -117,7 +115,7 @@ const Status = { RichContent, MentionLink, MentionsLine, - Popover + UserPopover }, props: [ 'statusoid', diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 624960fe..771d336a 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -123,42 +123,21 @@ class="left-side" > - - - - + +
    -
    import('../user_card/user_card.vue')), + Popover: defineAsyncComponent(() => import('../popover/popover.vue')) + } +} + +export default UserPopover diff --git a/src/components/user_popover/user_popover.vue b/src/components/user_popover/user_popover.vue new file mode 100644 index 00000000..7ad5c234 --- /dev/null +++ b/src/components/user_popover/user_popover.vue @@ -0,0 +1,34 @@ + + + + + -- cgit v1.2.3-70-g09d2 From ed8bc6102290cb50d44c1c7aa5696aac5624d907 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 22 Jun 2022 16:05:27 -0400 Subject: Add last edited at indicator in status --- src/components/status/status.js | 6 ++++++ src/components/status/status.scss | 3 ++- src/components/status/status.vue | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index a925f30b..b7f20374 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -392,6 +392,12 @@ const Status = { }, visibilityLocalized () { return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility) + }, + isEdited () { + return this.status.edited_at !== null + }, + editingAvailable () { + return this.$store.state.instance.editingAvailable } }, methods: { diff --git a/src/components/status/status.scss b/src/components/status/status.scss index b3ad3818..ada9841e 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -156,7 +156,8 @@ margin-right: 0.2em; } - & .heading-reply-row { + & .heading-reply-row, + & .heading-edited-row { position: relative; align-content: baseline; font-size: 0.85em; diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 67ce999a..a9630a53 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -328,6 +328,30 @@ class="mentions-line" />
    +
    + + + +
    Date: Sun, 31 Jul 2022 12:35:48 +0300 Subject: --fix --- src/App.vue | 20 ++++-- src/boot/after_store.js | 4 +- src/boot/routes.js | 9 ++- src/components/about/about.vue | 2 +- src/components/account_actions/account_actions.vue | 4 +- src/components/avatar_list/avatar_list.vue | 2 +- src/components/basic_user_card/basic_user_card.vue | 11 ++-- src/components/chat/chat.js | 2 +- src/components/chat_list/chat_list.vue | 2 +- src/components/chat_message/chat_message.vue | 10 +-- src/components/chat_title/chat_title.vue | 4 +- src/components/checkbox/checkbox.vue | 4 +- src/components/color_input/color_input.vue | 2 +- src/components/conversation/conversation.js | 2 +- src/components/conversation/conversation.vue | 2 +- src/components/desktop_nav/desktop_nav.js | 24 +++++--- .../domain_mute_card/domain_mute_card.vue | 4 +- src/components/emoji_input/emoji_input.js | 2 +- src/components/emoji_picker/emoji_picker.js | 2 +- src/components/emoji_reactions/emoji_reactions.vue | 2 +- src/components/extra_buttons/extra_buttons.js | 2 +- src/components/extra_buttons/extra_buttons.vue | 6 +- src/components/favorite_button/favorite_button.vue | 2 +- src/components/features_panel/features_panel.vue | 2 +- src/components/flash/flash.js | 2 +- src/components/font_control/font_control.vue | 2 +- src/components/hashtag_link/hashtag_link.vue | 4 +- src/components/image_cropper/image_cropper.js | 2 +- .../instance_specific_panel.vue | 2 +- src/components/interactions/interactions.js | 2 +- .../interface_language_switcher.vue | 1 + src/components/login_form/login_form.js | 2 +- src/components/login_form/login_form.vue | 2 +- src/components/media_upload/media_upload.js | 5 +- src/components/media_upload/media_upload.vue | 2 +- src/components/mention_link/mention_link.vue | 17 ++--- src/components/mentions_line/mentions_line.vue | 14 ++--- src/components/mfa_form/recovery_form.vue | 2 +- src/components/mobile_nav/mobile_nav.vue | 5 +- src/components/modal/modal.vue | 8 +-- .../moderation_tools/moderation_tools.vue | 8 +-- .../mrf_transparency_panel.js | 6 +- src/components/nav_panel/nav_panel.vue | 2 +- src/components/notification/notification.js | 2 +- src/components/notification/notification.vue | 4 +- .../notifications/notification_filters.vue | 4 +- src/components/notifications/notifications.vue | 5 +- src/components/popover/popover.js | 34 +++++----- .../post_status_form/post_status_form.js | 16 ++--- src/components/react_button/react_button.js | 2 +- src/components/react_button/react_button.vue | 8 +-- src/components/remote_follow/remote_follow.js | 2 +- src/components/retweet_button/retweet_button.vue | 2 +- src/components/search_bar/search_bar.js | 2 +- src/components/selectable_list/selectable_list.vue | 4 +- .../settings_modal/helpers/modified_indicator.vue | 4 +- .../helpers/server_side_indicator.vue | 4 +- src/components/settings_modal/settings_modal.vue | 4 +- src/components/settings_modal/tabs/general_tab.vue | 10 ++- .../settings_modal/tabs/mutes_and_blocks_tab.vue | 34 +++++----- src/components/settings_modal/tabs/profile_tab.js | 10 +-- src/components/settings_modal/tabs/profile_tab.vue | 2 +- .../settings_modal/tabs/security_tab/mfa.js | 6 +- .../settings_modal/tabs/security_tab/mfa_totp.js | 2 +- .../tabs/security_tab/security_tab.js | 2 +- .../settings_modal/tabs/theme_tab/preview.vue | 5 +- .../settings_modal/tabs/theme_tab/theme_tab.js | 8 +-- src/components/shadow_control/shadow_control.js | 8 ++- src/components/shadow_control/shadow_control.vue | 2 +- src/components/shout_panel/shout_panel.js | 2 +- src/components/side_drawer/side_drawer.js | 2 +- src/components/side_drawer/side_drawer.vue | 2 +- src/components/staff_panel/staff_panel.js | 4 +- src/components/staff_panel/staff_panel.vue | 2 +- src/components/status/status.js | 6 +- src/components/status/status.vue | 11 ++-- src/components/status_body/status_body.vue | 2 +- src/components/status_content/status_content.vue | 2 +- src/components/status_popover/status_popover.vue | 8 +-- src/components/sticker_picker/sticker_picker.js | 4 +- .../terms_of_service_panel.vue | 2 +- src/components/timeline/timeline.vue | 5 +- .../timeline/timeline_quick_settings.vue | 4 +- src/components/timeline_menu/timeline_menu.js | 6 +- src/components/timeline_menu/timeline_menu.vue | 6 +- .../timeline_menu/timeline_menu_content.vue | 2 +- src/components/user_card/user_card.js | 2 +- src/components/user_card/user_card.vue | 2 +- .../user_list_popover/user_list_popover.vue | 6 +- src/components/user_popover/user_popover.vue | 42 ++++++------- src/components/user_profile/user_profile.vue | 15 +++-- .../user_reporting_modal/user_reporting_modal.vue | 2 +- src/components/who_to_follow/who_to_follow.js | 2 +- .../who_to_follow_panel/who_to_follow_panel.js | 10 +-- .../who_to_follow_panel/who_to_follow_panel.vue | 2 +- src/i18n/messages.js | 2 +- src/lib/notification-i18n-loader.js | 4 +- src/lib/persisted_state.js | 12 ++-- src/modules/api.js | 4 +- src/modules/config.js | 2 +- src/modules/errors.js | 4 +- src/modules/serverSideConfig.js | 28 ++++----- src/modules/statuses.js | 20 +++--- src/modules/users.js | 24 ++++---- src/services/api/api.service.js | 72 +++++++++++----------- src/services/chat_service/chat_service.js | 12 ++-- src/services/chat_utils/chat_utils.js | 2 +- src/services/color_convert/color_convert.js | 12 ++-- src/services/completion/completion.js | 2 +- src/services/date_utils/date_utils.js | 2 +- .../entity_normalizer/entity_normalizer.service.js | 4 +- src/services/file_size_format/file_size_format.js | 8 +-- .../html_converter/html_line_converter.service.js | 4 +- src/services/html_converter/utility.service.js | 2 +- src/services/locale/locale.service.js | 14 ++--- src/services/new_api/password_reset.js | 2 +- .../notifications_fetcher.service.js | 12 ++-- src/services/push/push.js | 4 +- src/services/theme_data/theme_data.service.js | 2 +- .../timeline_fetcher/timeline_fetcher.service.js | 16 ++--- src/services/user_highlighter/user_highlighter.js | 2 +- src/sw.js | 4 +- test/e2e/custom-assertions/elementCount.js | 2 +- test/e2e/nightwatch.conf.js | 54 ++++++++-------- test/e2e/runner.js | 8 +-- test/unit/karma.conf.js | 14 ++--- test/unit/specs/components/emoji_input.spec.js | 2 +- test/unit/specs/components/rich_content.spec.js | 2 +- test/unit/specs/components/user_profile.spec.js | 6 +- test/unit/specs/modules/statuses.spec.js | 4 +- .../services/chat_service/chat_service.spec.js | 24 ++++---- .../entity_normalizer/entity_normalizer.spec.js | 8 +-- .../file_size_format/file_size_format.spec.js | 4 +- 133 files changed, 508 insertions(+), 449 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/App.vue b/src/App.vue index 7d4a8e1e..0efadaf0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -15,8 +15,12 @@ class="app-layout container" :class="classes" > -
    -