diff --git a/web/satellite/src/components/notifications/UploadNotification.vue b/web/satellite/src/components/notifications/UploadNotification.vue new file mode 100644 index 000000000..594047d94 --- /dev/null +++ b/web/satellite/src/components/notifications/UploadNotification.vue @@ -0,0 +1,81 @@ +// Copyright (C) 2023 Storj Labs, Inc. +// See LICENSE for copying information. + + + + + + diff --git a/web/satellite/src/components/objects/ObjectsArea.vue b/web/satellite/src/components/objects/ObjectsArea.vue index ed6082943..cbd459bd5 100644 --- a/web/satellite/src/components/objects/ObjectsArea.vue +++ b/web/satellite/src/components/objects/ObjectsArea.vue @@ -35,6 +35,5 @@ onMounted(async (): Promise => { diff --git a/web/satellite/src/components/objects/UploadFile.vue b/web/satellite/src/components/objects/UploadFile.vue index 5bfde372f..eeda0d104 100644 --- a/web/satellite/src/components/objects/UploadFile.vue +++ b/web/satellite/src/components/objects/UploadFile.vue @@ -262,6 +262,5 @@ watch(passphrase, async () => { diff --git a/web/satellite/src/store/modules/appStore.ts b/web/satellite/src/store/modules/appStore.ts index ec1ce432b..db336ae5e 100644 --- a/web/satellite/src/store/modules/appStore.ts +++ b/web/satellite/src/store/modules/appStore.ts @@ -30,6 +30,8 @@ class ViewsState { // for when the dashboard opens the pricing plan and the pricing plan navigates back repeatedly. public hasShownPricingPlan = false; public error: ErrorPageState = new ErrorPageState(); + public isLargeUploadNotificationShown = true; + public isLargeUploadWarningNotificationShown = false; } class ErrorPageState { @@ -132,6 +134,14 @@ export const useAppStore = defineStore('app', () => { state.viewsState.hasShownPricingPlan = value; } + function setLargeUploadWarningNotification(value: boolean): void { + state.viewsState.isLargeUploadWarningNotificationShown = value; + } + + function setLargeUploadNotification(value: boolean): void { + state.viewsState.isLargeUploadNotificationShown = value; + } + function closeDropdowns(): void { state.viewsState.activeDropdown = ''; } @@ -179,6 +189,8 @@ export const useAppStore = defineStore('app', () => { setPricingPlan, setManagePassphraseStep, setHasShownPricingPlan, + setLargeUploadWarningNotification, + setLargeUploadNotification, closeDropdowns, setErrorPage, removeErrorPage, diff --git a/web/satellite/src/store/modules/files.ts b/web/satellite/src/store/modules/files.ts index c418d84e7..bc446f13f 100644 --- a/web/satellite/src/store/modules/files.ts +++ b/web/satellite/src/store/modules/files.ts @@ -519,6 +519,12 @@ export const makeFilesModule = (): FilesModule => ({ Body: file, }; + // If file size exceeds 1 GB, show warning notification + if (file.size > (1024 * 1024 * 1024)) { + const appStore = useAppStore(); + appStore.setLargeUploadWarningNotification(true); + } + const upload = state.s3.upload( { ...params }, { partSize: 64 * 1024 * 1024 }, diff --git a/web/satellite/src/utils/localData.ts b/web/satellite/src/utils/localData.ts index 1d95221f8..ad9141cf9 100644 --- a/web/satellite/src/utils/localData.ts +++ b/web/satellite/src/utils/localData.ts @@ -11,6 +11,7 @@ export class LocalData { private static bucketGuideHidden = 'bucketGuideHidden'; private static serverSideEncryptionBannerHidden = 'serverSideEncryptionBannerHidden'; private static serverSideEncryptionModalHidden = 'serverSideEncryptionModalHidden'; + private static largeUploadNotificationDismissed = 'largeUploadNotificationDismissed'; private static sessionExpirationDate = 'sessionExpirationDate'; private static projectLimitBannerHidden = 'projectLimitBannerHidden'; @@ -84,6 +85,14 @@ export class LocalData { return value === 'true'; } + public static getLargeUploadNotificationDismissed(): boolean { + return Boolean(localStorage.getItem(LocalData.largeUploadNotificationDismissed)); + } + + public static setLargeUploadNotificationDismissed(): void { + localStorage.setItem(LocalData.largeUploadNotificationDismissed, 'true'); + } + public static getSessionExpirationDate(): Date | null { const data: string | null = localStorage.getItem(LocalData.sessionExpirationDate); if (data) { diff --git a/web/satellite/src/views/DashboardArea.vue b/web/satellite/src/views/DashboardArea.vue index 9ce6754ea..1d8c39e1b 100644 --- a/web/satellite/src/views/DashboardArea.vue +++ b/web/satellite/src/views/DashboardArea.vue @@ -62,6 +62,24 @@ + @@ -122,6 +140,7 @@ import { useAccessGrantsStore } from '@/store/modules/accessGrantsStore'; import { useBucketsStore } from '@/store/modules/bucketsStore'; import { useProjectsStore } from '@/store/modules/projectsStore'; +import UploadNotification from '@/components/notifications/UploadNotification.vue'; import NavigationArea from '@/components/navigation/NavigationArea.vue'; import InactivityModal from '@/components/modals/InactivityModal.vue'; import BetaSatBar from '@/components/infoBars/BetaSatBar.vue'; @@ -134,7 +153,11 @@ import UpgradeNotification from '@/components/notifications/UpgradeNotification. import ProjectLimitBanner from '@/components/notifications/ProjectLimitBanner.vue'; import BrandedLoader from '@/components/common/BrandedLoader.vue'; +import CloudIcon from '@/../static/images/notifications/cloudAlert.svg'; +import WarningIcon from '@/../static/images/notifications/circleWarning.svg'; + const bucketsStore = useBucketsStore(); + const appStore = useAppStore(); const agStore = useAccessGrantsStore(); const billingStore = useBillingStore(); @@ -350,6 +373,20 @@ const showMFARecoveryCodeBar = computed((): boolean => { return user.isMFAEnabled && user.mfaRecoveryCodeCount < recoveryCodeWarningThreshold; }); +/** + * Indicates whether the large upload notification should be shown. + */ +const isLargeUploadNotificationShown = computed((): boolean => { + return appStore.state.viewsState.isLargeUploadNotificationShown; +}); + +/** + * Indicates whether the large upload warning notification should be shown (file uploading exceeds 1GB). + */ +const isLargeUploadWarningNotificationShown = computed((): boolean => { + return appStore.state.viewsState.isLargeUploadWarningNotificationShown; +}); + /** * Indicates if current route is create project page. */ @@ -364,6 +401,28 @@ const isDashboardPage = computed((): boolean => { return router.currentRoute.name === RouteConfig.ProjectDashboard.name; }); +/** + * Indicates if current route is the bucketsView page. + */ +const isBucketsView = computed((): boolean => { + return router.currentRoute.name === RouteConfig.BucketsManagement.name; +}); + +/** + * Closes upload notification and persists state in local storage. + */ +function onNotificationCloseClick(): void { + appStore.setLargeUploadNotification(false); + LocalData.setLargeUploadNotificationDismissed(); +} + +/** + * Closes upload large files warning notification. + */ +function onWarningNotificationCloseClick(): void { + appStore.setLargeUploadWarningNotification(false); +} + /** * Stores project to vuex store and browser's local storage. * @param projectID - project id string @@ -596,6 +655,10 @@ onMounted(async () => { if (action.name === 'clear') clearSessionTimers(); }); + if (LocalData.getLargeUploadNotificationDismissed()) { + appStore.setLargeUploadNotification(false); + } + try { await usersStore.getUser(); await usersStore.getFrozenStatus(); @@ -690,6 +753,16 @@ onBeforeUnmount(() => { margin-top: 1rem; } + .banner-container { + + &__bottom { + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: flex-end; + } + } + .banner-container:empty { display: none; } @@ -723,11 +796,15 @@ onBeforeUnmount(() => { &__container { height: 100%; overflow-y: auto; + display: flex; + flex-direction: column; + align-items: center; &__content { max-width: 1200px; - margin: 0 auto; padding: 48px 48px 0; + box-sizing: border-box; + width: 100%; } } } diff --git a/web/satellite/static/images/notifications/circleWarning.svg b/web/satellite/static/images/notifications/circleWarning.svg new file mode 100644 index 000000000..05f164cee --- /dev/null +++ b/web/satellite/static/images/notifications/circleWarning.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/satellite/static/images/notifications/cloudAlert.svg b/web/satellite/static/images/notifications/cloudAlert.svg new file mode 100644 index 000000000..71d9cb628 --- /dev/null +++ b/web/satellite/static/images/notifications/cloudAlert.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file