web/satellite: add uploading large file notifications

Add default uploading large file notification when in buckets page.
When dismissed, store in local storage. When user uploads a file that
exceeds 1 GB, show warning notification.

Issue https://github.com/storj/storj/issues/5148

Change-Id: I895a230c017e8439ab2c19ea494930b7e9900a47
This commit is contained in:
Lizzy Thomson 2023-03-22 06:47:51 -06:00 committed by Storj Robot
parent 27d9d9f6d4
commit 3f8eb58e4c
9 changed files with 194 additions and 3 deletions

View File

@ -0,0 +1,81 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="notification-wrap__content" :class="{ 'notification-wrap__warning-colors': warningNotification}">
<component :is="notificationIcon" class="notification-wrap__content__icon" />
<div class="notification-wrap__content__middle">
<p class="text"><b>{{ wordingBold }}</b> {{ wording }}</p>
<a target="_blank" href="https://docs.storj.io/dcs/getting-started/quickstart-objectbrowser#WeS6v">See documentation</a>
</div>
<CloseIcon class="notification-wrap__content__close" @click="onCloseClick" />
</div>
</template>
<script setup lang="ts">
import Vue, { VueConstructor } from 'vue';
import CloseIcon from '@/../static/images/notifications/closeSmall.svg';
const props = defineProps<{
wordingBold: string;
wording: string;
notificationIcon: VueConstructor<Vue>;
warningNotification: boolean;
onCloseClick: () => void;
}>();
</script>
<style scoped lang="scss">
.notification-wrap {
&__content {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
border: 1px solid rgb(56 75 101 / 40%);
padding: 16px;
margin-bottom: 48px;
font-family: 'font_regular', sans-serif;
font-size: 14px;
border-radius: 16px;
box-shadow: 0 7px 20px rgba(0 0 0 / 15%);
&__icon {
flex-shrink: 0;
margin-right: 16px;
}
&__middle {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
width: 100%;
gap: 16px;
& b {
font-family: 'font_medium', sans-serif;
}
& a {
color: black;
text-decoration: underline;
}
}
&__close {
flex-shrink: 0;
margin-left: 16px;
cursor: pointer;
}
}
&__warning-colors {
background-color: #fec;
border: 1px solid #ffd78a;
}
}
</style>

View File

@ -35,6 +35,5 @@ onMounted(async (): Promise<void> => {
<style scoped lang="scss">
.objects-area {
padding: 20px 45px;
height: calc(100% - 40px);
}
</style>

View File

@ -262,6 +262,5 @@ watch(passphrase, async () => {
<style scoped>
.file-browser {
font-family: 'font_regular', sans-serif;
padding-bottom: 200px;
}
</style>

View File

@ -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,

View File

@ -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 },

View File

@ -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) {

View File

@ -62,6 +62,24 @@
</v-banner>
</div>
<router-view class="dashboard__wrap__main-area__content-wrap__container__content" />
<div class="banner-container__bottom dashboard__wrap__main-area__content-wrap__container__content">
<UploadNotification
v-if="isLargeUploadNotificationShown && !isLargeUploadWarningNotificationShown && isBucketsView"
wording-bold="The web browser is best for uploads up to 1GB."
wording="To upload larger files, check our recommendations"
:notification-icon="CloudIcon"
:warning-notification="false"
:on-close-click="onNotificationCloseClick"
/>
<UploadNotification
v-if="isLargeUploadWarningNotificationShown"
wording-bold="Trying to upload a large file?"
wording="Check the recommendations for your use case"
:notification-icon="WarningIcon"
:warning-notification="true"
:on-close-click="onWarningNotificationCloseClick"
/>
</div>
</div>
</div>
</div>
@ -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%;
}
}
}

View File

@ -0,0 +1,3 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 0.600006C22.9529 0.600006 29.4 7.04711 29.4 15C29.4 22.9529 22.9529 29.4 15 29.4C7.04711 29.4 0.600006 22.9529 0.600006 15C0.600006 7.04711 7.04711 0.600006 15 0.600006ZM15 3.24001C8.50514 3.24001 3.24001 8.50514 3.24001 15C3.24001 21.4949 8.50514 26.76 15 26.76C21.4949 26.76 26.76 21.4949 26.76 15C26.76 8.50514 21.4949 3.24001 15 3.24001ZM16.2 8.40074V15.9936C16.2 16.7226 15.609 17.3136 14.88 17.3136C14.1688 17.3136 13.5889 16.7511 13.5611 16.0467L13.56 15.9936V8.48858C13.56 7.75216 14.144 7.14842 14.88 7.12393C15.5852 7.10046 16.1758 7.65309 16.1993 8.35825C16.1998 8.37241 16.2 8.38657 16.2 8.40074ZM16.2 21.1207V21.2736C16.2 22.0026 15.609 22.5936 14.88 22.5936C14.1688 22.5936 13.5889 22.0311 13.5611 21.3267L13.56 21.2736V21.2086C13.56 20.4722 14.144 19.8684 14.88 19.8439C15.5852 19.8205 16.1758 20.3731 16.1993 21.0783C16.1998 21.0924 16.2 21.1066 16.2 21.1207Z" fill="#FF8A00"/>
</svg>

After

Width:  |  Height:  |  Size: 1010 B

View File

@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.4714 32C37.4486 32 39.8621 29.5865 39.8621 26.6094V25.4941C39.8621 22.5169 37.4486 20.1034 34.4714 20.1034L31.9243 20.1036C31.7154 13.9359 26.6496 9 20.431 9C15.6237 9 11.5053 11.9498 9.78712 16.138L8.93103 16.1379C4.55084 16.1379 1 19.6888 1 24.069C1 28.4492 4.55084 32 8.93103 32H34.4714Z" fill="#C8D3DE"/>
<path d="M20.431 28.8275C25.0302 28.8275 28.7586 25.0991 28.7586 20.5C28.7586 15.9008 25.0302 12.1724 20.431 12.1724C15.8318 12.1724 12.1035 15.9008 12.1035 20.5C12.1035 25.0991 15.8318 28.8275 20.431 28.8275Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.431 21.6896C21.5261 21.6896 22.4138 22.5773 22.4138 23.6723C22.4138 24.7674 21.5261 25.6551 20.431 25.6551C19.336 25.6551 18.4483 24.7674 18.4483 23.6723C18.4483 22.5773 19.336 21.6896 20.431 21.6896ZM20.431 14.5516C21.3808 14.5516 22.1507 15.3215 22.1507 16.2713C22.1507 16.3526 22.1449 16.4339 22.1334 16.5145L21.6207 20.1034H19.2414L18.7287 16.5145C18.5944 15.5743 19.2477 14.7032 20.1878 14.5689C20.2684 14.5574 20.3497 14.5516 20.431 14.5516Z" fill="#FF458B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB