From 6bb75a3a6d8452a3e1b88085fe87cf27386f222c Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Tue, 21 Apr 2020 23:27:51 +0300 Subject: make relationships separate from users --- src/components/status/status.js | 15 ++++++++++++--- 1 file changed, 12 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 fc5956ec..a73e3ae2 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -118,7 +118,13 @@ const Status = { return hits }, - muted () { return !this.unmuted && ((!(this.inProfile && this.status.user.id === this.profileUserId) && this.status.user.muted) || (!this.inConversation && this.status.thread_muted) || this.muteWordHits.length > 0) }, + muted () { + const relationship = this.$store.state.users.relationships[this.status.user.id] || {} + return !this.unmuted && ( + (!(this.inProfile && this.status.user.id === this.profileUserId) && relationship.muting) || + (!this.inConversation && this.status.thread_muted) || + this.muteWordHits.length > 0) + }, hideFilteredStatuses () { return this.mergedConfig.hideFilteredStatuses }, @@ -178,8 +184,11 @@ const Status = { if (this.status.user.id === this.status.attentions[i].id) { continue } - const taggedUser = this.$store.getters.findUser(this.status.attentions[i].id) - if (checkFollowing && taggedUser && taggedUser.following) { + // There's zero guarantee of this working. If we happen to have that user and their + // relationship in store then it will work, but there's kinda little chance of having + // them for people you're not following. + const relationship = this.$store.state.users.relationships[this.status.attentions[i].id] + if (checkFollowing && relationship && relationship.following) { return false } if (this.status.attentions[i].id === this.currentUser.id) { -- cgit v1.2.3-70-g09d2 From af9492977aaa10903d54add3187b5cf9d9a00d6c Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Fri, 24 Apr 2020 18:53:17 +0300 Subject: add back mute prediction, add getter for relationships --- src/components/block_card/block_card.js | 2 +- src/components/follow_card/follow_card.js | 2 +- src/components/mute_card/mute_card.js | 2 +- src/components/notification/notification.js | 2 +- src/components/status/status.js | 2 +- src/components/user_card/user_card.js | 2 +- src/components/user_settings/user_settings.js | 4 ++-- src/modules/users.js | 12 ++++++++++++ test/unit/specs/components/user_profile.spec.js | 1 + 9 files changed, 21 insertions(+), 8 deletions(-) (limited to 'src/components/status/status.js') diff --git a/src/components/block_card/block_card.js b/src/components/block_card/block_card.js index 659c75d8..0bf4e37b 100644 --- a/src/components/block_card/block_card.js +++ b/src/components/block_card/block_card.js @@ -12,7 +12,7 @@ const BlockCard = { return this.$store.getters.findUser(this.userId) }, relationship () { - return this.$store.state.users.relationships[this.userId] || {} + return this.$store.getters.relationship(this.userId) }, blocked () { return this.relationship.blocking diff --git a/src/components/follow_card/follow_card.js b/src/components/follow_card/follow_card.js index 620ae7fd..6dcb6d47 100644 --- a/src/components/follow_card/follow_card.js +++ b/src/components/follow_card/follow_card.js @@ -20,7 +20,7 @@ const FollowCard = { return this.$store.state.users.currentUser }, relationship () { - return this.$store.state.users.relationships[this.user.id] + return this.$store.getters.relationship(this.user.id) } } } diff --git a/src/components/mute_card/mute_card.js b/src/components/mute_card/mute_card.js index be528d37..cbec0e9b 100644 --- a/src/components/mute_card/mute_card.js +++ b/src/components/mute_card/mute_card.js @@ -12,7 +12,7 @@ const MuteCard = { return this.$store.getters.findUser(this.userId) }, relationship () { - return this.$store.state.users.relationships[this.userId] + return this.$store.getters.relationship(this.userId) }, muted () { return this.relationship.muting diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 09554f54..ff1c2817 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -56,7 +56,7 @@ const Notification = { return this.generateUserProfileLink(this.targetUser) }, needMute () { - return (this.$store.state.users.relationships[this.user.id] || {}).muting + return this.$store.getters.relationship(this.user.id).muting } } } diff --git a/src/components/status/status.js b/src/components/status/status.js index a73e3ae2..a36de028 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -119,7 +119,7 @@ const Status = { return hits }, muted () { - const relationship = this.$store.state.users.relationships[this.status.user.id] || {} + const relationship = this.$store.getters.relationship(this.userId) return !this.unmuted && ( (!(this.inProfile && this.status.user.id === this.profileUserId) && relationship.muting) || (!this.inConversation && this.status.thread_muted) || diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index fb3cfebc..8e6b9d7f 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -25,7 +25,7 @@ export default { return this.$store.getters.findUser(this.userId) }, relationship () { - return this.$store.state.users.relationships[this.userId] || {} + return this.$store.getters.relationship(this.userId) }, classes () { return [{ diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js index adfab8fa..5338c974 100644 --- a/src/components/user_settings/user_settings.js +++ b/src/components/user_settings/user_settings.js @@ -351,13 +351,13 @@ const UserSettings = { }, filterUnblockedUsers (userIds) { return reject(userIds, (userId) => { - const relationship = this.$store.state.users.relationships[userId] || {} + const relationship = this.$store.getters.relationship(this.userId) return relationship.blocking || userId === this.$store.state.users.currentUser.id }) }, filterUnMutedUsers (userIds) { return reject(userIds, (userId) => { - const relationship = this.$store.state.users.relationships[userId] || {} + const relationship = this.$store.getters.relationship(this.userId) return relationship.muting || userId === this.$store.state.users.currentUser.id }) }, diff --git a/src/modules/users.js b/src/modules/users.js index 6b19fc97..fb04ebd3 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -48,6 +48,11 @@ const unblockUser = (store, id) => { } const muteUser = (store, id) => { + const predictedRelationship = store.state.relationships[id] || { id } + predictedRelationship.muting = true + store.commit('updateUserRelationship', [predictedRelationship]) + store.commit('addMuteId', id) + return store.rootState.api.backendInteractor.muteUser({ id }) .then((relationship) => { store.commit('updateUserRelationship', [relationship]) @@ -56,6 +61,10 @@ const muteUser = (store, id) => { } const unmuteUser = (store, id) => { + const predictedRelationship = store.state.relationships[id] || { id } + predictedRelationship.muting = false + store.commit('updateUserRelationship', [predictedRelationship]) + return store.rootState.api.backendInteractor.unmuteUser({ id }) .then((relationship) => store.commit('updateUserRelationship', [relationship])) } @@ -227,6 +236,9 @@ export const getters = { return state.usersObject[query.toLowerCase()] } return result + }, + relationship: state => id => { + return state.relationships[id] || { id, loading: true } } } diff --git a/test/unit/specs/components/user_profile.spec.js b/test/unit/specs/components/user_profile.spec.js index 0a3f2d27..dcf066f9 100644 --- a/test/unit/specs/components/user_profile.spec.js +++ b/test/unit/specs/components/user_profile.spec.js @@ -19,6 +19,7 @@ const actions = { const testGetters = { findUser: state => getters.findUser(state.users), + relationship: state => getters.relationship(state.users), mergedConfig: state => ({ colors: '', highlight: {}, -- cgit v1.2.3-70-g09d2 From 8b1aa593a46869ac1ea26de8a1f31d9fa2f44e56 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Mon, 27 Apr 2020 10:06:17 +0300 Subject: fix status mutes --- src/components/status/status.js | 2 +- src/modules/users.js | 3 ++- 2 files changed, 3 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 a36de028..890f4b91 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -119,7 +119,7 @@ const Status = { return hits }, muted () { - const relationship = this.$store.getters.relationship(this.userId) + const relationship = this.$store.getters.relationship(this.status.user.id) return !this.unmuted && ( (!(this.inProfile && this.status.user.id === this.profileUserId) && relationship.muting) || (!this.inConversation && this.status.thread_muted) || diff --git a/src/modules/users.js b/src/modules/users.js index fb04ebd3..1d1b415c 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -238,7 +238,8 @@ export const getters = { return result }, relationship: state => id => { - return state.relationships[id] || { id, loading: true } + const rel = id && state.relationships[id] + return rel || { id, loading: true } } } -- cgit v1.2.3-70-g09d2 From 7a25160ddc15a97a02366dff60ec12801348f229 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Mon, 27 Apr 2020 12:53:04 +0300 Subject: Separate status content from status like in direct conversations mr --- src/components/status/status.js | 188 +----------------- src/components/status/status.vue | 230 +--------------------- src/components/status_content/status_content.js | 210 ++++++++++++++++++++ src/components/status_content/status_content.vue | 240 +++++++++++++++++++++++ 4 files changed, 460 insertions(+), 408 deletions(-) create mode 100644 src/components/status_content/status_content.js create mode 100644 src/components/status_content/status_content.vue (limited to 'src/components/status/status.js') diff --git a/src/components/status/status.js b/src/components/status/status.js index 61d66301..ab3de5fc 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -1,23 +1,17 @@ -import Attachment from '../attachment/attachment.vue' import FavoriteButton from '../favorite_button/favorite_button.vue' import ReactButton from '../react_button/react_button.vue' import RetweetButton from '../retweet_button/retweet_button.vue' -import Poll from '../poll/poll.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 Gallery from '../gallery/gallery.vue' -import LinkPreview from '../link-preview/link-preview.vue' import AvatarList from '../avatar_list/avatar_list.vue' import Timeago from '../timeago/timeago.vue' +import StatusContent from '../status_content/status_content.vue' import StatusPopover from '../status_popover/status_popover.vue' import EmojiReactions from '../emoji_reactions/emoji_reactions.vue' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' -import fileType from 'src/services/file_type/file_type.service' -import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' -import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js' import { filter, unescape, uniqBy } from 'lodash' import { mapGetters, mapState } from 'vuex' @@ -43,17 +37,10 @@ const Status = { replying: false, unmuted: false, userExpanded: false, - showingTall: this.inConversation && this.focused, - showingLongSubject: false, - error: null, - // not as computed because it sets the initial state which will be changed later - expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject + error: null } }, computed: { - localCollapseSubjectDefault () { - return this.mergedConfig.collapseMessageWithSubject - }, muteWords () { return this.mergedConfig.muteWords }, @@ -79,10 +66,6 @@ const Status = { const highlight = this.mergedConfig.highlight return highlightStyle(highlight[user.screen_name]) }, - hideAttachments () { - return (this.mergedConfig.hideAttachments && !this.inConversation) || - (this.mergedConfig.hideAttachmentsInConv && this.inConversation) - }, userProfileLink () { return this.generateUserProfileLink(this.status.user.id, this.status.user.screen_name) }, @@ -135,20 +118,6 @@ const Status = { // use conversation highlight only when in conversation return this.status.id === this.highlight }, - // This is a bit hacky, but we want to approximate post height before rendering - // so we count newlines (masto uses

for paragraphs, GS uses
between them) - // as well as approximate line count by counting characters and approximating ~80 - // per line. - // - // Using max-height + overflow: auto for status components resulted in false positives - // very often with japanese characters, and it was very annoying. - tallStatus () { - const lengthScore = this.status.statusnet_html.split(/ 20 - }, - longSubject () { - return this.status.summary.length > 900 - }, isReply () { return !!(this.status.in_reply_to_status_id && this.status.in_reply_to_user_id) }, @@ -188,32 +157,6 @@ const Status = { } return this.status.attentions.length > 0 }, - - // When a status has a subject and is also tall, we should only have one show more/less button. If the default is to collapse statuses with subjects, we just treat it like a status with a subject; otherwise, we just treat it like a tall status. - mightHideBecauseSubject () { - return this.status.summary && (!this.tallStatus || this.localCollapseSubjectDefault) - }, - mightHideBecauseTall () { - return this.tallStatus && (!this.status.summary || !this.localCollapseSubjectDefault) - }, - hideSubjectStatus () { - return this.mightHideBecauseSubject && !this.expandingSubject - }, - hideTallStatus () { - return this.mightHideBecauseTall && !this.showingTall - }, - showingMore () { - return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject) - }, - nsfwClickthrough () { - if (!this.status.nsfw) { - return false - } - if (this.status.summary && this.localCollapseSubjectDefault) { - return false - } - return true - }, replySubject () { if (!this.status.summary) return '' const decodedSummary = unescape(this.status.summary) @@ -227,83 +170,6 @@ const Status = { return '' } }, - attachmentSize () { - if ((this.mergedConfig.hideAttachments && !this.inConversation) || - (this.mergedConfig.hideAttachmentsInConv && this.inConversation) || - (this.status.attachments.length > this.maxThumbnails)) { - return 'hide' - } else if (this.compact) { - return 'small' - } - return 'normal' - }, - galleryTypes () { - if (this.attachmentSize === 'hide') { - return [] - } - return this.mergedConfig.playVideosInModal - ? ['image', 'video'] - : ['image'] - }, - galleryAttachments () { - return this.status.attachments.filter( - file => fileType.fileMatchesSomeType(this.galleryTypes, file) - ) - }, - nonGalleryAttachments () { - return this.status.attachments.filter( - file => !fileType.fileMatchesSomeType(this.galleryTypes, file) - ) - }, - hasImageAttachments () { - return this.status.attachments.some( - file => fileType.fileType(file.mimetype) === 'image' - ) - }, - hasVideoAttachments () { - return this.status.attachments.some( - file => fileType.fileType(file.mimetype) === 'video' - ) - }, - maxThumbnails () { - return this.mergedConfig.maxThumbnails - }, - postBodyHtml () { - const html = this.status.statusnet_html - - if (this.mergedConfig.greentext) { - try { - if (html.includes('>')) { - // This checks if post has '>' at the beginning, excluding mentions so that @mention >impying works - return processHtml(html, (string) => { - if (string.includes('>') && - string - .replace(/<[^>]+?>/gi, '') // remove all tags - .replace(/@\w+/gi, '') // remove mentions (even failed ones) - .trim() - .startsWith('>')) { - return `${string}` - } else { - return string - } - }) - } else { - return html - } - } catch (e) { - console.err('Failed to process status html', e) - return html - } - } else { - return html - } - }, - contentHtml () { - if (!this.status.summary_html) { - return this.postBodyHtml - } - return this.status.summary_html + '
' + this.postBodyHtml - }, combinedFavsAndRepeatsUsers () { // Use the status from the global status repository since favs and repeats are saved in it const combinedUsers = [].concat( @@ -312,9 +178,6 @@ const Status = { ) return uniqBy(combinedUsers, 'id') }, - ownStatus () { - return this.status.user.id === this.currentUser.id - }, tags () { return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ') }, @@ -328,21 +191,18 @@ const Status = { }) }, components: { - Attachment, FavoriteButton, ReactButton, RetweetButton, ExtraButtons, PostStatusForm, - Poll, UserCard, UserAvatar, - Gallery, - LinkPreview, AvatarList, Timeago, StatusPopover, - EmojiReactions + EmojiReactions, + StatusContent }, methods: { visibilityIcon (visibility) { @@ -363,32 +223,6 @@ const Status = { clearError () { this.error = undefined }, - linkClicked (event) { - const target = event.target.closest('.status-content a') - if (target) { - if (target.className.match(/mention/)) { - const href = target.href - const attn = this.status.attentions.find(attn => mentionMatchesUrl(attn, href)) - if (attn) { - event.stopPropagation() - event.preventDefault() - const link = this.generateUserProfileLink(attn.id, attn.screen_name) - this.$router.push(link) - return - } - } - if (target.rel.match(/(?:^|\s)tag(?:$|\s)/) || target.className.match(/hashtag/)) { - // Extract tag name from link url - const tag = extractTagFromUrl(target.href) - if (tag) { - const link = this.generateTagLink(tag) - this.$router.push(link) - return - } - } - window.open(target.href, '_blank') - } - }, toggleReplying () { this.replying = !this.replying }, @@ -406,22 +240,8 @@ const Status = { toggleUserExpanded () { this.userExpanded = !this.userExpanded }, - toggleShowMore () { - if (this.mightHideBecauseTall) { - this.showingTall = !this.showingTall - } else if (this.mightHideBecauseSubject) { - this.expandingSubject = !this.expandingSubject - } - }, generateUserProfileLink (id, name) { return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames) - }, - generateTagLink (tag) { - return `/tag/${tag}` - }, - setMedia () { - const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments - return () => this.$store.dispatch('setMedia', attachments) } }, watch: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index ca295640..1ee4f27c 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -226,118 +226,12 @@ -

- {{ $t("general.show_more") }} - -
- {{ $t("general.show_more") }} -
- - -
- -
- -
- - -
- - +
{ - .status-el:last-child { - border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; - border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); - border-bottom: none; - } -} - .favs-repeated-users { margin-top: $status-margin; diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js new file mode 100644 index 00000000..ccc01b6f --- /dev/null +++ b/src/components/status_content/status_content.js @@ -0,0 +1,210 @@ +import Attachment from '../attachment/attachment.vue' +import Poll from '../poll/poll.vue' +import Gallery from '../gallery/gallery.vue' +import LinkPreview from '../link-preview/link-preview.vue' +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' +import fileType from 'src/services/file_type/file_type.service' +import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js' +import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js' +import { mapGetters, mapState } from 'vuex' + +const StatusContent = { + name: 'StatusContent', + props: [ + 'status', + 'focused', + 'noHeading', + 'fullContent' + ], + data () { + return { + showingTall: this.inConversation && this.focused, + showingLongSubject: false, + // not as computed because it sets the initial state which will be changed later + expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject + } + }, + computed: { + localCollapseSubjectDefault () { + return this.mergedConfig.collapseMessageWithSubject + }, + hideAttachments () { + return (this.mergedConfig.hideAttachments && !this.inConversation) || + (this.mergedConfig.hideAttachmentsInConv && this.inConversation) + }, + // This is a bit hacky, but we want to approximate post height before rendering + // so we count newlines (masto uses

for paragraphs, GS uses
between them) + // as well as approximate line count by counting characters and approximating ~80 + // per line. + // + // Using max-height + overflow: auto for status components resulted in false positives + // very often with japanese characters, and it was very annoying. + tallStatus () { + const lengthScore = this.status.statusnet_html.split(/ 20 + }, + longSubject () { + return this.status.summary.length > 900 + }, + // When a status has a subject and is also tall, we should only have one show more/less button. If the default is to collapse statuses with subjects, we just treat it like a status with a subject; otherwise, we just treat it like a tall status. + mightHideBecauseSubject () { + return this.status.summary && (!this.tallStatus || this.localCollapseSubjectDefault) + }, + mightHideBecauseTall () { + return this.tallStatus && (!this.status.summary || !this.localCollapseSubjectDefault) + }, + hideSubjectStatus () { + return this.mightHideBecauseSubject && !this.expandingSubject + }, + hideTallStatus () { + return this.mightHideBecauseTall && !this.showingTall + }, + showingMore () { + return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject) + }, + nsfwClickthrough () { + if (!this.status.nsfw) { + return false + } + if (this.status.summary && this.localCollapseSubjectDefault) { + return false + } + return true + }, + attachmentSize () { + if ((this.mergedConfig.hideAttachments && !this.inConversation) || + (this.mergedConfig.hideAttachmentsInConv && this.inConversation) || + (this.status.attachments.length > this.maxThumbnails)) { + return 'hide' + } else if (this.compact) { + return 'small' + } + return 'normal' + }, + galleryTypes () { + if (this.attachmentSize === 'hide') { + return [] + } + return this.mergedConfig.playVideosInModal + ? ['image', 'video'] + : ['image'] + }, + galleryAttachments () { + return this.status.attachments.filter( + file => fileType.fileMatchesSomeType(this.galleryTypes, file) + ) + }, + nonGalleryAttachments () { + return this.status.attachments.filter( + file => !fileType.fileMatchesSomeType(this.galleryTypes, file) + ) + }, + hasImageAttachments () { + return this.status.attachments.some( + file => fileType.fileType(file.mimetype) === 'image' + ) + }, + hasVideoAttachments () { + return this.status.attachments.some( + file => fileType.fileType(file.mimetype) === 'video' + ) + }, + maxThumbnails () { + return this.mergedConfig.maxThumbnails + }, + postBodyHtml () { + const html = this.status.statusnet_html + + if (this.mergedConfig.greentext) { + try { + if (html.includes('>')) { + // This checks if post has '>' at the beginning, excluding mentions so that @mention >impying works + return processHtml(html, (string) => { + if (string.includes('>') && + string + .replace(/<[^>]+?>/gi, '') // remove all tags + .replace(/@\w+/gi, '') // remove mentions (even failed ones) + .trim() + .startsWith('>')) { + return `${string}` + } else { + return string + } + }) + } else { + return html + } + } catch (e) { + console.err('Failed to process status html', e) + return html + } + } else { + return html + } + }, + contentHtml () { + if (!this.status.summary_html) { + return this.postBodyHtml + } + return this.status.summary_html + '
' + this.postBodyHtml + }, + ...mapGetters(['mergedConfig']), + ...mapState({ + betterShadow: state => state.interface.browserSupport.cssFilter, + currentUser: state => state.users.currentUser + }) + }, + components: { + Attachment, + Poll, + Gallery, + LinkPreview + }, + methods: { + linkClicked (event) { + const target = event.target.closest('.status-content a') + if (target) { + if (target.className.match(/mention/)) { + const href = target.href + const attn = this.status.attentions.find(attn => mentionMatchesUrl(attn, href)) + if (attn) { + event.stopPropagation() + event.preventDefault() + const link = this.generateUserProfileLink(attn.id, attn.screen_name) + this.$router.push(link) + return + } + } + if (target.rel.match(/(?:^|\s)tag(?:$|\s)/) || target.className.match(/hashtag/)) { + // Extract tag name from link url + const tag = extractTagFromUrl(target.href) + if (tag) { + const link = this.generateTagLink(tag) + this.$router.push(link) + return + } + } + window.open(target.href, '_blank') + } + }, + toggleShowMore () { + if (this.mightHideBecauseTall) { + this.showingTall = !this.showingTall + } else if (this.mightHideBecauseSubject) { + this.expandingSubject = !this.expandingSubject + } + }, + generateUserProfileLink (id, name) { + return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames) + }, + generateTagLink (tag) { + return `/tag/${tag}` + }, + setMedia () { + const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments + return () => this.$store.dispatch('setMedia', attachments) + } + } +} + +export default StatusContent diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue new file mode 100644 index 00000000..8c2e8749 --- /dev/null +++ b/src/components/status_content/status_content.vue @@ -0,0 +1,240 @@ + + + + -- cgit v1.2.3-70-g09d2