diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/emoji_picker/emoji_picker.vue | 3 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.js | 38 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.vue | 34 | ||||
| -rw-r--r-- | src/components/report/report.js | 5 | ||||
| -rw-r--r-- | src/components/status/status.js | 28 | ||||
| -rw-r--r-- | src/components/status/status.scss | 18 | ||||
| -rw-r--r-- | src/components/status/status.vue | 39 | ||||
| -rw-r--r-- | src/components/status_content/status_content.js | 4 | ||||
| -rw-r--r-- | src/components/status_content/status_content.vue | 2 | ||||
| -rw-r--r-- | src/components/timeline/timeline.js | 3 |
10 files changed, 167 insertions, 7 deletions
diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue index 227d7718..b8d33309 100644 --- a/src/components/emoji_picker/emoji_picker.vue +++ b/src/components/emoji_picker/emoji_picker.vue @@ -28,6 +28,7 @@ active: activeGroupView === group.id }" :title="group.text" + role="button" @click.prevent="highlight(group.id)" > <span @@ -116,6 +117,7 @@ :key="group.id + emoji.displayText" :title="maybeLocalizedEmojiName(emoji)" class="emoji-item" + role="button" @click.stop.prevent="onEmoji(emoji)" > <span @@ -126,6 +128,7 @@ v-else class="emoji-picker-emoji -custom" loading="lazy" + :alt="maybeLocalizedEmojiName(emoji)" :src="emoji.imageUrl" :data-emoji-name="group.id + emoji.displayText" /> diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index b75fee69..ba49961d 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -156,11 +156,13 @@ const PostStatusForm = { poll: this.statusPoll || {}, mediaDescriptions: this.statusMediaDescriptions || {}, visibility: this.statusScope || scope, - contentType: statusContentType + contentType: statusContentType, + quoting: false } } return { + randomSeed: `${Math.random()}`.replace('.', '-'), dropFiles: [], uploadingFiles: false, error: null, @@ -265,6 +267,30 @@ const PostStatusForm = { isEdit () { return typeof this.statusId !== 'undefined' && this.statusId.trim() !== '' }, + quotable () { + if (!this.$store.state.instance.quotingAvailable) { + return false + } + + if (!this.replyTo) { + return false + } + + const repliedStatus = this.$store.state.statuses.allStatusesObject[this.replyTo] + if (!repliedStatus) { + return false + } + + if (repliedStatus.visibility === 'public' || + repliedStatus.visibility === 'unlisted' || + repliedStatus.visibility === 'local') { + return true + } else if (repliedStatus.visibility === 'private') { + return repliedStatus.user.id === this.$store.state.users.currentUser.id + } + + return false + }, ...mapGetters(['mergedConfig']), ...mapState({ mobileLayout: state => state.interface.mobileLayout @@ -292,7 +318,8 @@ const PostStatusForm = { visibility: newStatus.visibility, contentType: newStatus.contentType, poll: {}, - mediaDescriptions: {} + mediaDescriptions: {}, + quoting: false } this.pollFormVisible = false this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile() @@ -340,6 +367,8 @@ const PostStatusForm = { return } + const replyOrQuoteAttr = newStatus.quoting ? 'quoteId' : 'inReplyToStatusId' + const postingOptions = { status: newStatus.status, spoilerText: newStatus.spoilerText || null, @@ -347,7 +376,7 @@ const PostStatusForm = { sensitive: newStatus.nsfw, media: newStatus.files, store: this.$store, - inReplyToStatusId: this.replyTo, + [replyOrQuoteAttr]: this.replyTo, contentType: newStatus.contentType, poll, idempotencyKey: this.idempotencyKey @@ -373,6 +402,7 @@ const PostStatusForm = { } const newStatus = this.newStatus this.previewLoading = true + const replyOrQuoteAttr = newStatus.quoting ? 'quoteId' : 'inReplyToStatusId' statusPoster.postStatus({ status: newStatus.status, spoilerText: newStatus.spoilerText || null, @@ -380,7 +410,7 @@ const PostStatusForm = { sensitive: newStatus.nsfw, media: [], store: this.$store, - inReplyToStatusId: this.replyTo, + [replyOrQuoteAttr]: this.replyTo, contentType: newStatus.contentType, poll: {}, preview: true diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 86c1f907..9b108a5a 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -126,6 +126,36 @@ class="preview-status" /> </div> + <div + v-if="quotable" + role="radiogroup" + class="btn-group reply-or-quote-selector" + > + <button + :id="`reply-or-quote-option-${randomSeed}-reply`" + class="btn button-default reply-or-quote-option" + :class="{ toggled: !newStatus.quoting }" + tabindex="0" + role="radio" + :aria-labelledby="`reply-or-quote-option-${randomSeed}-reply`" + :aria-checked="!newStatus.quoting" + @click="newStatus.quoting = false" + > + {{ $t('post_status.reply_option') }} + </button> + <button + :id="`reply-or-quote-option-${randomSeed}-quote`" + class="btn button-default reply-or-quote-option" + :class="{ toggled: newStatus.quoting }" + tabindex="0" + role="radio" + :aria-labelledby="`reply-or-quote-option-${randomSeed}-quote`" + :aria-checked="newStatus.quoting" + @click="newStatus.quoting = true" + > + {{ $t('post_status.quote_option') }} + </button> + </div> <EmojiInput v-if="!disableSubject && (newStatus.spoilerText || alwaysShowSubject)" v-model="newStatus.spoilerText" @@ -420,6 +450,10 @@ margin: 0; } + .reply-or-quote-selector { + margin-bottom: 0.5em; + } + .text-format { .only-format { color: $fallback--faint; diff --git a/src/components/report/report.js b/src/components/report/report.js index 76055764..5685aa25 100644 --- a/src/components/report/report.js +++ b/src/components/report/report.js @@ -1,6 +1,7 @@ import Select from '../select/select.vue' import StatusContent from '../status_content/status_content.vue' import Timeago from '../timeago/timeago.vue' +import RichContent from 'src/components/rich_content/rich_content.jsx' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' const Report = { @@ -10,10 +11,12 @@ const Report = { components: { Select, StatusContent, - Timeago + Timeago, + RichContent }, computed: { report () { + console.log(this.$store.state.reports.reports[this.reportId] || {}) return this.$store.state.reports.reports[this.reportId] || {} }, state: { diff --git a/src/components/status/status.js b/src/components/status/status.js index 9a9bca7a..e722a635 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -133,6 +133,7 @@ const Status = { 'showPinned', 'inProfile', 'profileUserId', + 'inQuote', 'simpleTree', 'controlledThreadDisplayStatus', @@ -159,7 +160,8 @@ const Status = { uncontrolledMediaPlaying: [], suspendable: true, error: null, - headTailLinks: null + headTailLinks: null, + displayQuote: !this.inQuote } }, computed: { @@ -401,6 +403,18 @@ const Status = { }, editingAvailable () { return this.$store.state.instance.editingAvailable + }, + hasVisibleQuote () { + return this.status.quote_url && this.status.quote_visible + }, + hasInvisibleQuote () { + return this.status.quote_url && !this.status.quote_visible + }, + quotedStatus () { + return this.status.quote_id ? this.$store.state.statuses.allStatusesObject[this.status.quote_id] : undefined + }, + shouldDisplayQuote () { + return this.quotedStatus && this.displayQuote } }, methods: { @@ -469,6 +483,18 @@ const Status = { window.scrollBy(0, rect.bottom - window.innerHeight + 50) } } + }, + toggleDisplayQuote () { + if (this.shouldDisplayQuote) { + this.displayQuote = false + } else if (!this.quotedStatus) { + this.$store.dispatch('fetchStatus', this.status.quote_id) + .then(() => { + this.displayQuote = true + }) + } else { + this.displayQuote = true + } } }, watch: { diff --git a/src/components/status/status.scss b/src/components/status/status.scss index 44812867..760c6ac1 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -422,4 +422,22 @@ } } } + + .quoted-status { + margin-top: 0.5em; + border: 1px solid var(--border, $fallback--border); + border-radius: var(--attachmentRadius, $fallback--attachmentRadius); + + &.-unavailable-prompt { + padding: 0.5em; + } + } + + .display-quoted-status-button { + margin: 0.5em; + + &-icon { + color: inherit; + } + } } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 35b15362..c49a9e7b 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -364,6 +364,45 @@ @parseReady="setHeadTailLinks" /> + <article + v-if="hasVisibleQuote" + class="quoted-status" + > + <button + class="button-unstyled -link display-quoted-status-button" + :aria-expanded="shouldDisplayQuote" + @click="toggleDisplayQuote" + > + {{ shouldDisplayQuote ? $t('status.hide_quote') : $t('status.display_quote') }} + <FAIcon + class="display-quoted-status-button-icon" + :icon="shouldDisplayQuote ? 'chevron-up' : 'chevron-down'" + /> + </button> + <Status + v-if="shouldDisplayQuote" + :statusoid="quotedStatus" + :in-quote="true" + /> + </article> + <p + v-else-if="hasInvisibleQuote" + class="quoted-status -unavailable-prompt" + > + <i18n-t keypath="status.invisible_quote"> + <template #link> + <bdi> + <a + :href="status.quote_url" + target="_blank" + > + {{ status.quote_url }} + </a> + </bdi> + </template> + </i18n-t> + </p> + <div v-if="inConversation && !isPreview && replies && replies.length" class="replies" diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index 89f0aa51..8d8a91dc 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -73,6 +73,10 @@ const StatusContent = { }, computed: { ...controlledOrUncontrolledGetters(['showingTall', 'expandingSubject', 'showingLongSubject']), + statusCard () { + if (!this.status.card) return null + return this.status.card.url === this.status.quote_url ? null : this.status.card + }, hideAttachments () { return (this.mergedConfig.hideAttachments && !this.inConversation) || (this.mergedConfig.hideAttachmentsInConv && this.inConversation) diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue index c0e5c0b9..e977d489 100644 --- a/src/components/status_content/status_content.vue +++ b/src/components/status_content/status_content.vue @@ -43,7 +43,7 @@ /> <div - v-if="status.card && !noHeading && !compact" + v-if="statusCard && !noHeading && !compact" class="link-preview media-body" > <link-preview diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index b7414610..1050b87a 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -160,6 +160,9 @@ const Timeline = { if (this.timeline.flushMarker !== 0) { this.$store.commit('clearTimeline', { timeline: this.timelineName, excludeUserId: true }) this.$store.commit('queueFlush', { timeline: this.timelineName, id: 0 }) + if (this.timelineName === 'user') { + this.$store.dispatch('fetchPinnedStatuses', this.userId) + } this.fetchOlderStatuses() } else { this.blockClicksTemporarily() |
