, State> {
if (!config) {
return null;
}
-
return (
{
+ let returnValue = 'N/A';
+ if (typeof value === 'number') {
+ returnValue = prettyBytes(value);
+ } else if (typeof value === 'string') {
+ const parsed = parseInt(value, 10);
+
+ if (typeof parsed === 'number') {
+ returnValue = prettyBytes(parsed);
+ }
+ }
+ if (excludeUnits && totalUnits === returnValue.split(' ')[1]) {
+ return returnValue.split(' ')[0];
+ }
+ return returnValue;
+};
+
+const getFileSizeOrBytesProgress = (item: DownloadedItem) => {
+ const totalMegabytes = prettyBytesConverter(item.totalBytes);
+ if (item.state === 'progressing') {
+ return `${prettyBytesConverter(item.receivedBytes, true, totalMegabytes.split(' ')[1])}/${totalMegabytes}`;
+ }
+ return `${totalMegabytes}`;
+};
+
+const getDownloadingFileStatus = (item: DownloadedItem) => {
+ switch (item.state) {
+ case 'completed':
+ return 'Downloaded';
+ case 'deleted':
+ return 'Deleted';
+ default:
+ return 'Cancelled';
+ }
+};
+
+const getIconClassName = (file: DownloadedItem) => {
+ if (file.type === 'update') {
+ return 'mattermost';
+ }
+
+ if (!file.mimeType) {
+ return 'generic';
+ }
+
+ // Find thumbnail icon form MIME type
+ const fileType = file.mimeType.toLowerCase() as keyof typeof Constants.ICON_NAME_FROM_MIME_TYPE;
+ if (fileType in Constants.ICON_NAME_FROM_MIME_TYPE) {
+ return Constants.ICON_NAME_FROM_MIME_TYPE[fileType];
+ }
+
+ // Fallback to file extension
+ const extension = file.location.toLowerCase().split('.').pop() as keyof typeof Constants.ICON_NAME_FROM_EXTENSION;
+ if (extension && (extension in Constants.ICON_NAME_FROM_EXTENSION)) {
+ return Constants.ICON_NAME_FROM_EXTENSION[extension];
+ }
+
+ // use generic icon
+ return 'generic';
+};
+
+const isImageFile = (file: DownloadedItem): boolean => {
+ return file.mimeType?.toLowerCase().startsWith('image/') ?? false;
+};
+
+const prettyETA = (ms = 0, intl: IntlShape) => {
+ let eta;
+
+ if (ms < Constants.MINUTE_MS) {
+ eta = `${Math.round(ms / Constants.SECOND_MS)} ${intl.formatMessage({id: 'renderer.time.sec', defaultMessage: 'sec'})}`;
+ } else if (ms < Constants.HOUR_MS) {
+ eta = `${Math.round(ms / Constants.MINUTE_MS)} ${intl.formatMessage({id: 'renderer.time.mins', defaultMessage: 'mins'})}`;
+ } else {
+ eta = `${Math.round(ms / Constants.HOUR_MS)} ${intl.formatMessage({id: 'renderer.time.hours', defaultMessage: 'hours'})}`;
+ }
+ return `${eta} ${intl.formatMessage({id: 'renderer.downloadsDropdown.remaining', defaultMessage: 'remaining'})}`;
+};
+
+export {
+ getDownloadingFileStatus,
+ getFileSizeOrBytesProgress,
+ getIconClassName,
+ isImageFile,
+ prettyETA,
+};
diff --git a/src/types/downloads.ts b/src/types/downloads.ts
new file mode 100644
index 00000000..9598f189
--- /dev/null
+++ b/src/types/downloads.ts
@@ -0,0 +1,29 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {DownloadItemTypeEnum} from 'main/downloadsManager';
+
+export type DownloadItemUpdatedEventState = 'interrupted' | 'progressing';
+export type DownloadItemDoneEventState = 'completed' | 'cancelled' | 'interrupted';
+export type DownloadItemState = DownloadItemUpdatedEventState | DownloadItemDoneEventState | 'deleted' | 'available';
+
+export type DownloadedItem = {
+ type: DownloadItemTypeEnum;
+ filename: string;
+ state: DownloadItemState;
+ progress: number;
+ location: string;
+ mimeType: string | null;
+ addedAt: number;
+ receivedBytes: number;
+ totalBytes: number;
+}
+
+export type DownloadedItems = Record;
+
+export type CoordinatesToJsonType = Omit
+
+export type DownloadsMenuOpenEventPayload = {
+ item: DownloadedItem;
+ coordinates: CoordinatesToJsonType;
+}
diff --git a/tsconfig.json b/tsconfig.json
index 782bc48d..a42ed2ce 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -31,5 +31,4 @@
"storybook-static",
"coverage"
]
- }
-
\ No newline at end of file
+}
diff --git a/webpack.config.main.js b/webpack.config.main.js
index 3451c7d2..96627a3f 100644
--- a/webpack.config.main.js
+++ b/webpack.config.main.js
@@ -17,6 +17,8 @@ module.exports = merge(base, {
index: './src/main/app/index.ts',
mainWindow: './src/main/preload/mainWindow.js',
dropdown: './src/main/preload/dropdown.js',
+ downloadsDropdown: './src/main/preload/downloadsDropdown.js',
+ downloadsDropdownMenu: './src/main/preload/downloadsDropdownMenu.js',
preload: './src/main/preload/mattermost.js',
modalPreload: './src/main/preload/modalPreload.js',
loadingScreenPreload: './src/main/preload/loadingScreenPreload.js',
diff --git a/webpack.config.renderer.js b/webpack.config.renderer.js
index a20a02fa..3279a9c6 100644
--- a/webpack.config.renderer.js
+++ b/webpack.config.renderer.js
@@ -21,6 +21,8 @@ module.exports = merge(base, {
index: './src/renderer/index.tsx',
settings: './src/renderer/settings.tsx',
dropdown: './src/renderer/dropdown.tsx',
+ downloadsDropdownMenu: './src/renderer/downloadsDropdownMenu.tsx',
+ downloadsDropdown: './src/renderer/downloadsDropdown.tsx',
urlView: './src/renderer/modals/urlView/urlView.tsx',
newServer: './src/renderer/modals/newServer/newServer.tsx',
editServer: './src/renderer/modals/editServer/editServer.tsx',
@@ -55,6 +57,18 @@ module.exports = merge(base, {
chunks: ['dropdown'],
filename: 'dropdown.html',
}),
+ new HtmlWebpackPlugin({
+ title: 'Mattermost Desktop Downloads',
+ template: 'src/renderer/index.html',
+ chunks: ['downloadsDropdown'],
+ filename: 'downloadsDropdown.html',
+ }),
+ new HtmlWebpackPlugin({
+ title: 'Mattermost Desktop Downloads',
+ template: 'src/renderer/index.html',
+ chunks: ['downloadsDropdownMenu'],
+ filename: 'downloadsDropdownMenu.html',
+ }),
new HtmlWebpackPlugin({
title: 'Mattermost Desktop Settings',
template: 'src/renderer/index.html',