Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2429 - File attachment in chat should show the size #2435

Merged
merged 19 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions frontend/controller/actions/identity.js
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ export default (sbp('sbp/selectors/register', {

attachment.mimeType = attachmentBlob.type
attachment.name = `${fileNameWithoutExtension}.${extension}`
attachment.size = attachmentBlob.size
}
const response = await sbp('chelonia/fileUpload', attachmentBlob, {
type: attachment.mimeType,
Expand Down
29 changes: 16 additions & 13 deletions frontend/model/contracts/shared/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import {
objectOf, objectMaybeOf, arrayOf, unionOf, boolean,
object, string, stringMax, optional, number, mapOf, literalOf
object, string, stringMax, optional, number, mapOf, literalOf, numberRange
} from '~/frontend/model/contracts/misc/flowTyper.js'
import {
CHATROOM_TYPES,
Expand Down Expand Up @@ -64,18 +64,21 @@ export const messageType: any = objectMaybeOf({
type: unionOf(...Object.values(MESSAGE_NOTIFICATIONS).map(v => literalOf(v))),
params: mapOf(string, string) // { username }
}),
attachments: arrayOf(objectOf({
name: string,
mimeType: string,
dimension: optional(objectOf({
width: number,
height: number
})),
downloadData: objectOf({
manifestCid: string,
downloadParams: optional(object)
})
})),
attachments: optional(
arrayOf(objectOf({
name: string,
mimeType: string,
size: numberRange(1, Number.MAX_SAFE_INTEGER),
dimension: optional(objectOf({
width: number,
height: number
})),
downloadData: objectOf({
manifestCid: string,
downloadParams: optional(object)
})
}))
),
replyingMessage: objectOf({
hash: string, // scroll to the original message and highlight
text: string // display text(if too long, truncate)
Expand Down
1 change: 1 addition & 0 deletions frontend/views/containers/chatroom/SendArea.vue
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ export default ({
url: fileUrl,
name: file.name,
mimeType: file.type || '',
size: fileSize,
downloadData: null // NOTE: we can tell if the attachment has been uploaded by seeing if this field is non-null.
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@

.c-non-image-file-info
.c-file-name.has-ellipsis {{ entry.name }}
.c-file-ext {{ fileExt(entry) }}
.c-file-ext-and-size
.c-file-ext {{ fileExt(entry) }}
.c-file-size(v-if='entry.size') {{ fileSizeDisplay(entry) }}

.c-preview-img(v-else)
img(
Expand All @@ -36,7 +38,7 @@
.c-attachment-actions
tooltip(
direction='top'
:text='L("Download")'
:text='getDownloadTooltipText(entry)'
)
button.is-icon-small(
:aria-label='L("Download")'
Expand Down Expand Up @@ -91,9 +93,10 @@
import sbp from '@sbp/sbp'
import Tooltip from '@components/Tooltip.vue'
import { MESSAGE_VARIANTS } from '@model/contracts/shared/constants.js'
import { getFileExtension, getFileType } from '@view-utils/filters.js'
import { getFileExtension, getFileType, formatBytesDecimal } from '@view-utils/filters.js'
import { Secret } from '~/shared/domains/chelonia/Secret.js'
import { OPEN_MODAL } from '@utils/events.js'
import { L } from '@common/common.js'
import { randomHexString } from '@model/contracts/shared/giLodash.js'

export default {
Expand Down Expand Up @@ -155,6 +158,14 @@ export default {
fileExt ({ name }) {
return getFileExtension(name, true)
},
fileSizeDisplay ({ size }) {
return size ? formatBytesDecimal(size) : ''
},
getDownloadTooltipText ({ size }) {
return this.shouldPreviewImages
? `${L('Download ({size})', { size: formatBytesDecimal(size) })}`
: L('Download')
},
fileType ({ mimeType }) {
return getFileType(mimeType)
},
Expand Down Expand Up @@ -276,8 +287,22 @@ export default {
&.is-for-download {
padding: 0;

.c-preview-non-image .c-non-image-file-info {
width: calc(100% - 4rem);
.c-preview-non-image {
.c-non-image-file-info {
width: calc(100% - 4rem);
}

.c-file-ext-and-size {
display: flex;
align-items: flex-end;
flex-direction: row;
column-gap: 0.325rem;
}

.c-file-size {
color: $text_1;
font-size: 0.8em;
}
}

.c-attachment-actions-wrapper {
Expand Down Expand Up @@ -310,6 +335,8 @@ export default {
.is-download-item {
&:hover .c-attachment-actions-wrapper {
display: flex;
flex-direction: column;
align-items: flex-end;
}

.c-preview-non-image {
Expand Down
14 changes: 14 additions & 0 deletions frontend/views/utils/filters.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { L } from '@common/common.js'

export const toPercent = (decimal: number): number => Math.floor(decimal * 100)

export const getFileExtension = (
Expand All @@ -15,6 +17,18 @@ export const getFileType = (
return mimeType.match('image/') ? 'image' : 'non-image'
}

export const formatBytesDecimal = (bytes: number, decimals: number = 2): string => {
if (bytes < 0 || !Number.isFinite(bytes)) return L('Invalid size')
else if (bytes === 0) return L('0 Bytes')

const k = 1024 // Decimal base
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const i = Math.floor(Math.log(bytes) / Math.log(k))

const formattedValue = parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))
return `${formattedValue} ${sizes[i]}`
}

/**
* this function filters `list` by `keyword`
* `list` should be an array of objects and strings
Expand Down