From db864a8605a52c3e03400103bc064b187bdce0a8 Mon Sep 17 00:00:00 2001 From: Vitalii Date: Wed, 26 Oct 2022 19:47:48 +0300 Subject: [PATCH] web/satellite: reworked delete bucket modal to use common VModal component Reworked delete bucket modal to use common VModal component to improve UX. Issue: https://github.com/storj/storj/issues/5104 Change-Id: I74acd07a0cb3f7e0231f88bf1255de9ac000ace5 --- .../src/components/modals/AllModals.vue | 10 + .../components/modals/DeleteBucketModal.vue | 224 ++++++++++++++++++ .../navigation/ProjectSelection.vue | 11 +- .../src/components/objects/BucketItem.vue | 5 +- .../src/components/objects/BucketsView.vue | 187 --------------- .../src/components/objects/ObjectsPopup.vue | 164 ------------- .../src/components/objects/UploadFile.vue | 14 -- web/satellite/src/store/modules/appState.ts | 4 + web/satellite/src/store/modules/objects.ts | 6 - web/satellite/src/store/mutationConstants.ts | 1 + 10 files changed, 243 insertions(+), 383 deletions(-) create mode 100644 web/satellite/src/components/modals/DeleteBucketModal.vue delete mode 100644 web/satellite/src/components/objects/ObjectsPopup.vue diff --git a/web/satellite/src/components/modals/AllModals.vue b/web/satellite/src/components/modals/AllModals.vue index ab93cdcdc..5e05f1599 100644 --- a/web/satellite/src/components/modals/AllModals.vue +++ b/web/satellite/src/components/modals/AllModals.vue @@ -18,6 +18,7 @@ + @@ -39,10 +40,12 @@ import ShareBucketModal from '@/components/modals/ShareBucketModal.vue'; import ObjectDetailsModal from '@/components/modals/ObjectDetailsModal.vue'; import NewFolderModal from '@/components/modals/NewFolderModal.vue'; import ShareObjectModal from '@/components/modals/ShareObjectModal.vue'; +import DeleteBucketModal from '@/components/modals/DeleteBucketModal.vue'; // @vue/component @Component({ components: { + DeleteBucketModal, CreateProjectPromptModal, CreateProjectModal, AddPaymentMethodModal, @@ -153,6 +156,13 @@ export default class AllModals extends Vue { return this.$store.state.appStateModule.appState.isShareObjectModalShown; } + /** + * Indicates if delete bucket modal is shown. + */ + public get isDeleteBucketModal(): boolean { + return this.$store.state.appStateModule.appState.isDeleteBucketModalShown; + } + /** * Indicates if object details modal is shown. */ diff --git a/web/satellite/src/components/modals/DeleteBucketModal.vue b/web/satellite/src/components/modals/DeleteBucketModal.vue new file mode 100644 index 000000000..dbdb9db6e --- /dev/null +++ b/web/satellite/src/components/modals/DeleteBucketModal.vue @@ -0,0 +1,224 @@ +// Copyright (C) 2022 Storj Labs, Inc. +// See LICENSE for copying information. + + + + + + diff --git a/web/satellite/src/components/navigation/ProjectSelection.vue b/web/satellite/src/components/navigation/ProjectSelection.vue index 508f4e213..590371a6f 100644 --- a/web/satellite/src/components/navigation/ProjectSelection.vue +++ b/web/satellite/src/components/navigation/ProjectSelection.vue @@ -59,13 +59,13 @@ import { APP_STATE_ACTIONS, PM_ACTIONS } from '@/utils/constants/actionNames'; import { PROJECTS_ACTIONS } from '@/store/modules/projects'; import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames'; import { LocalData } from '@/utils/localData'; -import { OBJECTS_ACTIONS } from '@/store/modules/objects'; import { PAYMENTS_ACTIONS } from '@/store/modules/payments'; import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants'; import { BUCKET_ACTIONS } from '@/store/modules/buckets'; import { APP_STATE_MUTATIONS } from '@/store/mutationConstants'; import { Project } from '@/types/projects'; import { User } from '@/types/users'; +import { OBJECTS_ACTIONS } from '@/store/modules/objects'; import VLoader from '@/components/common/VLoader.vue'; @@ -146,15 +146,8 @@ export default class ProjectSelection extends Vue { if (this.isBucketsView) { await this.$store.dispatch(OBJECTS_ACTIONS.CLEAR); - this.analytics.pageVisit(RouteConfig.Buckets.path); await this.$router.push(RouteConfig.Buckets.path).catch(() => {return; }); - try { - await this.$store.dispatch(BUCKET_ACTIONS.FETCH, this.FIRST_PAGE); - } catch (error) { - await this.$notify.error(error.message); - } - return; } @@ -285,7 +278,7 @@ export default class ProjectSelection extends Vue { private get isBucketsView(): boolean { const currentRoute = this.$route.path; - return currentRoute.includes(RouteConfig.BucketsManagement.path); + return currentRoute.includes(RouteConfig.Buckets.path); } } diff --git a/web/satellite/src/components/objects/BucketItem.vue b/web/satellite/src/components/objects/BucketItem.vue index 03e2e8be4..0d3ca691e 100644 --- a/web/satellite/src/components/objects/BucketItem.vue +++ b/web/satellite/src/components/objects/BucketItem.vue @@ -31,6 +31,7 @@ import { Component, Prop } from 'vue-property-decorator'; import { RouteConfig } from '@/router'; import { Bucket } from '@/types/buckets'; import { LocalData } from '@/utils/localData'; +import { APP_STATE_MUTATIONS } from '@/store/mutationConstants'; import TableItem from '@/components/common/TableItem.vue'; import Resizable from '@/components/common/Resizable.vue'; @@ -52,8 +53,6 @@ export default class BucketItem extends Resizable { @Prop({ default: null }) public readonly itemData: Bucket; @Prop({ default: () => () => {} }) - public readonly showDeleteBucketPopup: () => void; - @Prop({ default: () => () => {} }) public readonly openDropdown; @Prop({ default: () => (_: string) => {} }) public readonly onClick: (bucket: string) => void; @@ -106,7 +105,7 @@ export default class BucketItem extends Resizable { * Holds on delete click logic. */ public onDeleteClick(): void { - this.showDeleteBucketPopup(); + this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_DELETE_BUCKET_MODAL_SHOWN); this.closeDropdown(); } diff --git a/web/satellite/src/components/objects/BucketsView.vue b/web/satellite/src/components/objects/BucketsView.vue index 6725c74ee..d7d081c8d 100644 --- a/web/satellite/src/components/objects/BucketsView.vue +++ b/web/satellite/src/components/objects/BucketsView.vue @@ -50,7 +50,6 @@ v-for="(bucket, key) in bucketsPage.buckets" :key="key" :item-data="bucket" - :show-delete-bucket-popup="showDeleteBucketPopup" :dropdown-key="key" :open-dropdown="openDropdown" :is-dropdown-open="activeDropdown === key" @@ -60,17 +59,6 @@ - @@ -80,20 +68,14 @@ import { Component, Vue, Watch } from 'vue-property-decorator'; import { RouteConfig } from '@/router'; import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants'; import { OBJECTS_ACTIONS } from '@/store/modules/objects'; -import { PROJECTS_ACTIONS } from '@/store/modules/projects'; -import { AccessGrant, EdgeCredentials } from '@/types/accessGrants'; -import { MetaUtils } from '@/utils/meta'; -import { Validator } from '@/utils/validation'; import { LocalData } from '@/utils/localData'; import { BUCKET_ACTIONS } from '@/store/modules/buckets'; import { BucketPage } from '@/types/buckets'; import { APP_STATE_MUTATIONS } from '@/store/mutationConstants'; import { AnalyticsHttpApi } from '@/api/analytics'; -import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames'; import VLoader from '@/components/common/VLoader.vue'; import BucketItem from '@/components/objects/BucketItem.vue'; -import ObjectsPopup from '@/components/objects/ObjectsPopup.vue'; import VTable from '@/components/common/VTable.vue'; import EncryptionBanner from '@/components/objects/EncryptionBanner.vue'; @@ -106,7 +88,6 @@ import EmptyBucketIcon from '@/../static/images/objects/emptyBucket.svg'; VTable, WhitePlusIcon, EmptyBucketIcon, - ObjectsPopup, BucketItem, VLoader, EncryptionBanner, @@ -114,14 +95,8 @@ import EmptyBucketIcon from '@/../static/images/objects/emptyBucket.svg'; }) export default class BucketsView extends Vue { private readonly FILE_BROWSER_AG_NAME: string = 'Web file browser API key'; - private worker: Worker; - private grantWithPermissions = ''; - private deleteBucketName = ''; public isLoading = true; - public isDeletePopupVisible = false; - public isRequestProcessing = false; - public errorMessage = ''; public activeDropdown = -1; public isServerSideEncryptionBannerHidden = true; @@ -149,9 +124,6 @@ export default class BucketsView extends Vue { */ public async setBucketsView(): Promise { try { - await this.setWorker(); - await this.removeTemporaryAccessGrant(); - await this.setAccess(); await this.fetchBuckets(); const wasDemoBucketCreated = LocalData.getDemoBucketCreatedStatus(); @@ -179,56 +151,6 @@ export default class BucketsView extends Vue { } } - /** - * Sets access to S3 client. - */ - public async setAccess(): Promise { - const cleanAPIKey: AccessGrant = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.CREATE, this.FILE_BROWSER_AG_NAME); - await this.$store.dispatch(OBJECTS_ACTIONS.SET_API_KEY, cleanAPIKey.secret); - - const now = new Date(); - const inThreeDays = new Date(now.setDate(now.getDate() + 3)); - - await this.worker.postMessage({ - 'type': 'SetPermission', - 'isDownload': true, - 'isUpload': true, - 'isList': true, - 'isDelete': true, - 'notAfter': inThreeDays.toISOString(), - 'buckets': [], - 'apiKey': cleanAPIKey.secret, - }); - - const grantEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve); - this.grantWithPermissions = grantEvent.data.value; - if (grantEvent.data.error) { - throw new Error(grantEvent.data.error); - } - - const salt = await this.$store.dispatch(PROJECTS_ACTIONS.GET_SALT, this.$store.getters.selectedProject.id); - const satelliteNodeURL: string = MetaUtils.getMetaContent('satellite-nodeurl'); - - this.worker.postMessage({ - 'type': 'GenerateAccess', - 'apiKey': this.grantWithPermissions, - 'passphrase': '', - 'salt': salt, - 'satelliteNodeURL': satelliteNodeURL, - }); - - const accessGrantEvent: MessageEvent = await new Promise(resolve => this.worker.onmessage = resolve); - if (accessGrantEvent.data.error) { - throw new Error(accessGrantEvent.data.error); - } - - const accessGrant = accessGrantEvent.data.value; - - const gatewayCredentials: EdgeCredentials = await this.$store.dispatch(ACCESS_GRANTS_ACTIONS.GET_GATEWAY_CREDENTIALS, { accessGrant, isPublic: false }); - await this.$store.dispatch(OBJECTS_ACTIONS.SET_GATEWAY_CREDENTIALS, gatewayCredentials); - await this.$store.dispatch(OBJECTS_ACTIONS.SET_S3_CLIENT); - } - /** * Fetches bucket using api. */ @@ -240,70 +162,11 @@ export default class BucketsView extends Vue { } } - /** - * Sets local worker with worker instantiated in store. - */ - public setWorker(): void { - this.worker = this.$store.state.accessGrantsModule.accessGrantsWebWorker; - this.worker.onerror = (error: ErrorEvent) => { - this.$notify.error(error.message); - }; - } - public onNewBucketButtonClick(): void { this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path); this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketCreation).path); } - /** - * Creates first ever demo bucket for user. - */ - public async createDemoBucket(): Promise { - if (this.isRequestProcessing) return; - - this.isRequestProcessing = true; - - try { - await this.$store.dispatch(OBJECTS_ACTIONS.CREATE_DEMO_BUCKET); - await this.fetchBuckets(); - - LocalData.setDemoBucketCreatedStatus(); - } catch (error) { - await this.$notify.error(error.message); - } finally { - this.isRequestProcessing = false; - } - } - - /** - * Holds delete bucket click logic. - */ - public async onDeleteBucketClick(): Promise { - if (this.isRequestProcessing) return; - - if (!this.isBucketNameValid(this.deleteBucketName)) return; - - this.isRequestProcessing = true; - - try { - if (!this.edgeCredentials.accessKeyId) { - await this.setAccess(); - } - await this.$store.dispatch(OBJECTS_ACTIONS.DELETE_BUCKET, this.deleteBucketName); - await this.fetchBuckets(); - } catch (error) { - await this.$notify.error(error.message); - return; - } finally { - this.isRequestProcessing = false; - } - - this.analytics.eventTriggered(AnalyticsEvent.BUCKET_DELETED); - - this.deleteBucketName = ''; - this.hideDeleteBucketPopup(); - } - /** * Removes temporary created access grant. */ @@ -329,30 +192,6 @@ export default class BucketsView extends Vue { this.activeDropdown = key; } - /** - * Makes delete bucket popup visible. - */ - public showDeleteBucketPopup(): void { - this.deleteBucketName = ''; - this.isDeletePopupVisible = true; - } - - /** - * Hides delete bucket popup. - */ - public hideDeleteBucketPopup(): void { - this.errorMessage = ''; - this.isDeletePopupVisible = false; - } - - /** - * Set delete bucket name form input. - */ - public setDeleteBucketName(name: string): void { - this.errorMessage = ''; - this.deleteBucketName = name; - } - /** * Hides server-side encryption banner. */ @@ -382,32 +221,6 @@ export default class BucketsView extends Vue { private get selectedProjectID(): string { return this.$store.getters.selectedProject.id; } - - /** - * Returns edge credentials from store. - */ - private get edgeCredentials(): EdgeCredentials { - return this.$store.state.objectsModule.gatewayCredentials; - } - - /** - * Returns validation status of a bucket name. - */ - private isBucketNameValid(name: string): boolean { - switch (true) { - case name.length < 3 || name.length > 63: - this.errorMessage = 'Name must be not less than 3 and not more than 63 characters length'; - - return false; - case !Validator.bucketName(name): - this.errorMessage = 'Name must contain only lowercase latin characters, numbers, a hyphen or a period'; - - return false; - - default: - return true; - } - } } diff --git a/web/satellite/src/components/objects/ObjectsPopup.vue b/web/satellite/src/components/objects/ObjectsPopup.vue deleted file mode 100644 index 3c660bac6..000000000 --- a/web/satellite/src/components/objects/ObjectsPopup.vue +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (C) 2021 Storj Labs, Inc. -// See LICENSE for copying information. - - - - - - diff --git a/web/satellite/src/components/objects/UploadFile.vue b/web/satellite/src/components/objects/UploadFile.vue index c4f9bd444..fdef64b60 100644 --- a/web/satellite/src/components/objects/UploadFile.vue +++ b/web/satellite/src/components/objects/UploadFile.vue @@ -220,20 +220,6 @@ export default class UploadFile extends Vue {