Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -172,26 +172,6 @@
</template>
{{ t('spreed', 'Mark as unread') }}
</NcActionButton>
<template v-if="isFileShare">
<NcActionSeparator />
<NcActionLink :href="messageFile.link">
<template #icon>
<IconFileOutline :size="20" />
</template>
{{ isSharedFolder ? t('spreed', 'Go to folder') : t('spreed', 'Go to file') }}
</NcActionLink>
<NcActionLink
v-if="!hideDownloadOption"
:href="linkToFileDownload"
:download="downloadFileName"
closeAfterClick>
<template #icon>
<NcIconSvgWrapper :svg="IconFileDownload" :size="20" />
</template>
<!-- TRANSLATORS: to download a folder/file -->
{{ isSharedFolder ? t('spreed', 'Download folder') : t('spreed', 'Download file') }}
</NcActionLink>
</template>
<template v-if="isThreadStarterMessage">
<NcActionSeparator />
<NcActionButton
Expand Down Expand Up @@ -415,7 +395,6 @@
</template>

<script>
import { getCurrentUser } from '@nextcloud/auth'
import { showError, showSuccess } from '@nextcloud/dialogs'
import { t } from '@nextcloud/l10n'
import { spawnDialog } from '@nextcloud/vue/functions/dialog'
Expand All @@ -425,13 +404,11 @@ import { toRefs } from 'vue'
import NcActionButton from '@nextcloud/vue/components/NcActionButton'
import NcActionCaption from '@nextcloud/vue/components/NcActionCaption'
import NcActionInput from '@nextcloud/vue/components/NcActionInput'
import NcActionLink from '@nextcloud/vue/components/NcActionLink'
import NcActions from '@nextcloud/vue/components/NcActions'
import NcActionSeparator from '@nextcloud/vue/components/NcActionSeparator'
import NcActionText from '@nextcloud/vue/components/NcActionText'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcEmojiPicker from '@nextcloud/vue/components/NcEmojiPicker'
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
import IconAccountOutline from 'vue-material-design-icons/AccountOutline.vue'
import IconAlarm from 'vue-material-design-icons/Alarm.vue'
import IconArrowLeft from 'vue-material-design-icons/ArrowLeft.vue'
Expand All @@ -447,7 +424,6 @@ import IconCloseCircleOutline from 'vue-material-design-icons/CloseCircleOutline
import IconContentCopy from 'vue-material-design-icons/ContentCopy.vue'
import IconDotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'
import IconEmoticonOutline from 'vue-material-design-icons/EmoticonOutline.vue'
import IconFileOutline from 'vue-material-design-icons/FileOutline.vue'
import IconForumOutline from 'vue-material-design-icons/ForumOutline.vue'
import IconMessageBadgeOutline from 'vue-material-design-icons/MessageBadgeOutline.vue'
import IconNoteEditOutline from 'vue-material-design-icons/NoteEditOutline.vue'
Expand All @@ -459,7 +435,6 @@ import IconPlus from 'vue-material-design-icons/Plus.vue'
import IconTranslate from 'vue-material-design-icons/Translate.vue'
import IconTrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'
import MessageTranslateDialog from './MessageTranslateDialog.vue'
import IconFileDownload from '../../../../../../img/material-icons/file-download.svg?raw'
import { useGetThreadId } from '../../../../../composables/useGetThreadId.ts'
import { useMessageInfo } from '../../../../../composables/useMessageInfo.ts'
import { ATTENDEE, CONVERSATION, MESSAGE, PARTICIPANT, SHARED_ITEM } from '../../../../../constants.ts'
Expand All @@ -471,11 +446,9 @@ import { useIntegrationsStore } from '../../../../../stores/integrations.js'
import { useReactionsStore } from '../../../../../stores/reactions.js'
import { useSharedItemsStore } from '../../../../../stores/sharedItems.ts'
import { isClassifiedConversation } from '../../../../../utils/conversation.ts'
import { generatePublicShareDownloadUrl, generateUserFileUrl, generateUserFolderUrl } from '../../../../../utils/davUtils.ts'
import { convertToUnix, formatDateTime, ONE_DAY_IN_MS } from '../../../../../utils/formattedTime.ts'
import { getCustomDateOptions } from '../../../../../utils/getCustomDateOptions.ts'
import { copyConversationLinkToClipboard } from '../../../../../utils/handleUrl.ts'
import { getFileKeys } from '../../../../../utils/message.ts'
import { parseMentions } from '../../../../../utils/textParse.ts'

const PIN_DURATION_OPTIONS = [
Expand All @@ -492,13 +465,11 @@ export default {
NcActionButton,
NcActionCaption,
NcActionInput,
NcActionLink,
NcActionSeparator,
NcActionText,
NcActions,
NcButton,
NcEmojiPicker,
NcIconSvgWrapper,
// Icons
IconAccountOutline,
IconAlarm,
Expand All @@ -515,7 +486,6 @@ export default {
IconTrashCanOutline,
IconEmoticonOutline,
IconMessageBadgeOutline,
IconFileOutline,
IconForumOutline,
IconNoteEditOutline,
IconOpenInNew,
Expand Down Expand Up @@ -603,7 +573,6 @@ export default {
isCurrentUserOwnMessage,
isFileShare,
isFileShareWithoutCaption,
hideDownloadOption,
isConversationReadOnly,
isConversationModifiable,
} = useMessageInfo(message)
Expand All @@ -615,15 +584,13 @@ export default {
?? getTalkConfig(message.value.token, 'chat', 'translations')?.length > 0

return {
IconFileDownload,
messageActions,
supportReminders,
reactionsStore,
isEditable,
isCurrentUserOwnMessage,
isFileShare,
isFileShareWithoutCaption,
hideDownloadOption,
isDeleteable,
isConversationReadOnly,
isConversationModifiable,
Expand Down Expand Up @@ -671,27 +638,6 @@ export default {
&& !this.isClassified
},

messageFile() {
const firstFileKey = getFileKeys(this.message).at(0)
return this.message.messageParameters[firstFileKey]
},

isSharedFolder() {
return this.messageFile?.mimetype === 'httpd/unix-directory'
},

downloadFileName() {
return this.messageFile.name + (this.isSharedFolder ? '.zip' : '')
},

linkToFileDownload() {
return getCurrentUser()
? (this.isSharedFolder
? generateUserFolderUrl(this.messageFile.path)
: generateUserFileUrl(this.messageFile.path))
: generatePublicShareDownloadUrl(this.messageFile.link)
},

isCurrentGuest() {
return this.actorStore.isActorGuest
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ import { hasTalkFeature } from '../../../../services/CapabilitiesManager.ts'
import { EventBus } from '../../../../services/EventBus.ts'
import { useActorStore } from '../../../../stores/actor.ts'
import { useChatExtrasStore } from '../../../../stores/chatExtras.ts'
import { isFilePreviewParameter, isTemporaryId } from '../../../../utils/message.ts'
import { isFilePreviewParameter, isTemporaryId, isTemporaryMessage } from '../../../../utils/message.ts'

const LocationCard = defineAsyncComponent(() => import('./MessagePart/LocationCard.vue'))

Expand Down Expand Up @@ -187,7 +187,7 @@ export default {
},

isTemporary() {
return !this.isScheduledMessage && isTemporaryId(this.message.id)
return !this.isScheduledMessage && isTemporaryMessage(this.message)
},

isDeletedMessage() {
Expand Down Expand Up @@ -490,10 +490,6 @@ export default {
background-color: var(--color-primary-element-light-hover);
border: 1px solid var(--color-border-dark);
}

:deep(.file-preview__progress) {
inset-inline-start: calc((var(--progress-bar-height) + var(--default-grid-baseline) * 3) * -1)
}
}

&.incoming {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
@load="onLoad"
@error="onError">
<template v-if="!isLoading || fallbackLocalUrl">
<span v-if="moreCount > 0" class="more-overlay">+{{ moreCount }}</span>
<span v-if="isPlayable && !smallPreview" class="play-video-button">
<IconPlayCircleOutline
:size="48"
Expand All @@ -60,8 +61,9 @@
<NcProgressBar
v-if="showUploadProgress"
class="file-preview__progress"
:class="{ 'file-preview__progress--pending': !uploadProgress }"
type="circular"
:value="uploadProgress" />
:value="uploadProgress || 30" />
<span
v-if="fileSizeLabel"
class="file-preview__size"
Expand Down Expand Up @@ -94,7 +96,13 @@
</template>
</NcButton>
<div v-if="shouldShowFileDetail" class="name-container">
{{ fileDetail }}
<template v-if="rowLayout">
<span class="name-container__name">{{ fileDetail }}</span>
<span v-if="fileMetaLabel" class="name-container__meta">{{ fileMetaLabel }}</span>
</template>
<template v-else>
{{ fileDetail }}
</template>
</div>
</component>
</template>
Expand Down Expand Up @@ -203,6 +211,24 @@ export default {
type: String,
default: '',
},

/**
* Sibling image files from the same upload group (message), in upload order.
* Used to scope the Viewer's swipeable list to this group instead of all conversation media.
*/
mediaGroup: {
type: Array,
default: undefined,
},

/**
* When set, this tile shows a "+N" overlay instead of its own preview,
* for the collapsed tail of an image row.
*/
moreCount: {
type: Number,
default: 0,
},
},

emits: ['removeFile'],
Expand Down Expand Up @@ -250,6 +276,22 @@ export default {
return this.file.name
},

// Extension shown on the second, muted line of the row layout ('PDF', 'DOCX', …)
fileExtension() {
const dotIndex = this.file.name.lastIndexOf('.')
return dotIndex > 0 ? this.file.name.slice(dotIndex + 1).toUpperCase() : ''
},

// File size shown on the second, muted line of the row layout
fileRowSize() {
const size = parseInt(this.file.size, 10)
return size ? formatFileSize(size, true) : ''
},

fileMetaLabel() {
return [this.fileExtension, this.fileRowSize].filter(Boolean).join(' · ')
},

fileSizeLabel() {
if (!this.isUploadEditor || !this.uploadFile) {
return ''
Expand Down Expand Up @@ -305,7 +347,7 @@ export default {

previewImageClass() {
let classes = ''
if (this.smallPreview) {
if (this.smallPreview || this.rowLayout) {
classes += 'preview-small '
} else if (this.mediumPreview) {
classes += 'preview-medium '
Expand Down Expand Up @@ -334,6 +376,11 @@ export default {
return {}
}

// Row layout always shows a small fixed-size icon, never a medium/full preview
if (this.rowLayout) {
return { width: '24px', height: '24px' }
}

// Fallback for loading mimeicons (preview for audio files is not provided)
if (this.file['preview-available'] !== 'yes' || this.file.mimetype.startsWith('audio/') || this.failed) {
return {
Expand Down Expand Up @@ -596,7 +643,7 @@ export default {
event.stopPropagation()
event.preventDefault()

if (this.itemType === SHARED_ITEM.TYPES.MEDIA) {
if (this.itemType === SHARED_ITEM.TYPES.MEDIA && this.isSharedItems) {
const getRevertedList = (items) => Object.values(items).reverse()
.map((item) => item.messageParameters.file)

Expand All @@ -609,6 +656,9 @@ export default {
}

this.openViewer(this.internalAbsolutePath, list, this.file, loadMore)
} else if (this.itemType === SHARED_ITEM.TYPES.MEDIA && this.mediaGroup?.length) {
// Message context: only swipe through images from the same upload group (this message)
this.openViewer(this.internalAbsolutePath, this.mediaGroup, this.file)
} else {
this.openViewer(this.internalAbsolutePath, [this.file], this.file)
}
Expand Down Expand Up @@ -649,9 +699,21 @@ export default {

&__progress {
position: absolute;
top: 50%;
inset-inline-end: calc(var(--progress-bar-height) * -1);
transform: translateY(-50%);
inset-block-end: var(--default-grid-baseline);
inset-inline-end: var(--default-grid-baseline);
border-radius: 50%;
z-index: 1;

// Override NcProgressBar styles to fake indeterminate state and mimic NcLoadingIcon
:deep(circle:first-of-type) {
stroke: var(--color-loading-dark);
}
:deep(circle:last-of-type) {
stroke: var(--color-loading-light);
}
&--pending {
animation: rotate var(--animation-duration, 0.8s) linear infinite;
}
}

&__size {
Expand Down Expand Up @@ -711,6 +773,21 @@ export default {
max-height: 100%;
border-radius: var(--border-radius);

// "+X more" overlay for the collapsed tail of an image row (see FilePreviewsWrapper)
.more-overlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--border-radius);
background-color: rgba(0, 0, 0, 0.5);
color: #ffffff;
font-size: var(--font-size-large);
font-weight: bold;
z-index: 1;
}

// Classified: signal that the hidden preview is clickable (not failed)
.classified-preview-button {
position: absolute;
Expand Down Expand Up @@ -829,15 +906,31 @@ export default {
align-items: center;
border-radius: var(--border-radius);
padding: 2px 4px;
// Reserve space for the hover actions button (see FilePreviewActions), so it never
// overlaps the file name and the row doesn't shift width on hover.
padding-inline-end: var(--default-clickable-area);

.image-container {
flex-shrink: 0;
width: 24px;
}

.name-container {
display: flex;
flex-direction: column;
padding: 0 4px;
font-weight: normal;
font-size: var(--font-size-small);

&__name {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

&__meta {
color: var(--color-text-maxcontrast);
}
}
}

Expand Down
Loading