diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index d5bce12a7..f7d6ebf10 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -52,6 +52,7 @@ const Attachment = {
'shiftDn',
'edit',
],
+ emits: ['play', 'pause', 'naturalSizeLoad'],
data() {
return {
localDescription: this.description || this.attachment.description,
diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index 54bcbd373..696167831 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -55,16 +55,6 @@ const sortAndFilterConversation = (conversation, statusoid) => {
}
const conversation = {
- data() {
- return {
- highlight: null,
- expanded: false,
- threadDisplayStatusObject: {}, // id => 'showing' | 'hidden'
- statusContentPropertiesObject: {},
- inlineDivePosition: null,
- loadStatusError: null,
- }
- },
props: [
'statusId',
'collapsable',
@@ -74,6 +64,16 @@ const conversation = {
'profileUserId',
'virtualHidden',
],
+ data() {
+ return {
+ highlight: null,
+ expanded: false,
+ threadDisplayStatusObject: {}, // id => 'showing' | 'hidden'
+ inlineDivePosition: null,
+ loadStatusError: null,
+ unsuspendibleIds: new Set(),
+ }
+ },
created() {
if (this.isPage) {
this.fetchConversation()
@@ -118,16 +118,7 @@ const conversation = {
return this.otherRepliesButtonPosition === 'inside'
},
suspendable() {
- if (this.isTreeView) {
- return Object.entries(this.statusContentProperties).every(
- ([, prop]) => !prop.replying && prop.mediaPlaying.length === 0,
- )
- }
- if (this.$refs.statusComponent && this.$refs.statusComponent[0]) {
- return this.$refs.statusComponent.every((s) => s.suspendable)
- } else {
- return true
- }
+ return this.unsuspendibleIds.size > 0
},
hideStatus() {
return this.virtualHidden && this.suspendable
@@ -364,31 +355,6 @@ const conversation = {
return a
}, {})
},
- statusContentProperties() {
- return this.conversation.reduce((a, k) => {
- const id = k.id
- const props = (() => {
- const def = {
- showingTall: false,
- expandingSubject: false,
- showingLongSubject: false,
- isReplying: false,
- mediaPlaying: [],
- }
-
- if (this.statusContentPropertiesObject[id]) {
- return {
- ...def,
- ...this.statusContentPropertiesObject[id],
- }
- }
- return def
- })()
-
- a[id] = props
- return a
- }, {})
- },
canDive() {
return this.isTreeView && this.isExpanded
},
@@ -514,22 +480,6 @@ 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],
- )
- },
leastVisibleAncestor(id) {
let cur = id
let parent = this.parentOf(cur)
@@ -629,6 +579,13 @@ const conversation = {
this.undive()
this.threadDisplayStatusObject = {}
},
+ onStatusSuspendStateChange({ id, suspend }) {
+ if (!suspend) {
+ this.unsuspendibleIds.add(id)
+ } else {
+ this.unsuspendibleIds.delete(id)
+ }
+ },
},
}
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index 8d4734083..8a46722ad 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -97,7 +97,7 @@
:expandable="!isExpanded"
:focused="isFocused(status.id)"
- :highlight="getHighlight()"
+ :highlight="maybeHighlight"
:inline-expanded="collapsable && isExpanded"
:show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
:in-profile="inProfile"
@@ -105,21 +105,11 @@
:profile-user-id="profileUserId"
:simple-tree="treeViewIsSimple"
:show-other-replies-as-button="showOtherRepliesButtonInsideStatus"
- :dive="() => diveIntoStatus(status.id)"
-
- :controlled-showing-tall="statusContentProperties[status.id].showingTall"
- :controlled-toggle-showing-tall="() => toggleStatusContentProperty(status.id, 'showingTall')"
- :controlled-expanding-subject="statusContentProperties[status.id].expandingSubject"
- :controlled-toggle-expanding-subject="() => toggleStatusContentProperty(status.id, 'expandingSubject')"
- :controlled-showing-long-subject="statusContentProperties[status.id].showingLongSubject"
- :controlled-toggle-showing-long-subject="() => toggleStatusContentProperty(status.id, 'showingLongSubject')"
- :controlled-replying="statusContentProperties[status.id].replying"
- :controlled-toggle-replying="() => toggleStatusContentProperty(status.id, 'replying')"
- :controlled-media-playing="statusContentProperties[status.id].mediaPlaying"
- :controlled-set-media-playing="(newVal) => toggleStatusContentProperty(status.id, 'mediaPlaying', newVal)"
+ can-dive
@goto="setHighlight"
- @toggle-expanded="toggleExpanded"
+ @dive="() => diveIntoStatus(status.id)"
+ @suspendable-state-change="onStatusSuspendStateChange"
/>
-
diff --git a/src/components/error_modal/error_modal.js b/src/components/error_modal/error_modal.js
index aaa9f99ec..560de3a53 100644
--- a/src/components/error_modal/error_modal.js
+++ b/src/components/error_modal/error_modal.js
@@ -30,7 +30,7 @@ const ErrorModal = {
type: Error,
},
},
- emits: ['clear', 'recover']
+ emits: ['clear', 'recover'],
}
export default ErrorModal
diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js
index 252a32bf6..c244a8cb3 100644
--- a/src/components/gallery/gallery.js
+++ b/src/components/gallery/gallery.js
@@ -27,8 +27,10 @@ const Gallery = {
return {
sizes: {},
hidingLong: true,
+ playingMedia: new Set(),
}
},
+ emits: ['play', 'pause'],
components: { Attachment },
computed: {
rows() {
@@ -115,11 +117,21 @@ const Gallery = {
return this.attachmentsDimensionalScore > 1
}
},
+ hasPlayingMedia() {
+ return this.playingMedia.size > 0
+ },
},
methods: {
onNaturalSizeLoad({ id, width, height }) {
set(this.sizes, id, { width, height })
},
+ onMediaStateChange(playing, id) {
+ if (playing) {
+ this.playingMedia.add(id)
+ } else {
+ this.playingMedia.delete(id)
+ }
+ },
rowStyle(row) {
if (row.audio) {
return { 'padding-bottom': '25%' } // fixed reduced height for audio
@@ -146,6 +158,15 @@ const Gallery = {
useMediaViewerStore().setMedia(this.attachments)
},
},
+ watch: {
+ hasPlayingMedia(newValue) {
+ if (newValue) {
+ this.$emit('play')
+ } else {
+ this.$emit('pause')
+ }
+ },
+ },
}
export default Gallery
diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue
index 11637f3c2..a32d4feb7 100644
--- a/src/components/gallery/gallery.vue
+++ b/src/components/gallery/gallery.vue
@@ -34,6 +34,8 @@
:style="itemStyle(attachment.id, row.items)"
@set-media="onMedia"
@natural-size-load="onNaturalSizeLoad"
+ @play="() => onMediaStateChange(true, attachment.id)"
+ @pause="() => onMediaStateChange(false, attachment.id)"
/>
diff --git a/src/components/global_error/global_error.js b/src/components/global_error/global_error.js
index 0f826f4c4..cd4593521 100644
--- a/src/components/global_error/global_error.js
+++ b/src/components/global_error/global_error.js
@@ -1,9 +1,9 @@
+import { mapActions, mapState } from 'pinia'
+
import ErrorModal from 'src/components/error_modal/error_modal.vue'
import { useInterfaceStore } from 'src/stores/interface.js'
-import { mapState, mapActions } from 'pinia'
-
const GlobalError = {
components: {
ErrorModal,
@@ -24,7 +24,11 @@ const GlobalError = {
details() {
if (this.globalError == null) return null
if (this.globalError.error != null) {
- return this.globalError.error.toString() + '\n\n' + this.globalError.error.stack
+ return (
+ this.globalError.error.toString() +
+ '\n\n' +
+ this.globalError.error.stack
+ )
} else {
return this.globalError.details
}
diff --git a/src/components/lists_edit/lists_edit.vue b/src/components/lists_edit/lists_edit.vue
index 713a40673..25b818deb 100644
--- a/src/components/lists_edit/lists_edit.vue
+++ b/src/components/lists_edit/lists_edit.vue
@@ -50,7 +50,7 @@
{
- this.$emit('can-close')
+ this.$emit('close-accepted')
})
},
discardAndCloseDraft() {
this.abandonDraft().then(() => {
- this.$emit('can-close')
+ this.$emit('close-accepted')
})
},
addBeforeUnloadListener() {
diff --git a/src/components/search/search.vue b/src/components/search/search.vue
index 4302f93f9..16418d389 100644
--- a/src/components/search/search.vue
+++ b/src/components/search/search.vue
@@ -63,7 +63,6 @@
:compact="false"
class="search-result"
:statusoid="status"
- :no-heading="false"
/>
@@ -555,9 +549,9 @@
:replied-user="status.user"
:copy-message-scope="status.visibility"
:subject="replySubject"
- @posted="doToggleReplying"
- @draft-done="doToggleReplying"
- @can-close="doToggleReplying"
+ @posted="closeReplyForm"
+ @draft-done="closeReplyForm"
+ @close-accepted="closeReplyForm"
/>
diff --git a/src/components/status_body/status_body.js b/src/components/status_body/status_body.js
index cdd43a024..2da6da555 100644
--- a/src/components/status_body/status_body.js
+++ b/src/components/status_body/status_body.js
@@ -15,32 +15,47 @@ library.add(faFile, faMusic, faImage, faLink, faPollH)
const StatusBody = {
name: 'StatusBody',
- props: [
- 'compact',
- 'collapse', // replaces newlines with spaces
- 'status',
- 'focused',
- 'noHeading',
- 'fullContent',
- 'singleLine',
- 'showingTall',
- 'expandingSubject',
- 'showingLongSubject',
- 'toggleShowingTall',
- 'toggleExpandingSubject',
- 'toggleShowingLongSubject',
- ],
+ props: {
+ status: {
+ // Main thing
+ type: Object,
+ required: true,
+ },
+ compact: {
+ // Resizes emoji and minimizes vertical space used
+ // Primarily used for showing status in react notifications
+ type: Boolean,
+ default: false,
+ },
+ collapse: {
+ // replaces newlines with spaces
+ type: Boolean,
+ default: false,
+ },
+ singleLine: {
+ // Show entire thing (subject and content) in a single line
+ // Primarily used in chats
+ type: Boolean,
+ default: false,
+ },
+ inConversation: {
+ // Is status rendered within open conversation?
+ // Used to automatically expand subjects (if collapsed)
+ type: Boolean,
+ default: false,
+ }
+ },
data() {
return {
postLength: this.status.text.length,
parseReadyDone: false,
+ showingTall: false,
+ showingLongSubject: false,
+ expandingSubject: null,
}
},
emits: ['parseReady'],
computed: {
- localCollapseSubjectDefault() {
- return this.mergedConfig.collapseMessageWithSubject
- },
allowNonSquareEmoji() {
return this.mergedConfig.nonSquareEmoji
},
@@ -51,32 +66,31 @@ const StatusBody = {
//
// Using max-height + overflow: auto for status components resulted in false positives
// very often with japanese characters, and it was very annoying.
- tallStatus() {
+ hasLongSubject() {
+ return this.status.summary.length > 240
+ },
+ hasSubject() {
+ return !!this.status.summary
+ },
+ // 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.inConversation && this.hasSubject && this.mergedConfig.collapseMessageWithSubject
+ },
+ mightHideBecauseTall() {
if (this.singleLine || this.compact) return false
const lengthScore =
this.status.raw_html.split(/ 20
},
- longSubject() {
- return this.status.summary.length > 240
- },
- // 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.localCollapseSubjectDefault
- },
- mightHideBecauseTall() {
- return (
- this.tallStatus &&
- !(this.status.summary && this.localCollapseSubjectDefault)
- )
- },
hideSubjectStatus() {
return this.mightHideBecauseSubject && !this.expandingSubject
},
hideTallStatus() {
return this.mightHideBecauseTall && !this.showingTall
},
- shouldShowToggle() {
+ shouldShowExpandToggle() {
return this.mightHideBecauseSubject || this.mightHideBecauseTall
},
toggleButtonClasses() {
@@ -97,6 +111,11 @@ const StatusBody = {
: this.$t('general.show_more')
}
},
+ shouldHide() {
+ return (
+ !this.showingMore && this.mightHideBecauseSubject && this.hasSubject
+ )
+ },
showingMore() {
return (
(this.mightHideBecauseTall && this.showingTall) ||
@@ -147,9 +166,9 @@ const StatusBody = {
},
toggleShowMore() {
if (this.mightHideBecauseTall) {
- this.toggleShowingTall()
+ this.showingTall = !this.showingTall
} else if (this.mightHideBecauseSubject) {
- this.toggleExpandingSubject()
+ this.expandingSubject = !this.expandingSubject
}
},
generateTagLink(tag) {
diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss
index ff35bde58..9954663e7 100644
--- a/src/components/status_body/status_body.scss
+++ b/src/components/status_body/status_body.scss
@@ -53,6 +53,7 @@
}
.text-wrapper {
+ position: relative;
text-overflow: ellipsis;
overflow-wrap: break-word;
overflow: hidden;
@@ -60,10 +61,12 @@
flex-flow: column nowrap;
&.-tall-status {
- position: relative;
- height: 16em;
z-index: 1;
+ &:not(.-hidden) {
+ height: 16em;
+ }
+
.media-body {
min-height: 0;
mask:
@@ -98,6 +101,7 @@
text-wrap: pretty;
width: 100%;
text-align: center;
+ margin: 0.1em 0;
}
.status-unhider {
@@ -107,17 +111,17 @@
padding-bottom: 1em;
}
- .tall-status-hider {
- position: absolute;
- height: 5em;
- margin-top: 10em;
- line-height: 8em;
- z-index: 2;
- }
-
.tall-subject-hider {
// position: absolute;
padding-bottom: 0.5em;
+
+ &:not(.cw-status-hider) {
+ position: absolute;
+ margin-top: 10em;
+ height: 5em;
+ line-height: 8em;
+ z-index: 2;
+ }
}
& .status-unhider,
diff --git a/src/components/status_body/status_body.vue b/src/components/status_body/status_body.vue
index f724f2413..b611d69b8 100644
--- a/src/components/status_body/status_body.vue
+++ b/src/components/status_body/status_body.vue
@@ -5,9 +5,9 @@
>
{{ $t("status.hide_full_subject") }}
@@ -34,10 +34,10 @@
diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js
index 2186498ef..1cd3fc7b4 100644
--- a/src/components/status_content/status_content.js
+++ b/src/components/status_content/status_content.js
@@ -22,69 +22,40 @@ import {
library.add(faCircleNotch, faFile, faMusic, faImage, faLink, 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.$data[toggle] !== undefined ||
- this.$props[toggle] !== undefined) &&
- 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 StatusContent = {
name: 'StatusContent',
- props: [
- 'status',
- 'compact',
- 'collapse',
- 'focused',
- 'noHeading',
- 'fullContent',
- 'singleLine',
- 'controlledShowingTall',
- 'controlledExpandingSubject',
- 'controlledToggleShowingTall',
- 'controlledToggleExpandingSubject',
- 'controlledShowingLongSubject',
- 'controlledToggleShowingLongSubject',
- ],
- emits: ['parseReady', 'mediaplay', 'mediapause'],
- 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:
- !useMergedConfigStore().mergedConfig.collapseMessageWithSubject,
- }
+ props: {
+ status: {
+ // Main thing
+ type: Object,
+ required: true,
+ },
+ compact: {
+ // Resizes emoji and minimizes vertical space used
+ // Primarily used for showing status in react notifications
+ type: Boolean,
+ default: false,
+ },
+ collapse: {
+ // replaces newlines with spaces
+ type: Boolean,
+ default: false,
+ },
+ singleLine: {
+ // Show entire thing (subject and content) in a single line
+ // Primarily used in chats
+ type: Boolean,
+ default: false,
+ },
+ inConversation: {
+ // Whether status content is being shown in an (open) conversation
+ // Used to control whether to display attachments or not
+ type: Boolean,
+ default: false,
+ },
},
+ emits: ['parseReady', 'mediaplay', 'mediapause'],
computed: {
- ...controlledOrUncontrolledGetters([
- 'showingTall',
- 'expandingSubject',
- 'showingLongSubject',
- ]),
statusCard() {
if (!this.status.card) return null
return this.status.card.url === this.status.quote_url
@@ -93,22 +64,20 @@ const StatusContent = {
},
hideAttachments() {
return (
- (this.mergedConfig.hideAttachments && !this.inConversation) ||
- (this.mergedConfig.hideAttachmentsInConv && this.inConversation)
+ !this.fullContent &&
+ ((this.mergedConfig.hideAttachments && !this.inConversation) ||
+ (this.mergedConfig.hideAttachmentsInConv && this.inConversation))
)
},
nsfwClickthrough() {
if (!this.status.nsfw) {
return false
}
- if (this.status.summary && this.localCollapseSubjectDefault) {
+ if (this.status.summary && this.mergedConfig.collapseMessageWithSubject) {
return false
}
return true
},
- localCollapseSubjectDefault() {
- return this.mergedConfig.collapseMessageWithSubject
- },
attachmentSize() {
if (this.compact) {
return 'small'
@@ -137,15 +106,6 @@ const StatusContent = {
StatusBody,
},
methods: {
- toggleShowingTall() {
- controlledOrUncontrolledToggle(this, 'showingTall')
- },
- toggleExpandingSubject() {
- controlledOrUncontrolledToggle(this, 'expandingSubject')
- },
- toggleShowingLongSubject() {
- controlledOrUncontrolledToggle(this, 'showingLongSubject')
- },
setMedia() {
const attachments =
this.attachmentSize === 'hide'
diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue
index 6dc80b8a5..6b46bd768 100644
--- a/src/components/status_content/status_content.vue
+++ b/src/components/status_content/status_content.vue
@@ -8,13 +8,8 @@
:status="status"
:compact="compact"
:single-line="singleLine"
- :showing-tall="showingTall"
- :expanding-subject="expandingSubject"
- :showing-long-subject="showingLongSubject"
- :toggle-showing-tall="toggleShowingTall"
- :toggle-expanding-subject="toggleExpandingSubject"
- :toggle-showing-long-subject="toggleShowingLongSubject"
:collapse="collapse"
+ :in-conversation="inConversation"
@parse-ready="$emit('parseReady', $event)"
>
@@ -42,12 +37,12 @@
:attachments="status.attachments"
:limit="compact ? 1 : 0"
:size="attachmentSize"
- @play="$emit('mediaplay', attachment.id)"
- @pause="$emit('mediapause', attachment.id)"
+ @play="$emit('mediaplay')"
+ @pause="$emit('mediapause')"
/>
s.suspendable)
- )
- }
- return selfSuspendable
- },
reverseLookupTable() {
return this.conversation.reduce(
(table, status, index) => {
@@ -69,29 +53,11 @@ const ThreadTree = {
threadShowing() {
return this.threadDisplayStatus[this.status.id] === 'showing'
},
- currentProp() {
- return this.statusContentProperties[this.status.id]
- },
},
methods: {
statusById(id) {
return this.conversation[this.reverseLookupTable[id]]
},
- collapseThread() {
- /* no-op */
- },
- showThread() {
- /* no-op */
- },
- showAllSubthreads() {
- /* no-op */
- },
- toggleCurrentProp(name) {
- this.toggleStatusContentProperty(this.status.id, name)
- },
- setCurrentProp(name) {
- 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 4ffe463fe..72f8512e5 100644
--- a/src/components/thread_tree/thread_tree.vue
+++ b/src/components/thread_tree/thread_tree.vue
@@ -1,44 +1,34 @@
- $emit('suspendableStateChange', e)"
/>
- $emit('goto', e)"
+ @dive="(e) => $emit('dive', e)"
+ @suspendable-state-change="e => $emit('suspendableStateChange', e)"
/>
-
diff --git a/src/stores/interface.js b/src/stores/interface.js
index e76a23cc2..6e82dc5f9 100644
--- a/src/stores/interface.js
+++ b/src/stores/interface.js
@@ -199,7 +199,7 @@ export const useInterfaceStore = defineStore('interface', {
console.log(this.globalError)
},
clearGlobalError() {
- this.globalError = null;
+ this.globalError = null
},
pushGlobalNotice({
messageKey,
diff --git a/src/sw.js b/src/sw.js
index f1c1b75d2..b7629b5f2 100644
--- a/src/sw.js
+++ b/src/sw.js
@@ -1,5 +1,8 @@
/* eslint-env serviceworker */
+// biome-ignore: side effect import of assets list
+import 'virtual:pleroma-fe/service_worker_env'
+
import { createI18n } from 'vue-i18n'
import { storage } from 'src/lib/storage.js'