From 3d37049eaed1312e8a5321bf2de759b93b212867 Mon Sep 17 00:00:00 2001 From: sw-wayner Date: Thu, 29 Feb 2024 15:23:44 -0400 Subject: [PATCH 01/24] ci: add needed actions --- .../workflows/build-and-publish-preview.yaml | 48 ++++++++ .github/workflows/clean-up-pr-preview.yaml | 27 +++++ .github/workflows/deploy-pr-preview.yaml | 110 ++++++++++++++++++ .../workflows/slash-command-dispatcher.yaml | 30 +++++ infrastructure/preview.Dockerfile | 16 +++ 5 files changed, 231 insertions(+) create mode 100644 .github/workflows/build-and-publish-preview.yaml create mode 100644 .github/workflows/clean-up-pr-preview.yaml create mode 100644 .github/workflows/deploy-pr-preview.yaml create mode 100644 .github/workflows/slash-command-dispatcher.yaml create mode 100644 infrastructure/preview.Dockerfile diff --git a/.github/workflows/build-and-publish-preview.yaml b/.github/workflows/build-and-publish-preview.yaml new file mode 100644 index 000000000..701f00a0e --- /dev/null +++ b/.github/workflows/build-and-publish-preview.yaml @@ -0,0 +1,48 @@ +name: Build & Publish Stable Preview +on: + push: + branches: ["master"] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: echo "registry=https://registry.yarnpkg.com/" > .npmrc + - run: echo "@internxt:registry=https://npm.pkg.github.com" >> .npmrc + # You cannot read packages from other private repos with GITHUB_TOKEN + # You have to use a PAT instead https://github.com/actions/setup-node/issues/49 + - run: echo //npm.pkg.github.com/:_authToken=${{ secrets.PERSONAL_ACCESS_TOKEN }} >> .npmrc + - run: echo "always-auth=true" >> .npmrc + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Build and push to ${{ github.event.repository.name }}-dev + uses: docker/build-push-action@v2 + with: + context: ./ + file: ./infrastructure/preview.Dockerfile + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}-dev:${{ github.sha }} + dispatch_update_preview_image: + needs: build + runs-on: ubuntu-latest + steps: + - name: Dispatch Update Preview Image Command + uses: myrotvorets/trigger-repository-dispatch-action@1.0.0 + with: + token: ${{ secrets.PAT }} + repo: internxt/environments + type: update-preview-image-command + payload: | + { + "image": { + "name": "${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}", + "newName": "${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}-dev", + "newTag": "${{ github.sha }}" + } + } \ No newline at end of file diff --git a/.github/workflows/clean-up-pr-preview.yaml b/.github/workflows/clean-up-pr-preview.yaml new file mode 100644 index 000000000..815cbf1ba --- /dev/null +++ b/.github/workflows/clean-up-pr-preview.yaml @@ -0,0 +1,27 @@ +name: Clean Up PR Preview +on: + pull_request: + types: [closed] +jobs: + dispatch_cleanup_deployment: + runs-on: ubuntu-latest + steps: + - name: Dispatch Cleanup Preview Repository Command + uses: myrotvorets/trigger-repository-dispatch-action@1.0.0 + with: + token: ${{ secrets.PAT }} + repo: internxt/environments + type: cleanup-preview-command + payload: | + { + "github": { + "payload": { + "repository": { + "name": "${{ github.event.repository.name }}" + }, + "issue": { + "number": ${{ github.event.number }} + } + } + } + } \ No newline at end of file diff --git a/.github/workflows/deploy-pr-preview.yaml b/.github/workflows/deploy-pr-preview.yaml new file mode 100644 index 000000000..d3056416a --- /dev/null +++ b/.github/workflows/deploy-pr-preview.yaml @@ -0,0 +1,110 @@ +name: Deploy PR Preview +on: + pull_request: + types: [opened, reopened, synchronize, ready_for_review] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: echo "registry=https://registry.yarnpkg.com/" > .npmrc + - run: echo "@internxt:registry=https://npm.pkg.github.com" >> .npmrc + # You cannot read packages from other private repos with GITHUB_TOKEN + # You have to use a PAT instead https://github.com/actions/setup-node/issues/49 + - run: echo //npm.pkg.github.com/:_authToken=${{ secrets.PERSONAL_ACCESS_TOKEN }} >> .npmrc + - run: echo "always-auth=true" >> .npmrc + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Build and push to ${{ github.event.repository.name }}-dev + uses: docker/build-push-action@v2 + with: + context: ./ + file: ./infrastructure/preview.Dockerfile + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}-dev:preview-${{ github.event.number }}-${{ github.event.pull_request.head.sha }} + add_preview_label: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions-ecosystem/action-add-labels@v1 + with: + labels: | + preview + dispatch_update_deployment: + needs: add_preview_label + runs-on: ubuntu-latest + if: ${{ contains(github.event.pull_request.labels.*.name, 'deployed') }} + steps: + - name: Dispatch Update Preview Repository Command + uses: myrotvorets/trigger-repository-dispatch-action@1.0.0 + with: + token: ${{ secrets.PAT }} + repo: internxt/environments + type: update-preview-command + payload: | + { + "github": { + "payload": { + "repository": { + "name": "${{ github.event.repository.name }}", + "full_name": "${{ github.event.repository.full_name }}" + }, + "issue": { + "number": ${{ github.event.number }}, + "labels": ${{ toJSON(github.event.pull_request.labels) }} + } + } + }, + "slash_command": { + "args": { + "named": { + "deployment": "${{ github.event.repository.name }}", + "tag": "preview-${{ github.event.number }}-${{ github.event.pull_request.head.sha }}", + "imageSuffix": "-dev" + } + } + } + } + dispatch_check_deployment: + needs: add_preview_label + runs-on: ubuntu-latest + steps: + - name: Dispatch Check Preview Repository Command + uses: myrotvorets/trigger-repository-dispatch-action@1.0.0 + with: + token: ${{ secrets.PAT }} + repo: internxt/environments + type: check-preview-command + payload: | + { + "github": { + "payload": { + "repository": { + "name": "${{ github.event.repository.name }}", + "full_name": "${{ github.event.repository.full_name }}", + "html_url": "${{ github.event.repository.html_url }}" + }, + "issue": { + "number": ${{ github.event.number }}, + "labels": ${{ toJSON(github.event.pull_request.labels) }}, + "pull_request": { + "html_url": "${{ github.event.pull_request.html_url }}" + } + } + } + }, + "slash_command": { + "args": { + "named": { + "notify": "true" + } + } + } + } \ No newline at end of file diff --git a/.github/workflows/slash-command-dispatcher.yaml b/.github/workflows/slash-command-dispatcher.yaml new file mode 100644 index 000000000..50120cde5 --- /dev/null +++ b/.github/workflows/slash-command-dispatcher.yaml @@ -0,0 +1,30 @@ +name: Slash Command Dispatch +on: + issue_comment: + types: [created] +jobs: + slash_command_dispatch: + runs-on: ubuntu-latest + if: ${{ contains(github.event.issue.labels.*.name, 'deployed') || contains(github.event.issue.labels.*.name, 'preview') }} + steps: + - name: Slash Command Dispatch + id: scd + uses: peter-evans/slash-command-dispatch@v4 + with: + token: ${{ secrets.PAT }} + commands: update-preview,check-preview + permission: write + repository: internxt/environments + issue-type: pull-request + allow-edits: false + reactions: false + - name: Edit comment with error message + if: steps.scd.outputs.error-message + uses: peter-evans/create-or-update-comment@v4 + with: + comment-id: ${{ github.event.comment.id }} + body: | + + > [!CAUTION] + > Couldn't dispatch your command due to error: + > **${{ steps.scd.outputs.error-message }}** \ No newline at end of file diff --git a/infrastructure/preview.Dockerfile b/infrastructure/preview.Dockerfile new file mode 100644 index 000000000..2595e8e84 --- /dev/null +++ b/infrastructure/preview.Dockerfile @@ -0,0 +1,16 @@ +FROM node:18-alpine3.19 + +RUN apk update +RUN apk add nginx git yarn + +WORKDIR /app +COPY package.json yarn.lock ./ +COPY .npmrc ./.npmrc +COPY ./scripts ./scripts/ + +RUN yarn +COPY . /app + +EXPOSE 3000 + +CMD ["yarn", "dev"] \ No newline at end of file From dd234d1ae6d4ad7d6a98a88e8207e0a6dac9d6f8 Mon Sep 17 00:00:00 2001 From: Ramon Candel Date: Mon, 4 Mar 2024 11:19:21 +0100 Subject: [PATCH 02/24] Fixed race condition when navigating between folders too fast --- .../CreateFolderDialog/CreateFolderDialog.tsx | 15 +++++---- .../DriveExplorer/DriveExplorer.tsx | 9 +++-- .../DriveExplorerList/DriveExplorerList.tsx | 33 ++++++++++++------- src/app/drive/views/DriveView/DriveView.tsx | 2 +- src/app/store/slices/storage/index.ts | 17 +++++----- src/app/store/slices/storage/storage.model.ts | 4 +-- .../store/slices/storage/storage.selectors.ts | 4 +-- .../storage.thunks/fetchFolderContentThunk.ts | 9 ++--- .../fetchSortedFolderContentThunk.ts | 4 +-- .../storage/storage.thunks/goToFolderThunk.ts | 3 ++ 10 files changed, 58 insertions(+), 42 deletions(-) diff --git a/src/app/drive/components/CreateFolderDialog/CreateFolderDialog.tsx b/src/app/drive/components/CreateFolderDialog/CreateFolderDialog.tsx index ba2c17cc3..a5fd7cf21 100644 --- a/src/app/drive/components/CreateFolderDialog/CreateFolderDialog.tsx +++ b/src/app/drive/components/CreateFolderDialog/CreateFolderDialog.tsx @@ -45,17 +45,18 @@ const CreateFolderDialog = ({ onFolderCreated, currentFolderId, neededFolderId } const createFolder = async () => { if (folderName && folderName.trim().length > 0) { setIsLoading(true); + const parentFolderId = currentFolderId ?? neededFolderId; await dispatch( storageThunks.createFolderThunk({ folderName, - parentFolderId: currentFolderId ? currentFolderId : neededFolderId, + parentFolderId, }), ) .unwrap() .then(() => { onFolderCreated && onFolderCreated(); - dispatch(storageActions.setHasMoreDriveFolders(true)); - dispatch(storageActions.setHasMoreDriveFiles(true)); + dispatch(storageActions.setHasMoreDriveFolders({ folderId: parentFolderId, status: true })); + dispatch(storageActions.setHasMoreDriveFiles({ folderId: parentFolderId, status: true })); setTimeout(() => { dispatch(fetchSortedFolderContentThunk(currentFolderId)); setIsLoading(false); @@ -72,7 +73,7 @@ const CreateFolderDialog = ({ onFolderCreated, currentFolderId, neededFolderId } return e; }); } else { - setError(translate('error.folderCannotBeEmpty') as string); + setError(translate('error.folderCannotBeEmpty')); } }; @@ -92,9 +93,9 @@ const CreateFolderDialog = ({ onFolderCreated, currentFolderId, neededFolderId } { setFolderName(name); setError(''); diff --git a/src/app/drive/components/DriveExplorer/DriveExplorer.tsx b/src/app/drive/components/DriveExplorer/DriveExplorer.tsx index aff99cc62..5d337941d 100644 --- a/src/app/drive/components/DriveExplorer/DriveExplorer.tsx +++ b/src/app/drive/components/DriveExplorer/DriveExplorer.tsx @@ -156,6 +156,7 @@ const DriveExplorer = (props: DriveExplorerProps): JSX.Element => { const [hasMoreItems, setHasMoreItems] = useState(true); const [hasMoreTrashFolders, setHasMoreTrashFolders] = useState(true); const [isLoadingTrashItems, setIsLoadingTrashItems] = useState(false); + const hasMoreItemsToLoad = isTrash ? hasMoreItems : hasMoreFiles || hasMoreFolders; // RIGHT CLICK MENU STATES const [isListElementsHovered, setIsListElementsHovered] = useState(false); @@ -822,7 +823,7 @@ const DriveExplorer = (props: DriveExplorerProps): JSX.Element => { items={items} isLoading={isTrash ? isLoadingTrashItems : isLoading} onEndOfScroll={fetchItems} - hasMoreItems={hasMoreItems} + hasMoreItems={hasMoreItemsToLoad} isTrash={isTrash} onHoverListItems={(areHovered) => { setIsListElementsHovered(areHovered); @@ -1045,6 +1046,8 @@ const dropTargetCollect: DropTargetCollector< export default connect((state: RootState) => { const currentFolderId: number = storageSelectors.currentFolderId(state); + const hasMoreFolders = state.storage.hasMoreDriveFolders[currentFolderId] ?? true; + const hasMoreFiles = state.storage.hasMoreDriveFiles[currentFolderId] ?? true; return { isAuthenticated: state.user.isAuthenticated, @@ -1063,7 +1066,7 @@ export default connect((state: RootState) => { planUsage: state.plan.planUsage, folderOnTrashLength: state.storage.folderOnTrashLength, filesOnTrashLength: state.storage.filesOnTrashLength, - hasMoreFolders: state.storage.hasMoreDriveFolders, - hasMoreFiles: state.storage.hasMoreDriveFiles, + hasMoreFolders, + hasMoreFiles, }; })(DropTarget([NativeTypes.FILE], dropTargetSpec, dropTargetCollect)(DriveExplorer)); diff --git a/src/app/drive/components/DriveExplorer/DriveExplorerList/DriveExplorerList.tsx b/src/app/drive/components/DriveExplorer/DriveExplorerList/DriveExplorerList.tsx index e65c9ffa8..0ad1e30c8 100644 --- a/src/app/drive/components/DriveExplorer/DriveExplorerList/DriveExplorerList.tsx +++ b/src/app/drive/components/DriveExplorer/DriveExplorerList/DriveExplorerList.tsx @@ -77,6 +77,25 @@ const createDriveListItem = (item: DriveItemData, isTrash?: boolean) => ( ); +const resetDriveOrder = ({ + dispatch, + orderType, + direction, + currentFolderId, +}: { + dispatch; + orderType: string; + direction: string; + currentFolderId: number; +}) => { + dispatch(storageActions.setDriveItemsSort(orderType)); + dispatch(storageActions.setDriveItemsOrder(direction)); + + dispatch(storageActions.setHasMoreDriveFolders({ folderId: currentFolderId, status: true })); + dispatch(storageActions.setHasMoreDriveFiles({ folderId: currentFolderId, status: true })); + dispatch(fetchSortedFolderContentThunk(currentFolderId)); +}; + const DriveExplorerList: React.FC = memo((props) => { const [isAllSelectedEnabled, setIsAllSelectedEnabled] = useState(false); const [editNameItem, setEditNameItem] = useState(null); @@ -143,21 +162,11 @@ const DriveExplorerList: React.FC = memo((props) => { dispatch(storageActions.setOrder({ by: value.field, direction })); if (value.field === 'name') { - dispatch(storageActions.setDriveItemsSort('plainName')); - dispatch(storageActions.setDriveItemsOrder(direction)); - - dispatch(storageActions.setHasMoreDriveFolders(true)); - dispatch(storageActions.setHasMoreDriveFiles(true)); - dispatch(fetchSortedFolderContentThunk(currentFolderId)); + resetDriveOrder({ dispatch, orderType: 'plainName', direction, currentFolderId }); } if (value.field === 'updatedAt') { - dispatch(storageActions.setDriveItemsSort('updatedAt')); - dispatch(storageActions.setDriveItemsOrder(direction)); - - dispatch(storageActions.setHasMoreDriveFolders(true)); - dispatch(storageActions.setHasMoreDriveFiles(true)); - dispatch(fetchSortedFolderContentThunk(currentFolderId)); + resetDriveOrder({ dispatch, orderType: 'updatedAt', direction, currentFolderId }); } }; diff --git a/src/app/drive/views/DriveView/DriveView.tsx b/src/app/drive/views/DriveView/DriveView.tsx index f26b8734d..2348d9d3a 100644 --- a/src/app/drive/views/DriveView/DriveView.tsx +++ b/src/app/drive/views/DriveView/DriveView.tsx @@ -139,7 +139,7 @@ export default connect((state: RootState) => { return { namePath: state.storage.namePath, - isLoading: state.storage.loadingFolders[currentFolderId], + isLoading: state.storage.loadingFolders[currentFolderId] ?? true, currentFolderId, items: sortedItems, }; diff --git a/src/app/store/slices/storage/index.ts b/src/app/store/slices/storage/index.ts index f64a427e8..37210881a 100644 --- a/src/app/store/slices/storage/index.ts +++ b/src/app/store/slices/storage/index.ts @@ -4,7 +4,6 @@ import selectors from './storage.selectors'; import { storageExtraReducers } from '../storage/storage.thunks'; import { filtersFactory, orderFactory, StorageSetFiltersPayload, StorageState } from './storage.model'; import databaseService, { DatabaseCollection } from '../../../database/services/database.service'; -import itemsListService from '../../../drive/services/items-list.service'; import { OrderDirection, OrderSettings } from '../../../core/types'; import { DriveItemData, DriveItemPatch, FileViewMode, FolderPath } from '../../../drive/types'; import { ShareLink } from '@internxt/sdk/dist/drive/share/types'; @@ -18,8 +17,8 @@ const initialState: StorageState = { moveDialogLevels: {}, levelsFoldersLength: {}, levelsFilesLength: {}, - hasMoreDriveFolders: true, - hasMoreDriveFiles: true, + hasMoreDriveFolders: {}, + hasMoreDriveFiles: {}, recents: [], isLoadingRecents: false, isLoadingDeleted: false, @@ -109,15 +108,15 @@ export const storageSlice = createSlice({ state.levelsFilesLength[action.payload.folderId] = 0; state.levels[action.payload.folderId] = []; }, - setHasMoreDriveFolders: (state: StorageState, action: PayloadAction) => { - state.hasMoreDriveFolders = action.payload; + setHasMoreDriveFolders: (state: StorageState, action: PayloadAction<{ folderId: number; status: boolean }>) => { + state.hasMoreDriveFolders[action.payload.folderId] = action.payload.status; }, - setHasMoreDriveFiles: (state: StorageState, action: PayloadAction) => { - state.hasMoreDriveFiles = action.payload; + setHasMoreDriveFiles: (state: StorageState, action: PayloadAction<{ folderId: number; status: boolean }>) => { + state.hasMoreDriveFiles[action.payload.folderId] = action.payload.status; }, resetDrivePagination: (state: StorageState) => { - state.hasMoreDriveFiles = true; - state.hasMoreDriveFolders = true; + state.hasMoreDriveFolders[state.currentPath.id] = true; + state.hasMoreDriveFiles[state.currentPath.id] = true; }, setRecents: (state: StorageState, action: PayloadAction) => { state.recents = action.payload; diff --git a/src/app/store/slices/storage/storage.model.ts b/src/app/store/slices/storage/storage.model.ts index 173f76e57..b1c1dcba2 100644 --- a/src/app/store/slices/storage/storage.model.ts +++ b/src/app/store/slices/storage/storage.model.ts @@ -15,8 +15,8 @@ export interface StorageState { moveDialogLevels: Record; levelsFoldersLength: Record; levelsFilesLength: Record; - hasMoreDriveFolders: boolean; - hasMoreDriveFiles: boolean; + hasMoreDriveFolders: Record; + hasMoreDriveFiles: Record; recents: DriveItemData[]; isLoadingRecents: boolean; isLoadingDeleted: boolean; diff --git a/src/app/store/slices/storage/storage.selectors.ts b/src/app/store/slices/storage/storage.selectors.ts index 01029a3e4..be0a2cbc4 100644 --- a/src/app/store/slices/storage/storage.selectors.ts +++ b/src/app/store/slices/storage/storage.selectors.ts @@ -37,10 +37,10 @@ const storageSelectors = { return this.levelItems(state)(currentFolderId); }, hasMoreFiles(state: RootState): boolean { - return state.storage.hasMoreDriveFiles; + return state.storage.hasMoreDriveFiles[state.storage.currentPath.id]; }, hasMoreFolders(state: RootState): boolean { - return state.storage.hasMoreDriveFolders; + return state.storage.hasMoreDriveFolders[state.storage.currentPath.id]; }, levelItems(state: RootState): (folderId: number) => DriveItemData[] { return (folderId) => state.storage.levels[folderId] || []; diff --git a/src/app/store/slices/storage/storage.thunks/fetchFolderContentThunk.ts b/src/app/store/slices/storage/storage.thunks/fetchFolderContentThunk.ts index 7583c0382..5dbaf7575 100644 --- a/src/app/store/slices/storage/storage.thunks/fetchFolderContentThunk.ts +++ b/src/app/store/slices/storage/storage.thunks/fetchFolderContentThunk.ts @@ -20,8 +20,9 @@ export const fetchPaginatedFolderContentThunk = createAsyncThunk { const storageState = getState().storage; - const hasMoreDriveFolders = storageState.hasMoreDriveFolders; - const hasMoreDriveFiles = storageState.hasMoreDriveFiles; + const hasMoreDriveFolders = storageState.hasMoreDriveFolders[folderId] ?? true; + const hasMoreDriveFiles = storageState.hasMoreDriveFiles[folderId] ?? true; + const foldersOffset = (storageState.levels[folderId] ?? []).filter(filterFolderItems).length; const filesOffset = (storageState.levels[folderId] ?? []).filter(filterFilesItems).length; const driveItemsSort = storageState.driveItemsSort; @@ -63,10 +64,10 @@ export const fetchPaginatedFolderContentThunk = createAsyncThunk( 'storage/fetchSortedFolderContentThunk', async (folderId, { getState, dispatch }) => { - dispatch(storageActions.setHasMoreDriveFolders(true)); - dispatch(storageActions.setHasMoreDriveFiles(true)); + dispatch(storageActions.setHasMoreDriveFolders({ folderId, status: true })); + dispatch(storageActions.setHasMoreDriveFiles({ folderId, status: true })); const storageState = getState().storage; const hasMoreDriveFolders = storageState.hasMoreDriveFolders; diff --git a/src/app/store/slices/storage/storage.thunks/goToFolderThunk.ts b/src/app/store/slices/storage/storage.thunks/goToFolderThunk.ts index 6df34b3aa..07a29849c 100644 --- a/src/app/store/slices/storage/storage.thunks/goToFolderThunk.ts +++ b/src/app/store/slices/storage/storage.thunks/goToFolderThunk.ts @@ -28,6 +28,9 @@ export const goToFolderThunk = createAsyncThunk Date: Mon, 4 Mar 2024 11:44:54 -0400 Subject: [PATCH 03/24] fix: add ready-for-preview instead of preview --- .github/workflows/deploy-pr-preview.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-pr-preview.yaml b/.github/workflows/deploy-pr-preview.yaml index d3056416a..778a6854b 100644 --- a/.github/workflows/deploy-pr-preview.yaml +++ b/.github/workflows/deploy-pr-preview.yaml @@ -6,14 +6,14 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Check Out Repo + uses: actions/checkout@v2 - run: echo "registry=https://registry.yarnpkg.com/" > .npmrc - run: echo "@internxt:registry=https://npm.pkg.github.com" >> .npmrc # You cannot read packages from other private repos with GITHUB_TOKEN # You have to use a PAT instead https://github.com/actions/setup-node/issues/49 - run: echo //npm.pkg.github.com/:_authToken=${{ secrets.PERSONAL_ACCESS_TOKEN }} >> .npmrc - run: echo "always-auth=true" >> .npmrc - - name: Login to DockerHub uses: docker/login-action@v1 with: @@ -28,7 +28,7 @@ jobs: file: ./infrastructure/preview.Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}-dev:preview-${{ github.event.number }}-${{ github.event.pull_request.head.sha }} - add_preview_label: + add_ready_for_preview_label: if: github.event.pull_request.draft == false runs-on: ubuntu-latest needs: build @@ -36,9 +36,9 @@ jobs: - uses: actions-ecosystem/action-add-labels@v1 with: labels: | - preview + ready-for-preview dispatch_update_deployment: - needs: add_preview_label + needs: add_ready_for_preview_label runs-on: ubuntu-latest if: ${{ contains(github.event.pull_request.labels.*.name, 'deployed') }} steps: @@ -73,8 +73,9 @@ jobs: } } dispatch_check_deployment: - needs: add_preview_label + needs: add_ready_for_preview_label runs-on: ubuntu-latest + if: ${{ contains(github.event.pull_request.labels.*.name, 'preview') }} steps: - name: Dispatch Check Preview Repository Command uses: myrotvorets/trigger-repository-dispatch-action@1.0.0 From af9870d725ed04ad4e6998a3ac344bd6bf810c2e Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+masterprog-cmd@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:17:18 +0100 Subject: [PATCH 04/24] feat: add spring banner --- src/app/banners/BannerWrapper.tsx | 7 ++- src/app/banners/FeaturesBanner.tsx | 90 ++++++++++++++---------------- src/app/i18n/locales/de.json | 25 +++++---- src/app/i18n/locales/en.json | 20 +++---- src/app/i18n/locales/es.json | 20 +++---- src/app/i18n/locales/fr.json | 20 +++---- src/app/i18n/locales/it.json | 20 +++---- src/app/i18n/locales/ru.json | 22 ++++---- src/app/i18n/locales/zh.json | 24 ++++---- 9 files changed, 123 insertions(+), 125 deletions(-) diff --git a/src/app/banners/BannerWrapper.tsx b/src/app/banners/BannerWrapper.tsx index d157ba5ad..0f08c4db8 100644 --- a/src/app/banners/BannerWrapper.tsx +++ b/src/app/banners/BannerWrapper.tsx @@ -8,9 +8,10 @@ import { useSelector } from 'react-redux'; import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; import Banner from './Banner'; +import FeaturesBanner from './FeaturesBanner'; -const SHOW_BANNER_COOKIE_NAME = 'show_lifetime_soft_banner'; -const OFFER_OFF_DAY = new Date('2024-03-04'); +const SHOW_BANNER_COOKIE_NAME = 'show_spring_banner'; +const OFFER_OFF_DAY = new Date('2024-03-18'); const BannerWrapper = (): JSX.Element => { const [showBanner, setShowBanner] = useState(false); @@ -41,7 +42,7 @@ const BannerWrapper = (): JSX.Element => { } } - return ; + return ; }; export default BannerWrapper; diff --git a/src/app/banners/FeaturesBanner.tsx b/src/app/banners/FeaturesBanner.tsx index 782e64e40..0f00682a6 100644 --- a/src/app/banners/FeaturesBanner.tsx +++ b/src/app/banners/FeaturesBanner.tsx @@ -1,74 +1,70 @@ -import { CheckCircle, CircleWavyCheck, X } from '@phosphor-icons/react'; -import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; +import { CheckCircle, Flower, X } from '@phosphor-icons/react'; +import { useTranslation } from 'react-i18next'; const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose: () => void }): JSX.Element => { - const { translate } = useTranslationContext(); + const { t } = useTranslation(); - const features = [ - { - title: translate('featuresBanner.features.discount'), - }, - { - title: translate('featuresBanner.features.safeCloud'), - }, - { - title: translate('featuresBanner.features.openSource'), - }, - { - title: translate('featuresBanner.features.endToEnd'), - }, - { - title: translate('featuresBanner.features.unauthorized'), - }, - { - title: translate('featuresBanner.features.offerEnds'), - }, - ]; + const features = t('featuresBanner.features', { returnObjects: true }) as string[]; + + const handleOnClick = () => { + window.open('https://internxt.com/pricing', '_blank', 'nofollow noreferrer'); + }; return ( + //Background
+ {/* Banner */}
- -
-
-
-

{translate('featuresBanner.header')}

-

{translate('featuresBanner.title')}

+
+
+
+

{t('featuresBanner.label')}

-
+

+ {t('featuresBanner.title')} +

+ +
-
- -

{translate('lifetimeBanner.guarantee')}

+
+ +

{t('featuresBanner.guarantee')}

+ +

{t('featuresBanner.lastCta')}

-
- {features.map((item) => ( -
- -

{item.title}

+
+
+
+ {features.map((card) => ( +
+ +

{card}

+
+ ))}
- ))} +
); }; + export default FeaturesBanner; diff --git a/src/app/i18n/locales/de.json b/src/app/i18n/locales/de.json index 199d16982..ca0a9e9f7 100644 --- a/src/app/i18n/locales/de.json +++ b/src/app/i18n/locales/de.json @@ -4,18 +4,19 @@ "logOut": "Log-out" }, "featuresBanner": { - "header": "Spare 70%!", - "title": "Bereiten Sie sich auf die Datenschutzwoche vor", - "cta": "Plan auswählen", - "guarantee": "30-tägige Geld-zurück-Garantie", - "features": { - "discount": "Sparen Sie bei monatlichen und jährlichen Plänen", - "safeCloud": "Sicherer und geschützter Cloud-Speicher", - "openSource": "Open Source und transparent", - "endToEnd": "Ende-zu-Ende-verschlüsselte Übertragungen", - "unauthorized": "Kein unberechtigter Zugriff", - "offerEnds": "Angebot endet am 29. Februar" - } + "label": "Spare 75%", + "title": "Starten Sie Ihre digitale Frühjahrsputz", + "cta": "Wähle einen Plan", + "guarantee": "30 Tage Geld-zurück-Garantie", + "lastCta": "*Angebot gilt für kostenlose Konten oder neue Kunden", + "features": [ + "Sparen Sie bei allen unseren Plänen", + "Sicherer Cloud-Speicher", + "Backup privater Dateien und Fotos", + "End-to-end verschlüsselte Übertragungen", + "Kein unbefugter Zugriff", + "Angebot endet am 18. März" + ] }, "valentinesBanner": { "title": "Liebe ohne grenzen!", diff --git a/src/app/i18n/locales/en.json b/src/app/i18n/locales/en.json index 415a6a223..c4e31c8f9 100644 --- a/src/app/i18n/locales/en.json +++ b/src/app/i18n/locales/en.json @@ -4,19 +4,19 @@ "logOut": "Log Out" }, "featuresBanner": { - "header": "Save 70%!", - "title": "Get ready for Data Privacy Week", + "label": "Save 75%", + "title": "Start your digital spring cleaning", "cta": "Choose plan", "guarantee": "30-day money-back guarantee", "lastCta": "*Offer is for free accounts or new customers", - "features": { - "discount": "Save on monthly & annual plans", - "safeCloud": "Safe and secure cloud storage", - "openSource": "Open source and transparent", - "endToEnd": "End-to-end encrypted transfers", - "unauthorized": "No unauthorized access", - "offerEnds": "Offer ends February 29th" - } + "features": [ + "Save on all of our plans", + "Safe and secure cloud storage", + "Open source and transparent", + "End-to-end encrypted transfers", + "No unauthorized access", + "Offer ends March 18th" + ] }, "valentinesBanner": { "title": "Love without limits!", diff --git a/src/app/i18n/locales/es.json b/src/app/i18n/locales/es.json index d2fc32040..4eeb6f068 100644 --- a/src/app/i18n/locales/es.json +++ b/src/app/i18n/locales/es.json @@ -4,19 +4,19 @@ "logOut": "Cerrar sesión" }, "featuresBanner": { - "header": "¡Ahorra un 70%!", - "title": "Prepárate para la Semana de la Privacidad de Datos", + "label": "Ahorra un 75%", + "title": "Comienza a ahorrar esta primavera", "cta": "Elige un plan", "guarantee": "Garantía de devolución de 30 días", "lastCta": "*La oferta es para cuentas gratuitas o nuevos clientes", - "features": { - "discount": "Ahorra en suscripciones mensuales y anuales", - "safeCloud": "Almacenamiento seguro en la nube", - "openSource": "Código abierto y transparente", - "endToEnd": "Transferencias cifradas de extremo a extremo", - "unauthorized": "Sin acceso no autorizado", - "offerEnds": "La oferta finaliza el 29 de febrero" - } + "features": [ + "Ahorra en todos nuestros planes", + "Almacenamiento seguro en la nube", + "Código abierto y auditado", + "Transferencias cifradas de extremo a extremo", + "Sin accesos no autorizados", + "La oferta finaliza el 18 de marzo" + ] }, "valentinesBanner": { "title": "¡Amor sin limites!", diff --git a/src/app/i18n/locales/fr.json b/src/app/i18n/locales/fr.json index f520eb3de..f7d8e10e7 100644 --- a/src/app/i18n/locales/fr.json +++ b/src/app/i18n/locales/fr.json @@ -4,19 +4,19 @@ "logOut": "Déconnexion" }, "featuresBanner": { - "header": "Économisez 70% !", - "title": "Préparez-vous pour la Semaine de la Protection des Données", + "label": "Économisez 75%", + "title": "Profitez de votre offre de printemps", "cta": "Choisissez un plan", "guarantee": "Garantie de remboursement de 30 jours", "lastCta": "*L'offre est valable pour les comptes gratuits ou les nouveaux clients", - "features": { - "discount": "Économisez sur les plans mensuels et annuels", - "safeCloud": "Stockage cloud sûr et sécurisé", - "openSource": "Open source et transparent", - "endToEnd": "Transferts chiffrés de bout en bout", - "unauthorized": "Aucun accès non autorisé", - "offerEnds": "L'offre se termine le 29 février" - } + "features": [ + "Économisez sur tous nos plans", + "Stockage cloud sûr et sécurisé", + "Open source et transparent", + "Transferts chiffrés de bout en bout", + "Aucun accès non autorisé", + "L'offre se termine le 18 mars" + ] }, "valentinesBanner": { "title": "L'amour sans limites !", diff --git a/src/app/i18n/locales/it.json b/src/app/i18n/locales/it.json index 31d287ded..54b64f9b8 100644 --- a/src/app/i18n/locales/it.json +++ b/src/app/i18n/locales/it.json @@ -9,19 +9,19 @@ "cta": "Get the deal" }, "featuresBanner": { - "header": "Risparmia il 70%!", - "title": "Preparati per la Settimana della Privacy dei Dati", + "label": "Risparmia il 75%", + "title": "Inizia la tua pulizia digitale di primavera", "cta": "Scegli un piano", "guarantee": "Garanzia di rimborso entro 30 giorni", "lastCta": "*L'offerta è valida solo per account gratuiti o nuovi clienti", - "features": { - "discount": "Risparmia su piani mensili e annuali", - "safeCloud": "Archiviazione cloud sicura e protetta", - "openSource": "Open source e trasparente", - "endToEnd": "Trasferimenti crittografati end-to-end", - "unauthorized": "Nessun accesso non autorizzato", - "offerEnds": "L'offerta termina il 29 febbraio" - } + "features": [ + "Risparmia su tutti i nostri piani", + "Archiviazione cloud sicura e protetta", + "Open source e trasparente", + "Trasferimenti crittografati end-to-end", + "Nessun accesso non autorizzato", + "L'offerta termina il 18 marzo" + ] }, "lifetimeBanner": { "label": "Cercate uno spazio sicuro per i vostri dati per tutta la vita?", diff --git a/src/app/i18n/locales/ru.json b/src/app/i18n/locales/ru.json index ed04b362d..a65073863 100644 --- a/src/app/i18n/locales/ru.json +++ b/src/app/i18n/locales/ru.json @@ -9,19 +9,19 @@ "cta": "Получить скидку" }, "featuresBanner": { - "label": "Сэкономьте 70%!", - "title": "Приготовьтесь к Неделе конфиденциальности данных", + "label": "Скидка 75%", + "title": "Получите своё весеннее предложение", "cta": "Выбрать план", - "guarantee": "30-дневная гарантия возврата денег", + "guarantee": "Гарантия возврата денег в течение 30 дней", "lastCta": "*Предложение действует только для бесплатных аккаунтов или новых клиентов", - "features": { - "discount": "Скидка на годовые и месячные планы", - "safeCloud": "Надежное и безопасное облачное хранилище", - "openSource": "Открытый исходный код и прозрачность", - "endToEnd": "Передача данных в зашифрованном виде", - "unauthorized": "Отсутствие несанкционированного доступа", - "offerEnds": "Предложение действует до 29 февраля" - } + "features": [ + "Экономьте на всех наших планах", + "Надежное и безопасное облачное хранилище", + "Открытый исходный код и прозрачность", + "Передача данных в зашифрованном виде", + "Отсутствие несанкционированного доступа", + "Предложение заканчивается 18 марта" + ] }, "lifetimeBanner": { "label": "Ищите надежное хранилище данных на всю жизнь?", diff --git a/src/app/i18n/locales/zh.json b/src/app/i18n/locales/zh.json index 66d8e96a3..137e2b783 100644 --- a/src/app/i18n/locales/zh.json +++ b/src/app/i18n/locales/zh.json @@ -4,19 +4,19 @@ "logOut": "登出" }, "featuresBanner": { - "header": "省 70%!", - "title": "为数据隐私周做好准备", + "label": "省75%", + "title": "开始您的数字春季大扫除", "cta": "选择计划", - "guarantee": "30 天内退款保证", - "lastCta": "*此优惠仅适用于免费账户或新客户", - "features": { - "discount": "在月度和年度计划上节省", - "safeCloud": "安全可靠的云存储", - "openSource": "开源和透明", - "endToEnd": "端到端加密传输", - "unauthorized": "无未经授权访问", - "offerEnds": "优惠截止日期为2月29日" - } + "guarantee": "30天退款保证", + "lastCta": "*优惠仅适用于免费帐户或新客户", + "features": [ + "在我们所有的计划上节省", + "安全可靠的云存储", + "开源和透明", + "端到端加密传输", + "没有未经授权的访问", + "优惠截至日期为3月18日" + ] }, "valentinesBanner": { "title": "爱无极限!", From 4090ee8a32629fbc6f0ca94a7ff137d8a9f3983b Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+masterprog-cmd@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:19:55 +0100 Subject: [PATCH 05/24] fix: remove unused import --- src/app/banners/BannerWrapper.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/banners/BannerWrapper.tsx b/src/app/banners/BannerWrapper.tsx index 0f08c4db8..86e8ab3f1 100644 --- a/src/app/banners/BannerWrapper.tsx +++ b/src/app/banners/BannerWrapper.tsx @@ -7,7 +7,6 @@ import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; -import Banner from './Banner'; import FeaturesBanner from './FeaturesBanner'; const SHOW_BANNER_COOKIE_NAME = 'show_spring_banner'; From da4ac46043d93fbb7427484031049f4e8bf82b66 Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+masterprog-cmd@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:25:14 +0100 Subject: [PATCH 06/24] feat: use custom hook (translations) --- src/app/banners/FeaturesBanner.tsx | 16 ++++++++-------- src/app/i18n/provider/TranslationProvider.tsx | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/banners/FeaturesBanner.tsx b/src/app/banners/FeaturesBanner.tsx index 0f00682a6..3be02ed85 100644 --- a/src/app/banners/FeaturesBanner.tsx +++ b/src/app/banners/FeaturesBanner.tsx @@ -1,10 +1,10 @@ import { CheckCircle, Flower, X } from '@phosphor-icons/react'; -import { useTranslation } from 'react-i18next'; +import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose: () => void }): JSX.Element => { - const { t } = useTranslation(); + const { translate } = useTranslationContext(); - const features = t('featuresBanner.features', { returnObjects: true }) as string[]; + const features = translate('featuresBanner.features', { returnObjects: true }) as string[]; const handleOnClick = () => { window.open('https://internxt.com/pricing', '_blank', 'nofollow noreferrer'); @@ -28,10 +28,10 @@ const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose:
-

{t('featuresBanner.label')}

+

{translate('featuresBanner.label')}

- {t('featuresBanner.title')} + {translate('featuresBanner.title')}

@@ -39,14 +39,14 @@ const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose: onClick={handleOnClick} className="flex w-max items-center rounded-lg bg-primary px-5 py-3 text-lg font-medium text-white hover:bg-primary-dark" > - {t('featuresBanner.cta')} + {translate('featuresBanner.cta')}
-

{t('featuresBanner.guarantee')}

+

{translate('featuresBanner.guarantee')}

-

{t('featuresBanner.lastCta')}

+

{translate('featuresBanner.lastCta')}

diff --git a/src/app/i18n/provider/TranslationProvider.tsx b/src/app/i18n/provider/TranslationProvider.tsx index 573534e8c..30192e10a 100644 --- a/src/app/i18n/provider/TranslationProvider.tsx +++ b/src/app/i18n/provider/TranslationProvider.tsx @@ -2,7 +2,7 @@ import React, { createContext, useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; interface TranslationContextProps { - translate: (key: string, props?: Record) => string; + translate: (key: string, props?: Record) => string | string[] | undefined; } const TranslationContext = createContext({ translate: () => '' }); From 6ee23e572f7156bf784e2131665f1531aafc74eb Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+masterprog-cmd@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:30:08 +0100 Subject: [PATCH 07/24] fix: error in translations provider --- src/app/banners/FeaturesBanner.tsx | 2 +- src/app/i18n/provider/TranslationProvider.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/banners/FeaturesBanner.tsx b/src/app/banners/FeaturesBanner.tsx index 3be02ed85..36219ab1c 100644 --- a/src/app/banners/FeaturesBanner.tsx +++ b/src/app/banners/FeaturesBanner.tsx @@ -7,7 +7,7 @@ const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose: const features = translate('featuresBanner.features', { returnObjects: true }) as string[]; const handleOnClick = () => { - window.open('https://internxt.com/pricing', '_blank', 'nofollow noreferrer'); + window.open('https://internxt.com/pricing', '_blank', 'noopener noreferrer'); }; return ( diff --git a/src/app/i18n/provider/TranslationProvider.tsx b/src/app/i18n/provider/TranslationProvider.tsx index 30192e10a..af02d67b0 100644 --- a/src/app/i18n/provider/TranslationProvider.tsx +++ b/src/app/i18n/provider/TranslationProvider.tsx @@ -2,7 +2,7 @@ import React, { createContext, useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; interface TranslationContextProps { - translate: (key: string, props?: Record) => string | string[] | undefined; + translate: (key: string, props?: Record) => string | string[] | any; } const TranslationContext = createContext({ translate: () => '' }); From 2b7b19f18aea1e39255dcbbd5925ec1d10f5ff22 Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+masterprog-cmd@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:43:55 +0100 Subject: [PATCH 08/24] feat: adding space between components --- src/app/banners/FeaturesBanner.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/banners/FeaturesBanner.tsx b/src/app/banners/FeaturesBanner.tsx index 36219ab1c..3b41523c9 100644 --- a/src/app/banners/FeaturesBanner.tsx +++ b/src/app/banners/FeaturesBanner.tsx @@ -25,7 +25,7 @@ const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose: -
+

{translate('featuresBanner.label')}

From 858ef89fe4b5b7e7417264d0b719f537fedd8f69 Mon Sep 17 00:00:00 2001 From: inxtci Date: Tue, 5 Mar 2024 08:38:20 +0000 Subject: [PATCH 09/24] [skip ci] Bump version v1.0.249 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bf9d1412e..3ef3405aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drive-web", - "version": "v1.0.248", + "version": "v1.0.249", "private": true, "dependencies": { "@headlessui/react": "1.7.5", From 7cdba8515d9e3a4ee9badd574946cee60a05d2eb Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+masterprog-cmd@users.noreply.github.com> Date: Tue, 5 Mar 2024 11:19:11 +0100 Subject: [PATCH 10/24] feat: create new function to get arrays from json --- src/app/banners/FeaturesBanner.tsx | 4 ++-- src/app/i18n/provider/TranslationProvider.tsx | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/app/banners/FeaturesBanner.tsx b/src/app/banners/FeaturesBanner.tsx index 3b41523c9..3183ce2e7 100644 --- a/src/app/banners/FeaturesBanner.tsx +++ b/src/app/banners/FeaturesBanner.tsx @@ -2,9 +2,9 @@ import { CheckCircle, Flower, X } from '@phosphor-icons/react'; import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose: () => void }): JSX.Element => { - const { translate } = useTranslationContext(); + const { translate, translateList } = useTranslationContext(); - const features = translate('featuresBanner.features', { returnObjects: true }) as string[]; + const features = translateList('featuresBanner.features'); const handleOnClick = () => { window.open('https://internxt.com/pricing', '_blank', 'noopener noreferrer'); diff --git a/src/app/i18n/provider/TranslationProvider.tsx b/src/app/i18n/provider/TranslationProvider.tsx index af02d67b0..1a91f3e1c 100644 --- a/src/app/i18n/provider/TranslationProvider.tsx +++ b/src/app/i18n/provider/TranslationProvider.tsx @@ -2,17 +2,21 @@ import React, { createContext, useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; interface TranslationContextProps { - translate: (key: string, props?: Record) => string | string[] | any; + translate: (key: string, props?: Record) => string; + translateList: (key: string) => string[]; } -const TranslationContext = createContext({ translate: () => '' }); +const TranslationContext = createContext({ translate: () => '', translateList: () => [] }); interface TranslationProviderProps { children: React.ReactNode; } export const TranslationProvider: React.FC = ({ children }) => { const { t } = useTranslation(); - const value = useMemo(() => ({ translate: t }), [t]); + + const translateList = (key: string) => t(key, { returnObjects: true }) as string[]; + + const value = useMemo(() => ({ translate: t, translateList }), [t]); return {children}; }; From 27673b07f6311558f6c8c320de68506a21315349 Mon Sep 17 00:00:00 2001 From: inxtci Date: Tue, 5 Mar 2024 11:30:35 +0000 Subject: [PATCH 11/24] [skip ci] Bump version v1.0.250 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3ef3405aa..3d6bd3609 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drive-web", - "version": "v1.0.249", + "version": "v1.0.250", "private": true, "dependencies": { "@headlessui/react": "1.7.5", From da490c0995617c053e346930a0866b1e308803d0 Mon Sep 17 00:00:00 2001 From: Ramon Candel Date: Wed, 6 Mar 2024 10:34:31 +0100 Subject: [PATCH 12/24] Added allowance offset for tutorial upload file button --- .../DriveExplorer/DriveExplorer.tsx | 93 ++++++++++--------- .../components/DriveTopBarActions.tsx | 18 ++-- .../components/Tutorial/signUpSteps.tsx | 5 +- 3 files changed, 61 insertions(+), 55 deletions(-) diff --git a/src/app/drive/components/DriveExplorer/DriveExplorer.tsx b/src/app/drive/components/DriveExplorer/DriveExplorer.tsx index 5d337941d..477218bf5 100644 --- a/src/app/drive/components/DriveExplorer/DriveExplorer.tsx +++ b/src/app/drive/components/DriveExplorer/DriveExplorer.tsx @@ -1,72 +1,72 @@ -import { createRef, useState, RefObject, useEffect, useRef, LegacyRef, useCallback } from 'react'; +import { ArrowFatUp, CaretDown, FileArrowUp, FolderSimplePlus, Plus, Trash, UploadSimple } from '@phosphor-icons/react'; +import { LegacyRef, RefObject, createRef, useCallback, useEffect, useRef, useState } from 'react'; import { connect } from 'react-redux'; -import { Trash, UploadSimple, FolderSimplePlus, FileArrowUp, Plus, CaretDown, ArrowFatUp } from '@phosphor-icons/react'; -import { NativeTypes } from 'react-dnd-html5-backend'; import { ConnectDropTarget, DropTarget, DropTargetCollector, DropTargetSpec } from 'react-dnd'; +import { NativeTypes } from 'react-dnd-html5-backend'; -import DriveExplorerList from './DriveExplorerList/DriveExplorerList'; -import DriveExplorerGrid from './DriveExplorerGrid/DriveExplorerGrid'; import folderEmptyImage from 'assets/icons/light/folder-open.svg'; -import Empty from '../../../shared/components/Empty/Empty'; import { transformDraggedItems } from '../../../core/services/drag-and-drop.service'; -import { StorageFilters } from '../../../store/slices/storage/storage.model'; -import { AppDispatch, RootState } from '../../../store'; import { Workspace } from '../../../core/types'; +import Empty from '../../../shared/components/Empty/Empty'; +import { AppDispatch, RootState } from '../../../store'; +import { StorageFilters } from '../../../store/slices/storage/storage.model'; +import DriveExplorerGrid from './DriveExplorerGrid/DriveExplorerGrid'; +import DriveExplorerList from './DriveExplorerList/DriveExplorerList'; -import './DriveExplorer.scss'; -import storageThunks from '../../../store/slices/storage/storage.thunks'; +import { Menu, Transition } from '@headlessui/react'; +import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; +import { useHotkeys } from 'react-hotkeys-hook'; +import moveItemsToTrash from 'use_cases/trash/move-items-to-trash'; +import { getTrashPaginated } from '../../../../use_cases/trash/get_trash'; +import BannerWrapper from '../../../banners/BannerWrapper'; import deviceService from '../../../core/services/device.service'; -import { storageActions } from '../../../store/slices/storage'; -import { uiActions } from '../../../store/slices/ui'; +import errorService from '../../../core/services/error.service'; +import localStorageService, { STORAGE_KEYS } from '../../../core/services/local-storage.service'; +import RealtimeService, { SOCKET_EVENTS } from '../../../core/services/socket.service'; +import ClearTrashDialog from '../../../drive/components/ClearTrashDialog/ClearTrashDialog'; import CreateFolderDialog from '../../../drive/components/CreateFolderDialog/CreateFolderDialog'; import DeleteItemsDialog from '../../../drive/components/DeleteItemsDialog/DeleteItemsDialog'; -import ClearTrashDialog from '../../../drive/components/ClearTrashDialog/ClearTrashDialog'; -import UploadItemsFailsDialog from '../UploadItemsFailsDialog/UploadItemsFailsDialog'; -import Button from '../../../shared/components/Button/Button'; -import storageSelectors from '../../../store/slices/storage/storage.selectors'; -import { planSelectors } from '../../../store/slices/plan'; -import { DriveItemData, FileViewMode, FolderPath } from '../../types'; -import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; -import iconService from '../../services/icon.service'; -import MoveItemsDialog from '../MoveItemsDialog/MoveItemsDialog'; -import { IRoot } from '../../../store/slices/storage/storage.thunks/uploadFolderThunk'; import { transformInputFilesToJSON, transformJsonFilesToItems, } from '../../../drive/services/folder.service/uploadFolderInput.service'; -import { useAppDispatch, useAppSelector } from '../../../store/hooks'; +import { useTranslationContext } from '../../../i18n/provider/TranslationProvider'; import notificationsService, { ToastType } from '../../../notifications/services/notifications.service'; +import { AdvancedSharedItem } from '../../../share/types'; +import Button from '../../../shared/components/Button/Button'; +import SkinSkeletonItem from '../../../shared/components/List/SkinSketelonItem'; +import { Tutorial } from '../../../shared/components/Tutorial/Tutorial'; +import { getSignUpSteps } from '../../../shared/components/Tutorial/signUpSteps'; +import { useAppDispatch, useAppSelector } from '../../../store/hooks'; +import { planSelectors } from '../../../store/slices/plan'; +import { storageActions } from '../../../store/slices/storage'; +import storageSelectors from '../../../store/slices/storage/storage.selectors'; +import storageThunks from '../../../store/slices/storage/storage.thunks'; +import { fetchPaginatedFolderContentThunk } from '../../../store/slices/storage/storage.thunks/fetchFolderContentThunk'; +import { fetchSortedFolderContentThunk } from '../../../store/slices/storage/storage.thunks/fetchSortedFolderContentThunk'; import { handleRepeatedUploadingFiles, handleRepeatedUploadingFolders, } from '../../../store/slices/storage/storage.thunks/renameItemsThunk'; -import NameCollisionContainer from '../NameCollisionDialog/NameCollisionContainer'; -import { useTranslationContext } from '../../../i18n/provider/TranslationProvider'; -import { Menu, Transition } from '@headlessui/react'; -import { useHotkeys } from 'react-hotkeys-hook'; -import { getTrashPaginated } from '../../../../use_cases/trash/get_trash'; -import { Tutorial } from '../../../shared/components/Tutorial/Tutorial'; +import { IRoot } from '../../../store/slices/storage/storage.thunks/uploadFolderThunk'; +import { uiActions } from '../../../store/slices/ui'; import { userSelectors } from '../../../store/slices/user'; -import localStorageService, { STORAGE_KEYS } from '../../../core/services/local-storage.service'; -import { getSignUpSteps } from '../../../shared/components/Tutorial/signUpSteps'; import { useTaskManagerGetNotifications } from '../../../tasks/hooks'; import { TaskStatus } from '../../../tasks/types'; -import SkinSkeletonItem from '../../../shared/components/List/SkinSketelonItem'; -import errorService from '../../../core/services/error.service'; -import { fetchPaginatedFolderContentThunk } from '../../../store/slices/storage/storage.thunks/fetchFolderContentThunk'; -import RealtimeService, { SOCKET_EVENTS } from '../../../core/services/socket.service'; +import iconService from '../../services/icon.service'; +import { DriveItemData, FileViewMode, FolderPath } from '../../types'; +import EditItemNameDialog from '../EditItemNameDialog/EditItemNameDialog'; +import ItemDetailsDialog from '../ItemDetailsDialog/ItemDetailsDialog'; +import MoveItemsDialog from '../MoveItemsDialog/MoveItemsDialog'; +import NameCollisionContainer from '../NameCollisionDialog/NameCollisionContainer'; import ShareDialog from '../ShareDialog/ShareDialog'; -import { fetchSortedFolderContentThunk } from '../../../store/slices/storage/storage.thunks/fetchSortedFolderContentThunk'; +import StopSharingAndMoveToTrashDialogWrapper from '../StopSharingAndMoveToTrashDialogWrapper/StopSharingAndMoveToTrashDialogWrapper'; +import UploadItemsFailsDialog from '../UploadItemsFailsDialog/UploadItemsFailsDialog'; import WarningMessageWrapper from '../WarningMessage/WarningMessageWrapper'; -import EditItemNameDialog from '../EditItemNameDialog/EditItemNameDialog'; +import './DriveExplorer.scss'; import { DriveTopBarItems } from './DriveTopBarItems'; -import ItemDetailsDialog from '../ItemDetailsDialog/ItemDetailsDialog'; import DriveTopBarActions from './components/DriveTopBarActions'; -import { AdvancedSharedItem } from '../../../share/types'; -import moveItemsToTrash from 'use_cases/trash/move-items-to-trash'; -import StopSharingAndMoveToTrashDialogWrapper from '../StopSharingAndMoveToTrashDialogWrapper/StopSharingAndMoveToTrashDialogWrapper'; -import BannerWrapper from '../../../banners/BannerWrapper'; const TRASH_PAGINATION_OFFSET = 50; const UPLOAD_ITEMS_LIMIT = 1000; @@ -170,11 +170,12 @@ const DriveExplorer = (props: DriveExplorerProps): JSX.Element => { // ONBOARDING TUTORIAL STATES const [currentTutorialStep, setCurrentTutorialStep] = useState(0); const [showSecondTutorialStep, setShowSecondTutorialStep] = useState(false); - const stepOneTutorialRef = useRef(null); + const uploadFileButtonRef = useRef(null); const isSignUpTutorialCompleted = localStorageService.hasCompletedTutorial(user?.userId); const successNotifications = useTaskManagerGetNotifications({ status: [TaskStatus.Success], }); + const divRef = useRef(null); const showTutorial = useAppSelector(userSelectors.hasSignedToday) && @@ -188,7 +189,8 @@ const DriveExplorer = (props: DriveExplorerProps): JSX.Element => { }, 0); passToNextStep(); }, - stepOneTutorialRef, + stepOneTutorialRef: uploadFileButtonRef, + offset: hasAnyItemSelected ? { x: divRef?.current?.offsetWidth ?? 0, y: 0 } : { x: 0, y: 0 }, }, { onNextStepClicked: () => { @@ -744,7 +746,7 @@ const DriveExplorer = (props: DriveExplorerProps): JSX.Element => { }} { currentFolderId={currentFolderId} setEditNameItem={setEditNameItem} hasItems={hasItems} + driveActionsRef={divRef} />
{isTrash && ( diff --git a/src/app/drive/components/DriveExplorer/components/DriveTopBarActions.tsx b/src/app/drive/components/DriveExplorer/components/DriveTopBarActions.tsx index 02540158f..dd91a371c 100644 --- a/src/app/drive/components/DriveExplorer/components/DriveTopBarActions.tsx +++ b/src/app/drive/components/DriveExplorer/components/DriveTopBarActions.tsx @@ -9,25 +9,25 @@ import { Users, } from '@phosphor-icons/react'; import { ReactComponent as MoveActionIcon } from 'assets/icons/move.svg'; +import moveItemsToTrash from 'use_cases/trash/move-items-to-trash'; +import errorService from '../../../../core/services/error.service'; +import { DriveItemData, DriveItemDetails, FileViewMode } from '../../../../drive/types'; +import { useTranslationContext } from '../../../../i18n/provider/TranslationProvider'; +import shareService from '../../../../share/services/share.service'; import Button from '../../../../shared/components/Button/Button'; import Dropdown from '../../../../shared/components/Dropdown'; import TooltipElement, { DELAY_SHOW_MS } from '../../../../shared/components/Tooltip/Tooltip'; -import { useTranslationContext } from '../../../../i18n/provider/TranslationProvider'; import { useAppDispatch, useAppSelector } from '../../../../store/hooks'; import { storageActions } from '../../../../store/slices/storage'; +import storageThunks from '../../../../store/slices/storage/storage.thunks'; import { uiActions } from '../../../../store/slices/ui'; +import useDriveItemStoreProps from '../DriveExplorerItem/hooks/useDriveStoreProps'; import { contextMenuDriveFolderNotSharedLink, contextMenuDriveFolderShared, contextMenuDriveItemShared, contextMenuDriveNotSharedLink, } from '../DriveExplorerList/DriveItemContextMenu'; -import moveItemsToTrash from 'use_cases/trash/move-items-to-trash'; -import errorService from '../../../../core/services/error.service'; -import storageThunks from '../../../../store/slices/storage/storage.thunks'; -import shareService from '../../../../share/services/share.service'; -import { DriveItemData, DriveItemDetails, FileViewMode } from '../../../../drive/types'; -import useDriveItemStoreProps from '../DriveExplorerItem/hooks/useDriveStoreProps'; const DriveTopBarActions = ({ selectedItems, @@ -36,6 +36,7 @@ const DriveTopBarActions = ({ hasAnyItemSelected, isTrash, hasItems, + driveActionsRef, }: { selectedItems: DriveItemData[]; currentFolderId: number; @@ -43,6 +44,7 @@ const DriveTopBarActions = ({ hasAnyItemSelected: boolean; isTrash: boolean; hasItems: boolean; + driveActionsRef?: React.MutableRefObject; }) => { const dispatch = useAppDispatch(); @@ -211,7 +213,7 @@ const DriveTopBarActions = ({ {hasItemsAndIsNotTrash && ( <> {separatorV} -
+
{selectedItems.length === 1 && ( <>
void; stepOneTutorialRef: MutableRefObject; + offset?: { x: number; y: number }; }, stepTwoOptions: { onNextStepClicked: () => void; @@ -37,7 +38,7 @@ export const getSignUpSteps = ( ), placement: 'bottom-end' as const, ref: stepOneOptions.stepOneTutorialRef, - offset: { x: 0, y: -40 }, + offset: { x: 0 + (stepOneOptions.offset?.x ?? 0), y: -40 + (stepOneOptions.offset?.x ?? 0) }, disableClickNextStepOutOfContent: true, }, { From a98959fe7c167683cb9472955176c190a1985042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= Date: Mon, 18 Mar 2024 13:18:28 +0100 Subject: [PATCH 13/24] feat(network): dynamic upload concurrency on Chrome depending on RAM usage --- src/app/network/UploadManager.ts | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/app/network/UploadManager.ts b/src/app/network/UploadManager.ts index e6770ed8f..5dfe8ed8f 100644 --- a/src/app/network/UploadManager.ts +++ b/src/app/network/UploadManager.ts @@ -70,6 +70,7 @@ export const uploadFileWithManager = ( }; class UploadManager { + private currentGroupBeingUploaded: FileSizeType = FileSizeType.Small; private filesGroups: Record< FileSizeType, { @@ -101,6 +102,41 @@ class UploadManager { (fileData, next: (err: Error | null, res?: DriveFileData) => void) => { if (this.abortController?.signal.aborted ?? fileData.abortController?.signal.aborted) return; + if (window.performance && (window.performance as any).memory) { + const memory = (window.performance as unknown as { + memory: { + totalJSHeapSize: number, + usedJSHeapSize: number, + jsHeapSizeLimit: number + } + }).memory; + + if (memory.jsHeapSizeLimit !== null && memory.usedJSHeapSize !== null) { + const memoryUsagePercentage = memory.usedJSHeapSize / memory.jsHeapSizeLimit; + console.log({...memory, memoryUsagePercentage}); + + const shouldIncreaseConcurrency = memoryUsagePercentage < 0.7 && this.currentGroupBeingUploaded !== FileSizeType.Big; + + if (shouldIncreaseConcurrency) { + const newConcurrency = Math.min( + this.uploadQueue.concurrency + 1, + this.filesGroups[FileSizeType.Small].concurrency + ); + console.warn(`Memory usage under 70%. Increasing upload concurrency to ${newConcurrency}`); + this.uploadQueue.concurrency = newConcurrency; + } + + const shouldReduceConcurrency = memoryUsagePercentage >= 0.8 && this.uploadQueue.concurrency > 1; + + if (shouldReduceConcurrency) { + console.warn('Memory usage reached 80%. Reducing upload concurrency.'); + this.uploadQueue.concurrency = 1; + } + } + } else { + console.warn('Memory usage control is not available'); + } + let uploadAttempts = 0; const uploadId = randomBytes(10).toString('hex'); const taskId = fileData.taskId; @@ -454,8 +490,12 @@ class UploadManager { if (smallSizedFiles.length > 0) await uploadFiles(smallSizedFiles, this.filesGroups.small.concurrency); + this.currentGroupBeingUploaded = FileSizeType.Medium; + if (mediumSizedFiles.length > 0) await uploadFiles(mediumSizedFiles, this.filesGroups.medium.concurrency); + this.currentGroupBeingUploaded = FileSizeType.Big; + if (bigSizedFiles.length > 0) await uploadFiles(bigSizedFiles, this.filesGroups.big.concurrency); return uploadedFilesData; From c7bd5f9c7e202702d40b9c91e8d2d6a061ab6800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= Date: Mon, 18 Mar 2024 17:40:31 +0100 Subject: [PATCH 14/24] fix(env): add performance attribute to window global types --- src/react-app-env.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index fd92754f9..ee8cacfec 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -39,6 +39,13 @@ interface Window { ready: (cb: () => void) => void; execute: (siteKey: string, { action: string }) => Promise; }; + performance: { + memory?: { + jsHeapSizeLimit: number; + totalJSHeapSize: number; + usedJSHeapSize: number; + }; + } } interface Navigator { From 143e09eb7cb48da51924eaa4a907483b6fe97ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= Date: Mon, 18 Mar 2024 17:41:20 +0100 Subject: [PATCH 15/24] refactor(network): remove no longer needed inline window.performance typing --- src/app/network/UploadManager.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/app/network/UploadManager.ts b/src/app/network/UploadManager.ts index 5dfe8ed8f..52cd5c5a7 100644 --- a/src/app/network/UploadManager.ts +++ b/src/app/network/UploadManager.ts @@ -103,18 +103,10 @@ class UploadManager { if (this.abortController?.signal.aborted ?? fileData.abortController?.signal.aborted) return; if (window.performance && (window.performance as any).memory) { - const memory = (window.performance as unknown as { - memory: { - totalJSHeapSize: number, - usedJSHeapSize: number, - jsHeapSizeLimit: number - } - }).memory; + const memory = window.performance.memory; - if (memory.jsHeapSizeLimit !== null && memory.usedJSHeapSize !== null) { + if (memory && memory.jsHeapSizeLimit !== null && memory.usedJSHeapSize !== null) { const memoryUsagePercentage = memory.usedJSHeapSize / memory.jsHeapSizeLimit; - console.log({...memory, memoryUsagePercentage}); - const shouldIncreaseConcurrency = memoryUsagePercentage < 0.7 && this.currentGroupBeingUploaded !== FileSizeType.Big; if (shouldIncreaseConcurrency) { From 6e384e05e214a1c8edbb7d366a9f727122dd3178 Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+masterprog-cmd@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:14:53 +0100 Subject: [PATCH 16/24] fix: center the last labels in Sign up form --- src/app/auth/views/SignUpView/SignUpView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/auth/views/SignUpView/SignUpView.tsx b/src/app/auth/views/SignUpView/SignUpView.tsx index 767eebfe9..914957872 100644 --- a/src/app/auth/views/SignUpView/SignUpView.tsx +++ b/src/app/auth/views/SignUpView/SignUpView.tsx @@ -34,7 +34,7 @@ export default function SignUpView(props: SignUpViewProps): JSX.Element {
{isRegularSignup && ( -
+ {!props.displayIframe && ( -
+ -
+
{translate('general.terms')} From ee7773726be588f18e86e95e08a28209ea1f5688 Mon Sep 17 00:00:00 2001 From: inxtci Date: Mon, 18 Mar 2024 17:39:01 +0000 Subject: [PATCH 19/24] [skip ci] Bump version v1.0.251 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d6bd3609..50e6ff881 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drive-web", - "version": "v1.0.250", + "version": "v1.0.251", "private": true, "dependencies": { "@headlessui/react": "1.7.5", From 3a151a170c7aed9dcf956e8f8a5ed7f033433bce Mon Sep 17 00:00:00 2001 From: inxtci Date: Tue, 19 Mar 2024 08:29:40 +0000 Subject: [PATCH 20/24] [skip ci] Bump version v1.0.252 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 50e6ff881..313df519b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drive-web", - "version": "v1.0.251", + "version": "v1.0.252", "private": true, "dependencies": { "@headlessui/react": "1.7.5", From 467dffb92dbff56118f7b6d2483f67318efb948b Mon Sep 17 00:00:00 2001 From: inxtci Date: Tue, 19 Mar 2024 15:47:20 +0000 Subject: [PATCH 21/24] [skip ci] Bump version v1.0.253 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 313df519b..43bf53c5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drive-web", - "version": "v1.0.252", + "version": "v1.0.253", "private": true, "dependencies": { "@headlessui/react": "1.7.5", From ef010e99fe1e0f68423729fed99e37c77a6567d7 Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+masterprog-cmd@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:05:54 +0200 Subject: [PATCH 22/24] feat: add world cloud security banner --- src/app/banners/BannerWrapper.tsx | 4 ++-- src/app/banners/FeaturesBanner.tsx | 22 ++++++++++------------ src/app/i18n/locales/de.json | 8 ++++---- src/app/i18n/locales/en.json | 8 ++++---- src/app/i18n/locales/es.json | 10 +++++----- src/app/i18n/locales/fr.json | 8 ++++---- src/app/i18n/locales/it.json | 8 ++++---- src/app/i18n/locales/ru.json | 8 ++++---- src/app/i18n/locales/zh.json | 8 ++++---- 9 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/app/banners/BannerWrapper.tsx b/src/app/banners/BannerWrapper.tsx index 86e8ab3f1..1b6555713 100644 --- a/src/app/banners/BannerWrapper.tsx +++ b/src/app/banners/BannerWrapper.tsx @@ -9,8 +9,8 @@ import { useSelector } from 'react-redux'; import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; import FeaturesBanner from './FeaturesBanner'; -const SHOW_BANNER_COOKIE_NAME = 'show_spring_banner'; -const OFFER_OFF_DAY = new Date('2024-03-18'); +const SHOW_BANNER_COOKIE_NAME = 'show_world_security_banner'; +const OFFER_OFF_DAY = new Date('2024-04-18'); const BannerWrapper = (): JSX.Element => { const [showBanner, setShowBanner] = useState(false); diff --git a/src/app/banners/FeaturesBanner.tsx b/src/app/banners/FeaturesBanner.tsx index 3183ce2e7..4586f2f82 100644 --- a/src/app/banners/FeaturesBanner.tsx +++ b/src/app/banners/FeaturesBanner.tsx @@ -1,4 +1,4 @@ -import { CheckCircle, Flower, X } from '@phosphor-icons/react'; +import { CheckCircle, ShieldCheck, X } from '@phosphor-icons/react'; import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose: () => void }): JSX.Element => { @@ -7,7 +7,7 @@ const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose: const features = translateList('featuresBanner.features'); const handleOnClick = () => { - window.open('https://internxt.com/pricing', '_blank', 'noopener noreferrer'); + window.open('https://internxt.com/lifetime', '_blank', 'noopener noreferrer'); }; return ( @@ -25,14 +25,12 @@ const FeaturesBanner = ({ showBanner, onClose }: { showBanner: boolean; onClose: -
-
-
-

{translate('featuresBanner.label')}

+
+
+
+

{translate('featuresBanner.label')}

-

- {translate('featuresBanner.title')} -

+

{translate('featuresBanner.title')}