web/satellite: added cancel upload warning popup

Added warning popup for when file upload is in progress.
It says that leaving objects page will cancel upload

Change-Id: If49c1ddc898f2e6dbdc86e5a00d604a55b628720
This commit is contained in:
Vitalii Shpital 2021-06-18 17:00:12 +03:00
parent 7d3b5b932e
commit d9741491a1
8 changed files with 185 additions and 29 deletions

View File

@ -0,0 +1,134 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="uc-area">
<div class="uc-area__popup">
<h1 class="uc-area__popup__title">Upload in progress</h1>
<div class="uc-area__popup__container">
<div class="uc-area__popup__container__header">
<WarningIcon/>
<h2 class="uc-area__popup__container__header__question">Are you sure you want to leave?</h2>
</div>
<p class="uc-area__popup__container__msg">
Navigating to another page while uploading data may cancel your upload. Please confirm before
proceeding.
</p>
</div>
<VButton
width="100%"
height="48px"
label="Continue uploading"
:on-press="closePopup"
/>
<p class="uc-area__popup__link" @click="onLeaveClick">Cancel upload and leave</p>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import VButton from '@/components/common/VButton.vue';
import WarningIcon from '@/../static/images/objects/cancelWarning.svg';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
@Component({
components: {
WarningIcon,
VButton,
},
})
export default class UploadCancelPopup extends Vue {
/**
* Holds on leave click logic.
*/
public onLeaveClick(): void {
this.$router.push(this.leaveRoute);
this.closePopup();
}
/**
* Close upload cancel info popup.
*/
public closePopup(): void {
this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_UPLOAD_CANCEL_POPUP);
}
/**
* Returns leave attempt's route path from store.
*/
private get leaveRoute(): string {
return this.$store.state.objectsModule.leaveRoute;
}
}
</script>
<style scoped lang="scss">
.uc-area {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 100;
background: rgba(27, 37, 51, 0.75);
display: flex;
align-items: center;
justify-content: center;
font-family: 'font_regular', sans-serif;
&__popup {
padding: 70px;
border-radius: 8px;
background-color: #fff;
display: flex;
flex-direction: column;
align-items: center;
&__title {
width: 100%;
text-align: left;
font-family: 'font_bold', sans-serif;
font-size: 23px;
line-height: 49px;
letter-spacing: -0.100741px;
color: #252525;
margin: 0 0 22px 0;
}
&__container {
background: #f7f8fb;
border-radius: 8px;
margin-bottom: 22px;
max-width: 465px;
padding: 20px;
&__header {
display: flex;
align-items: center;
margin-bottom: 10px;
&__question {
font-family: 'font_bold', sans-serif;
font-size: 16px;
line-height: 19px;
color: #1b2533;
margin: 0 0 0 10px;
}
}
}
&__link {
font-weight: 500;
font-size: 16px;
line-height: 21px;
color: #0068dc;
margin: 22px 0 0 0;
cursor: pointer;
}
}
}
</style>

View File

@ -7,6 +7,7 @@
<div class="file-browser">
<FileBrowser></FileBrowser>
</div>
<UploadCancelPopup v-if="isCancelUploadPopupVisible"/>
</div>
</template>
@ -14,6 +15,8 @@
import { FileBrowser } from 'browser';
import { Component, Vue } from 'vue-property-decorator';
import UploadCancelPopup from '@/components/objects/UploadCancelPopup.vue';
import { AnalyticsHttpApi } from '@/api/analytics';
import { RouteConfig } from '@/router';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
@ -24,6 +27,7 @@ import { MetaUtils } from '@/utils/meta';
@Component({
components: {
FileBrowser,
UploadCancelPopup,
},
})
export default class UploadFile extends Vue {
@ -124,6 +128,13 @@ export default class UploadFile extends Vue {
};
}
/**
* Indicates if upload cancel popup is visible.
*/
public get isCancelUploadPopupVisible(): boolean {
return this.$store.state.appStateModule.appState.isUploadCancelPopupVisible;
}
/**
* Generates public access key.
*/

View File

@ -36,6 +36,7 @@ import ProjectsList from '@/components/projectsList/ProjectsList.vue';
import ProjectMembersArea from '@/components/team/ProjectMembersArea.vue';
import store from '@/store';
import { OBJECTS_ACTIONS } from '@/store/modules/objects';
import { NavigationLink } from '@/types/navigation';
const DashboardArea = () => import('@/views/DashboardArea.vue');
@ -180,11 +181,6 @@ export const router = new Router({
name: RouteConfig.ProjectDashboard.name,
component: ProjectDashboard,
},
{
path: RouteConfig.Root.path,
name: 'default',
component: ProjectDashboard,
},
{
path: RouteConfig.Users.path,
name: RouteConfig.Users.name,
@ -347,6 +343,7 @@ export const router = new Router({
children: [
{
path: '*',
name: RouteConfig.UploadFile.name,
component: UploadFile,
},
],
@ -363,7 +360,12 @@ export const router = new Router({
],
});
router.beforeEach((to, from, next) => {
router.beforeEach(async (to, from, next) => {
if (from.name === RouteConfig.UploadFile.name && !store.state.appStateModule.appState.isUploadCancelPopupVisible) {
const areUploadsInProgress: boolean = await store.dispatch(OBJECTS_ACTIONS.CHECK_ONGOING_UPLOADS, to.path);
if (areUploadsInProgress) return;
}
if (navigateToDefaultSubTab(to.matched, RouteConfig.Account)) {
next(RouteConfig.Account.with(RouteConfig.Billing).path);
@ -394,12 +396,6 @@ router.beforeEach((to, from, next) => {
return;
}
if (to.name === 'default') {
next(RouteConfig.ProjectDashboard.path);
return;
}
next();
});
@ -424,7 +420,6 @@ router.afterEach(({ name }, from) => {
* if our route is a tab and has no sub tab route - we will navigate to default subtab.
* F.E. /account/ -> /account/billing/;
* @param routes - array of RouteRecord from vue-router
* @param next - callback to process next route
* @param tabRoute - tabNavigator route
*/
function navigateToDefaultSubTab(routes: RouteRecord[], tabRoute: NavigationLink): boolean {

View File

@ -23,11 +23,11 @@ export const appStateModule = {
isPeriodsDropdownShown: false,
isDeleteProjectPopupShown: false,
isDeleteAccountPopupShown: false,
isSortProjectMembersByPopupShown: false,
isSuccessfulRegistrationShown: false,
isEditProfilePopupShown: false,
isChangePasswordPopupShown: false,
isPaymentSelectionShown: false,
isUploadCancelPopupVisible: false,
},
satelliteName: '',
partneredSatellites: new Array<PartneredSatellite>(),
@ -79,10 +79,6 @@ export const appStateModule = {
[APP_STATE_MUTATIONS.TOGGLE_DELETE_ACCOUNT_DROPDOWN](state: any): void {
state.appState.isDeleteAccountPopupShown = !state.appState.isDeleteAccountPopupShown;
},
// Mutation changing 'sort project members by' popup visibility.
[APP_STATE_MUTATIONS.TOGGLE_SORT_PM_BY_DROPDOWN](state: any): void {
state.appState.isSortProjectMembersByPopupShown = !state.appState.isSortProjectMembersByPopupShown;
},
// Mutation changing 'successful registration' area visibility.
[APP_STATE_MUTATIONS.TOGGLE_SUCCESSFUL_REGISTRATION](state: any): void {
state.appState.isSuccessfulRegistrationShown = !state.appState.isSuccessfulRegistrationShown;
@ -93,6 +89,9 @@ export const appStateModule = {
[APP_STATE_MUTATIONS.TOGGLE_EDIT_PROFILE_POPUP](state: any): void {
state.appState.isEditProfilePopupShown = !state.appState.isEditProfilePopupShown;
},
[APP_STATE_MUTATIONS.TOGGLE_UPLOAD_CANCEL_POPUP](state: any): void {
state.appState.isUploadCancelPopupVisible = !state.appState.isUploadCancelPopupVisible;
},
[APP_STATE_MUTATIONS.SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP](state: any, id: string): void {
state.appState.setDefaultPaymentMethodID = id;
},
@ -110,7 +109,6 @@ export const appStateModule = {
state.appState.isAvailableBalanceDropdownShown = false;
state.appState.isPeriodsDropdownShown = false;
state.appState.isPaymentSelectionShown = false;
state.appState.isSortProjectMembersByPopupShown = false;
},
[APP_STATE_MUTATIONS.CHANGE_STATE](state: any, newFetchState: AppState): void {
state.appState.fetchState = newFetchState;
@ -218,13 +216,6 @@ export const appStateModule = {
commit(APP_STATE_MUTATIONS.TOGGLE_DELETE_ACCOUNT_DROPDOWN);
},
[APP_STATE_ACTIONS.TOGGLE_SORT_PM_BY_DROPDOWN]: function ({commit, state}: any): void {
if (!state.appState.isSortProjectMembersByPopupShown) {
commit(APP_STATE_MUTATIONS.CLOSE_ALL);
}
commit(APP_STATE_MUTATIONS.TOGGLE_SORT_PM_BY_DROPDOWN);
},
[APP_STATE_ACTIONS.TOGGLE_SUCCESSFUL_REGISTRATION]: function ({commit, state}: any): void {
if (!state.appState.isSuccessfulRegistrationShown) {
commit(APP_STATE_MUTATIONS.CLOSE_ALL);
@ -235,6 +226,9 @@ export const appStateModule = {
[APP_STATE_ACTIONS.TOGGLE_CHANGE_PASSWORD_POPUP]: function ({commit}: any): void {
commit(APP_STATE_MUTATIONS.TOGGLE_CHANGE_PASSWORD_POPUP);
},
[APP_STATE_ACTIONS.TOGGLE_UPLOAD_CANCEL_POPUP]: function ({commit}: any): void {
commit(APP_STATE_MUTATIONS.TOGGLE_UPLOAD_CANCEL_POPUP);
},
[APP_STATE_ACTIONS.TOGGLE_EDIT_PROFILE_POPUP]: function ({commit}: any): void {
commit(APP_STATE_MUTATIONS.TOGGLE_EDIT_PROFILE_POPUP);
},

View File

@ -5,6 +5,7 @@ import S3, { Bucket } from 'aws-sdk/clients/s3';
import { StoreModule } from '@/store';
import { GatewayCredentials } from '@/types/accessGrants';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
export const OBJECTS_ACTIONS = {
CLEAR: 'clearObjects',
@ -17,6 +18,7 @@ export const OBJECTS_ACTIONS = {
FETCH_BUCKETS: 'fetchBuckets',
CREATE_BUCKET: 'createBucket',
DELETE_BUCKET: 'deleteBucket',
CHECK_ONGOING_UPLOADS: 'checkOngoingUploads',
};
export const OBJECTS_MUTATIONS = {
@ -28,6 +30,7 @@ export const OBJECTS_MUTATIONS = {
SET_BUCKETS: 'setBuckets',
SET_FILE_COMPONENT_BUCKET_NAME: 'setFileComponentBucketName',
SET_PASSPHRASE: 'setPassphrase',
SET_LEAVE_ROUTE: 'setLeaveRoute',
};
const {
@ -39,6 +42,7 @@ const {
SET_BUCKETS,
SET_PASSPHRASE,
SET_FILE_COMPONENT_BUCKET_NAME,
SET_LEAVE_ROUTE,
} = OBJECTS_MUTATIONS;
export class ObjectsState {
@ -49,6 +53,7 @@ export class ObjectsState {
public bucketsList: Bucket[] = [];
public passphrase: string = '';
public fileComponentBucketName: string = '';
public leaveRoute: string = '';
}
/**
@ -85,6 +90,9 @@ export function makeObjectsModule(): StoreModule<ObjectsState> {
[SET_FILE_COMPONENT_BUCKET_NAME](state: ObjectsState, bucketName: string) {
state.fileComponentBucketName = bucketName;
},
[SET_LEAVE_ROUTE](state: ObjectsState, leaveRoute: string) {
state.leaveRoute = leaveRoute;
},
[CLEAR](state: ObjectsState) {
state.apiKey = '';
state.passphrase = '';
@ -129,9 +137,19 @@ export function makeObjectsModule(): StoreModule<ObjectsState> {
Bucket: name,
}).promise();
},
clearObjects: function ({commit}: any): void {
clearObjects: function({commit}: any): void {
commit(CLEAR);
},
checkOngoingUploads: function({commit, dispatch, rootState}: any, leaveRoute: string): boolean {
if (!rootState.files.uploading.length) {
return false;
}
commit(SET_LEAVE_ROUTE, leaveRoute);
dispatch(APP_STATE_ACTIONS.TOGGLE_UPLOAD_CANCEL_POPUP, null, {root: true});
return true;
},
},
};
}

View File

@ -21,11 +21,11 @@ export const APP_STATE_MUTATIONS = {
TOGGLE_FREE_CREDITS_DROPDOWN: 'TOGGLE_FREE_CREDITS_DROPDOWN',
TOGGLE_AVAILABLE_BALANCE_DROPDOWN: 'TOGGLE_AVAILABLE_BALANCE_DROPDOWN',
TOGGLE_PERIODS_DROPDOWN: 'TOGGLE_PERIODS_DROPDOWN',
TOGGLE_SORT_PM_BY_DROPDOWN: 'TOGGLE_SORT_PM_BY_DROPDOWN',
TOGGLE_SUCCESSFUL_REGISTRATION: 'TOGGLE_SUCCESSFUL_REGISTRATION',
TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP: 'TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP',
TOGGLE_EDIT_PROFILE_POPUP: 'TOGGLE_EDIT_PROFILE_POPUP',
TOGGLE_CHANGE_PASSWORD_POPUP: 'TOGGLE_CHANGE_PASSWORD_POPUP',
TOGGLE_UPLOAD_CANCEL_POPUP: 'TOGGLE_UPLOAD_CANCEL_POPUP',
SHOW_DELETE_PAYMENT_METHOD_POPUP: 'SHOW_DELETE_PAYMENT_METHOD_POPUP',
SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP: 'SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP',
CLOSE_ALL: 'CLOSE_ALL',

View File

@ -13,11 +13,11 @@ export const APP_STATE_ACTIONS = {
TOGGLE_PERIODS_DROPDOWN: 'togglePeriodsDropdown',
TOGGLE_DEL_PROJ: 'toggleDeleteProjectPopup',
TOGGLE_DEL_ACCOUNT: 'toggleDeleteAccountPopup',
TOGGLE_SORT_PM_BY_DROPDOWN: 'toggleSortProjectMembersByPopup',
TOGGLE_SUCCESSFUL_REGISTRATION: 'TOGGLE_SUCCESSFUL_REGISTRATION',
TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP: 'toggleSuccessfulProjectCreationPopup',
TOGGLE_EDIT_PROFILE_POPUP: 'toggleEditProfilePopup',
TOGGLE_CHANGE_PASSWORD_POPUP: 'toggleChangePasswordPopup',
TOGGLE_UPLOAD_CANCEL_POPUP: 'toggleUploadCancelPopup',
SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP: 'showSetDefaultPaymentMethodPopup',
CLOSE_SET_DEFAULT_PAYMENT_METHOD_POPUP: 'closeSetDefaultPaymentMethodPopup',
SHOW_DELETE_PAYMENT_METHOD_POPUP: 'showDeletePaymentMethodPopup',

View File

@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="18" y="18" width="18" height="18" rx="9" transform="rotate(-180 18 18)" fill="#92A0AE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.99707 4.66675C8.44478 4.66675 7.99707 5.11446 7.99707 5.66675L7.99707 9.00008C7.99707 9.55237 8.44478 10.0001 8.99707 10.0001C9.54935 10.0001 9.99707 9.55237 9.99707 9.00008L9.99707 5.66675C9.99707 5.11446 9.54935 4.66675 8.99707 4.66675ZM9.00098 11C8.31062 11 7.75098 11.5596 7.75098 12.25C7.75098 12.9404 8.31062 13.5 9.00098 13.5C9.69133 13.5 10.251 12.9404 10.251 12.25C10.251 11.5596 9.69133 11 9.00098 11Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 689 B