From 2054e4cb77f630ce2627a098386b5bf9799690f6 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Fri, 24 May 2024 11:43:38 -0300 Subject: [PATCH 01/10] feat(empty state): implement empty state --- .../drive_detail_folder_empty_card.dart | 467 ++++++++++++++++-- lib/pages/drive_detail/drive_detail_page.dart | 63 ++- 2 files changed, 457 insertions(+), 73 deletions(-) diff --git a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart index 7084cc4ff7..315bc6cc71 100644 --- a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart +++ b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart @@ -4,83 +4,454 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { final bool promptToAddFiles; final String driveId; final String parentFolderId; + final bool isRootFolder; const DriveDetailFolderEmptyCard({ super.key, this.promptToAddFiles = false, required this.driveId, required this.parentFolderId, + required this.isRootFolder, }); @override Widget build(BuildContext context) => buildArDriveCard(context); Widget buildArDriveCard(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: ArDriveCard( - backgroundColor: - ArDriveTheme.of(context).themeData.tableTheme.backgroundColor, - width: double.infinity, - content: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Flexible( - child: SizedBox( - height: 45, + ArDriveTypographyNew.of(context); + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + return BlocBuilder( + builder: (context, state) { + final isANewUser = (state as DrivesLoadSuccess).userDrives.length == 1; + + return ScreenTypeLayout.builder(mobile: (context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Stack( + children: [ + Positioned( + bottom: 0, + right: 0, + child: SvgPicture.asset( + Resources.images.login.bannerLightMode, + fit: BoxFit.cover, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: !isRootFolder + ? _emptyFolder(context) + : isANewUser + ? _newUserEmptyRootFolder(context) + : _existingUserEmptyRootFolder(context), + ), + ], + ), + ); + }, desktop: (context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.8, + child: ArDriveCard( + width: double.infinity, + backgroundColor: colorTokens.containerL1, + content: !isRootFolder + ? _emptyFolder(context) + : isANewUser + ? _newUserEmptyRootFolder(context) + : _existingUserEmptyRootFolder(context), ), ), + ); + }); + }, + ); + } + + Widget _emptyFolder(BuildContext context) { + final typography = ArDriveTypographyNew.of(context); + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + final width = MediaQuery.of(context).size.width; + return ScreenTypeLayout.builder(mobile: (context) { + return SingleChildScrollView( + child: Column( + children: [ Text( - appLocalizationsOf(context).noFiles, - style: ArDriveTypography.headline.headline5Regular(), + 'Ready to organize your files?', + style: typography.display( + fontWeight: ArFontWeight.bold, + ), ), - const Flexible( - child: SizedBox( - height: 45, + Text( + 'Start by adding some content to your new folder. Explore the various options available to keep your drive neat and efficient.', + style: typography.heading5( + color: colorTokens.textLow, ), + textAlign: TextAlign.center, ), - if (promptToAddFiles) - InkWell( - onTap: () { - promptToUpload( - context, - driveId: driveId, - parentFolderId: parentFolderId, - isFolderUpload: false, - ); - }, - child: Container( - decoration: BoxDecoration( - border: Border.all( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, - width: 1, - ), - borderRadius: BorderRadius.circular(10), - ), - padding: - const EdgeInsets.symmetric(horizontal: 40, vertical: 40), + const SizedBox(height: 20), + _uploadFileCard(context), + const SizedBox(height: 20), + _uploadFolderCard(context), + const SizedBox(height: 20), + _createPinCard(context), + ], + ), + ); + }, desktop: (context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 66), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ArDriveImage( + image: AssetImage(Resources.images.login.confetti), + ), + Flexible( child: Column( - mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - ArDriveIcons.upload(size: 45), - const SizedBox( - height: 10, + Text( + 'Ready to organize your files?', + style: typography.display( + fontWeight: ArFontWeight.bold, + ), ), Text( - appLocalizationsOf(context).uploadYourFirstFile, - style: ArDriveTypography.headline.headline5Regular(), + 'Start by adding some content to your new folder. Explore the various options available to keep your drive neat and efficient.', + style: typography.heading5( + color: colorTokens.textLow, + ), textAlign: TextAlign.center, ), ], ), ), + ArDriveImage( + image: AssetImage(Resources.images.login.confetti), + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _uploadFileCard(context), + _uploadFolderCard(context), + if (width > SMALL_DESKTOP) _createPinCard(context), + ], + ) + ], + ); + }); + } + + Widget _existingUserEmptyRootFolder(BuildContext context) { + final typography = ArDriveTypographyNew.of(context); + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + + final width = MediaQuery.of(context).size.width; + + return ScreenTypeLayout.builder( + mobile: (context) { + return SingleChildScrollView( + child: Column( + children: [ + Text( + 'Just look at this shiny new drive!', + style: typography.heading4( + fontWeight: ArFontWeight.bold, + ), + ), + Text( + 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next. 👇', + style: typography.paragraphLarge( + color: colorTokens.textLow, + ), + textAlign: TextAlign.center, ), + const SizedBox(height: 20), + _uploadFileCard(context), + const SizedBox(height: 20), + _uploadFolderCard(context), + const SizedBox(height: 20), + _createFolderCard(context), + const SizedBox(height: 20), + _createPinCard(context), + const SizedBox(height: 20), + ], + ), + ); + }, + desktop: (context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 66), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ArDriveImage( + image: AssetImage(Resources.images.login.confetti), + ), + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Just look at this shiny new drive!', + style: typography.display( + fontWeight: ArFontWeight.bold, + ), + ), + Text( + 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next. 👇', + style: typography.heading5( + color: colorTokens.textLow, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ArDriveImage( + image: AssetImage(Resources.images.login.confetti), + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _uploadFileCard(context), + _uploadFolderCard(context), + if (width > SMALL_DESKTOP) _createFolderCard(context), + if (width > LARGE_DESKTOP) _createPinCard(context), + ], + ) ], - ), + ); + }, + ); + } + + Widget _newUserEmptyRootFolder(BuildContext context) { + final typography = ArDriveTypographyNew.of(context); + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + + return ScreenTypeLayout.builder( + mobile: (context) { + return SingleChildScrollView( + child: Column( + children: [ + Text( + 'You\'re on chain!', + style: typography.heading4( + fontWeight: ArFontWeight.bold, + ), + ), + Text( + 'You have just made your first blockchain interaction, congrats! You can now use your new drive to manage, share, and organize just about any multimedia file.', + style: typography.paragraphLarge( + color: colorTokens.textLow, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + _uploadFileCard(context), + const SizedBox(height: 20), + _createFolderCard(context), + ], + ), + ); + }, + desktop: (context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 66), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ArDriveImage( + image: AssetImage(Resources.images.login.confetti), + ), + Flexible( + child: Column( + children: [ + Text( + 'You\'re on chain!', + style: typography.display( + fontWeight: ArFontWeight.bold, + ), + ), + Text( + 'You have just made your first blockchain interaction, congrats! You can now use your new drive to manage, share, and organize just about any multimedia file.', + style: typography.heading5( + color: colorTokens.textLow, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ArDriveImage( + image: AssetImage(Resources.images.login.confetti), + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _uploadFileCard(context), + _createFolderCard(context), + ], + ) + ], + ); + }, + ); + } + + Widget _uploadFileCard( + BuildContext context, + ) { + return _actionCard( + context: context, + title: 'Upload File(s)', + description: + 'Upload a file, or a selection of files, that are collectively under 100KB into your new drive.', + buttonText: 'Upload', + onPressed: () { + promptToUpload( + context, + driveId: driveId, + parentFolderId: parentFolderId, + isFolderUpload: false, + ); + }, + icon: ArDriveIcons.upload(), + ); + } + + Widget _createFolderCard( + BuildContext context, + ) { + return _actionCard( + context: context, + title: 'Create a Folder', + description: + 'Create a new folder to organize your files. You can create as many folders as you need to keep your drive organized.', + buttonText: 'Create Folder', + onPressed: () { + promptToCreateFolder(context, + driveId: driveId, parentFolderId: parentFolderId); + }, + icon: ArDriveIcons.iconUploadFolder1(), + ); + } + + Widget _uploadFolderCard( + BuildContext context, + ) { + return _actionCard( + context: context, + title: 'Upload a Folder', + description: + 'Upload existing folders that total less than 100KB from a computer, other file storage apps, or an external HD.', + buttonText: 'Upload Folder', + onPressed: () { + promptToUpload( + context, + driveId: driveId, + parentFolderId: parentFolderId, + isFolderUpload: true, + ); + }, + icon: ArDriveIcons.iconUploadFolder1(), + ); + } + + Widget _createPinCard( + BuildContext context, + ) { + return _actionCard( + context: context, + title: 'Create a Pin', + description: + 'Pin any permaweb file to your Drive, to create inspiration boards, recipe collections, or NFT compilations. ', + buttonText: 'Create Pin', + onPressed: () { + showPinFileDialog(context: context); + }, + icon: ArDriveIcons.pinWithCircle(), + ); + } + + Widget _attachDrive(BuildContext context) { + return _actionCard( + context: context, + title: 'Attach Drive', + description: + 'Attach a new drive to your account to increase your storage capacity and organize your files.', + buttonText: 'Attach Drive', + onPressed: () { + attachDrive(context: context); + }, + icon: ArDriveIcons.iconAttachDrive(), + ); + } + + Widget _actionCard({ + required BuildContext context, + required String title, + required String description, + required String buttonText, + required Function() onPressed, + required ArDriveIcon icon, + }) { + final typography = ArDriveTypographyNew.of(context); + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + + return ArDriveCard( + width: 248 + 35, + height: 248 + 35, + backgroundColor: colorTokens.containerL2, + contentPadding: const EdgeInsets.all(31), + content: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + icon.copyWith(size: 25), + Text( + title, + style: typography.paragraphXLarge( + fontWeight: ArFontWeight.semiBold, + ), + ), + const SizedBox(height: 10), + Text( + description, + style: typography.paragraphNormal( + color: colorTokens.textLow, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + ArDriveButtonNew( + text: buttonText, + typography: typography, + onPressed: onPressed, + variant: ButtonVariant.secondary, + ) + ], ), ); } diff --git a/lib/pages/drive_detail/drive_detail_page.dart b/lib/pages/drive_detail/drive_detail_page.dart index 9af301be25..b338f2c046 100644 --- a/lib/pages/drive_detail/drive_detail_page.dart +++ b/lib/pages/drive_detail/drive_detail_page.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:ardrive/app_shell.dart'; import 'package:ardrive/authentication/ardrive_auth.dart'; +import 'package:ardrive/authentication/components/breakpoint_layout_builder.dart'; import 'package:ardrive/blocs/blocs.dart'; import 'package:ardrive/blocs/fs_entry_preview/fs_entry_preview_cubit.dart'; import 'package:ardrive/blocs/prompt_to_snapshot/prompt_to_snapshot_bloc.dart'; @@ -17,12 +18,14 @@ import 'package:ardrive/components/drive_detach_dialog.dart'; import 'package:ardrive/components/drive_rename_form.dart'; import 'package:ardrive/components/fs_entry_license_form.dart'; import 'package:ardrive/components/new_button/new_button.dart'; +import 'package:ardrive/components/pin_file_dialog.dart'; import 'package:ardrive/components/prompt_to_snapshot_dialog.dart'; import 'package:ardrive/components/side_bar.dart'; import 'package:ardrive/core/activity_tracker.dart'; import 'package:ardrive/download/multiple_file_download_modal.dart'; import 'package:ardrive/entities/entities.dart' as entities; import 'package:ardrive/l11n/l11n.dart'; +import 'package:ardrive/misc/resources.dart'; import 'package:ardrive/models/license.dart'; import 'package:ardrive/models/models.dart'; import 'package:ardrive/pages/congestion_warning_wrapper.dart'; @@ -55,6 +58,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:just_audio/just_audio.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:synchronized/synchronized.dart'; @@ -561,6 +565,11 @@ class _DriveDetailPageState extends State { .folderInView.folder.id, promptToAddFiles: driveDetailState .hasWritePermissions, + isRootFolder: driveDetailState + .folderInView + .folder + .parentFolderId == + null, ), ), ], @@ -578,6 +587,10 @@ class _DriveDetailPageState extends State { driveDetailState.folderInView.folder.id, promptToAddFiles: driveDetailState.hasWritePermissions, + isRootFolder: driveDetailState.folderInView + .folder.parentFolderId == + null && + !hasSubfolders, ), ), ], @@ -801,32 +814,32 @@ class _DriveDetailPageState extends State { ), ), Expanded( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - child: (hasSubfolders || hasFiles) - ? ListView.separated( - controller: _scrollController, - separatorBuilder: (context, index) => const SizedBox( - height: 5, - ), - itemCount: filteredItems.length, - itemBuilder: (context, index) { - return ArDriveItemListTile( - key: ObjectKey([filteredItems[index]]), - drive: state.currentDrive, - item: filteredItems[index], - ); - }, - ) - : DriveDetailFolderEmptyCard( - promptToAddFiles: state.hasWritePermissions, - driveId: state.currentDrive.id, - parentFolderId: state.folderInView.folder.id, + child: (hasSubfolders || hasFiles) + ? ListView.separated( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, ), - ), + controller: _scrollController, + separatorBuilder: (context, index) => const SizedBox( + height: 5, + ), + itemCount: filteredItems.length, + itemBuilder: (context, index) { + return ArDriveItemListTile( + key: ObjectKey([filteredItems[index]]), + drive: state.currentDrive, + item: filteredItems[index], + ); + }, + ) + : DriveDetailFolderEmptyCard( + promptToAddFiles: state.hasWritePermissions, + driveId: state.currentDrive.id, + parentFolderId: state.folderInView.folder.id, + isRootFolder: + state.folderInView.folder.parentFolderId == null, + ), ), ], ); From da71d96e632b4e5ad54d308e8d7fe53ccc303333 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Fri, 24 May 2024 11:45:25 -0300 Subject: [PATCH 02/10] Update drive_detail_folder_empty_card.dart --- .../components/drive_detail_folder_empty_card.dart | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart index 315bc6cc71..40b8f7d8f0 100644 --- a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart +++ b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart @@ -396,20 +396,6 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { ); } - Widget _attachDrive(BuildContext context) { - return _actionCard( - context: context, - title: 'Attach Drive', - description: - 'Attach a new drive to your account to increase your storage capacity and organize your files.', - buttonText: 'Attach Drive', - onPressed: () { - attachDrive(context: context); - }, - icon: ArDriveIcons.iconAttachDrive(), - ); - } - Widget _actionCard({ required BuildContext context, required String title, From e7eb69db014ff2cf1b01d4a859510d02ce0f5263 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 30 May 2024 09:57:28 -0300 Subject: [PATCH 03/10] task(logging) - filter logs for version 2.32.0 - add transaction id on failed to decrypt logs --- lib/core/crypto/crypto.dart | 8 ++++- lib/utils/logger.dart | 3 +- .../ardrive_crypto/lib/src/exceptions.dart | 29 ++++++++++++++----- test/utils/logger_test.dart | 1 + 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/lib/core/crypto/crypto.dart b/lib/core/crypto/crypto.dart index 03b1c79340..ccab9c6e9c 100644 --- a/lib/core/crypto/crypto.dart +++ b/lib/core/crypto/crypto.dart @@ -92,7 +92,10 @@ class ArDriveCrypto { final cipherIvTag = transaction.getTag(EntityTag.cipherIv); if (cipher == null || cipherIvTag == null) { - throw MissingCipherTagException(); + throw MissingCipherTagException( + corruptedDataAppVersion: transaction.getTag(EntityTag.appVersion), + corruptedTransactionId: transaction.id, + ); } final cipherIv = utils.decodeBase64ToBytes(cipherIvTag); @@ -128,6 +131,7 @@ class ArDriveCrypto { } else { throw UnknownCipherException( corruptedDataAppVersion: transaction.getTag(EntityTag.appVersion), + corruptedTransactionId: transaction.id, ); } @@ -143,6 +147,7 @@ class ArDriveCrypto { /// Unknow error throw TransactionDecryptionException( corruptedDataAppVersion: transaction.getTag(EntityTag.appVersion), + corruptedTransactionId: transaction.id, ); } } @@ -161,6 +166,7 @@ class ArDriveCrypto { if (cipher == null || cipherIvTag == null) { throw MissingCipherTagException( corruptedDataAppVersion: transaction.getTag(EntityTag.appVersion), + corruptedTransactionId: transaction.id, ); } diff --git a/lib/utils/logger.dart b/lib/utils/logger.dart index c7ee475868..a232514bd4 100644 --- a/lib/utils/logger.dart +++ b/lib/utils/logger.dart @@ -13,9 +13,10 @@ final Set _knownTransactionDecryptionBugVersions = { '2.30.0', '2.30.1', '2.30.2', + '2.32.0', '2.36.0', '2.37.0', - '2.37.1' + '2.37.1', }; ShouldLogErrorCallback shouldLogErrorCallback = (error) { diff --git a/packages/ardrive_crypto/lib/src/exceptions.dart b/packages/ardrive_crypto/lib/src/exceptions.dart index dbe5039878..a8048dfc9d 100644 --- a/packages/ardrive_crypto/lib/src/exceptions.dart +++ b/packages/ardrive_crypto/lib/src/exceptions.dart @@ -1,37 +1,50 @@ class ArDriveDecryptionException implements Exception { final String? corruptedDataAppVersion; + final String? corruptedTransactionId; - ArDriveDecryptionException({this.corruptedDataAppVersion}); + ArDriveDecryptionException({ + this.corruptedDataAppVersion, + this.corruptedTransactionId, + }); @override String toString() { - return 'ARFSDecryptionException: corruptedDataAppVersion: $corruptedDataAppVersion'; + return 'ArDriveDecryptionException: corruptedDataAppVersion: $corruptedDataAppVersion and corruptedTransactionId: $corruptedTransactionId'; } } class TransactionDecryptionException extends ArDriveDecryptionException { - TransactionDecryptionException({super.corruptedDataAppVersion}); + TransactionDecryptionException({ + super.corruptedDataAppVersion, + super.corruptedTransactionId, + }); @override String toString() { - return 'TransactionDecryptionException: corruptedDataAppVersion: $corruptedDataAppVersion'; + return 'TransactionDecryptionException: corruptedDataAppVersion: $corruptedDataAppVersion and corruptedTransactionId: $corruptedTransactionId'; } } class MissingCipherTagException extends ArDriveDecryptionException { - MissingCipherTagException({super.corruptedDataAppVersion}); + MissingCipherTagException({ + super.corruptedDataAppVersion, + super.corruptedTransactionId, + }); @override String toString() { - return 'MissingCipherTagException: corruptedDataAppVersion: $corruptedDataAppVersion'; + return 'MissingCipherTagException: corruptedDataAppVersion: $corruptedDataAppVersion and corruptedTransactionId: $corruptedTransactionId'; } } class UnknownCipherException extends ArDriveDecryptionException { - UnknownCipherException({super.corruptedDataAppVersion}); + UnknownCipherException({ + super.corruptedDataAppVersion, + super.corruptedTransactionId, + }); @override String toString() { - return 'UnknowCipherException: corruptedDataAppVersion: $corruptedDataAppVersion'; + return 'UnknowCipherException: corruptedDataAppVersion: $corruptedDataAppVersion and corruptedTransactionId: $corruptedTransactionId'; } } diff --git a/test/utils/logger_test.dart b/test/utils/logger_test.dart index 4b813f956a..a1a4fdebb8 100644 --- a/test/utils/logger_test.dart +++ b/test/utils/logger_test.dart @@ -18,6 +18,7 @@ void main() { '2.30.0', '2.30.1', '2.30.2', + '2.32.0', '2.36.0', '2.37.0', '2.37.1' From 9209d6972cbd1c3e467788b2b727510910cd2f66 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 30 May 2024 10:01:06 -0300 Subject: [PATCH 04/10] Update drives_cubit.dart --- lib/blocs/drives/drives_cubit.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/blocs/drives/drives_cubit.dart b/lib/blocs/drives/drives_cubit.dart index e2fbd4f689..386487de4c 100644 --- a/lib/blocs/drives/drives_cubit.dart +++ b/lib/blocs/drives/drives_cubit.dart @@ -132,6 +132,10 @@ class DrivesCubit extends Cubit { drivesWithAlerts: const [], canCreateNewDrive: false); + if (isClosed) { + return; + } + emit(state); } From 7d747bc90fb07f50b54374aa5974e170980feb25 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 30 May 2024 10:27:52 -0300 Subject: [PATCH 05/10] feat(empty state) - add events and page view - updates create folder and create pin modals --- lib/components/folder_create_form.dart | 4 +- lib/components/pin_file_dialog.dart | 25 +++---- .../drive_detail_folder_empty_card.dart | 69 ++++++++++++++++--- .../plausible_custom_events.dart | 16 +++++ .../plausible_page_view_events.dart | 5 ++ 5 files changed, 90 insertions(+), 29 deletions(-) diff --git a/lib/components/folder_create_form.dart b/lib/components/folder_create_form.dart index 4daacf7823..72bcb903c7 100644 --- a/lib/components/folder_create_form.dart +++ b/lib/components/folder_create_form.dart @@ -94,11 +94,11 @@ class _FolderCreateFormState extends State { ); } }, - builder: (context, state) => ArDriveStandardModal( + builder: (context, state) => ArDriveStandardModalNew( title: appLocalizationsOf(context).createFolderEmphasized, content: SizedBox( width: kMediumDialogWidth, - child: ArDriveTextField( + child: ArDriveTextFieldNew( controller: _folderNameController, autofocus: true, onFieldSubmitted: (value) { diff --git a/lib/components/pin_file_dialog.dart b/lib/components/pin_file_dialog.dart index 52c15e1ad3..4d80b7565c 100644 --- a/lib/components/pin_file_dialog.dart +++ b/lib/components/pin_file_dialog.dart @@ -100,14 +100,14 @@ class PinFileDialog extends StatelessWidget { final idValidationError = _getIdValidationError(context, state); final nameValidationError = _getNameValidationError(context, state); - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: appLocalizationsOf(context).newFilePin, content: SizedBox( width: kMediumDialogWidth, child: Column( mainAxisSize: MainAxisSize.min, children: [ - ArDriveTextField( + ArDriveTextFieldNew( isEnabled: state is! PinFileNetworkCheckRunning, label: appLocalizationsOf(context).enterTxIdOrFileId, isFieldRequired: true, @@ -119,7 +119,7 @@ class PinFileDialog extends StatelessWidget { ); }, ), - ArDriveTextField( + ArDriveTextFieldNew( isEnabled: true, label: appLocalizationsOf(context).enterFileName, isFieldRequired: true, @@ -154,10 +154,6 @@ class PinFileDialog extends StatelessWidget { } String? _getIdValidationError(BuildContext context, PinFileState state) { - // if (state is PinFileInitial) { - // return null; - // } - if (state.idValidation == IdValidationResult.invalid) { return appLocalizationsOf(context).theIdProvidedIsNotValid; } else if (state.idValidation == IdValidationResult.required) { @@ -183,10 +179,6 @@ class PinFileDialog extends StatelessWidget { } String? _getNameValidationError(BuildContext context, PinFileState state) { - // if (state is PinFileInitial) { - // return null; - // } - if (state.nameValidation == NameValidationResult.invalid) { return appLocalizationsOf(context).validationInvalid; } else if (state.nameValidation == NameValidationResult.required) { @@ -197,21 +189,22 @@ class PinFileDialog extends StatelessWidget { return null; } - ArDriveStandardModal _errorDialog( + ArDriveStandardModalNew _errorDialog( BuildContext context, { required String errorText, bool doublePop = false, }) => - ArDriveStandardModal( + ArDriveStandardModalNew( width: kMediumDialogWidth, title: appLocalizationsOf(context).failedToCreatePin, content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 16), - Text(errorText), - const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text(errorText), + ), ], ), actions: [ diff --git a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart index 40b8f7d8f0..71d9b0e88e 100644 --- a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart +++ b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart @@ -1,6 +1,6 @@ part of '../drive_detail_page.dart'; -class DriveDetailFolderEmptyCard extends StatelessWidget { +class DriveDetailFolderEmptyCard extends StatefulWidget { final bool promptToAddFiles; final String driveId; final String parentFolderId; @@ -14,6 +14,34 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { required this.isRootFolder, }); + @override + State createState() => + _DriveDetailFolderEmptyCardState(); +} + +class _DriveDetailFolderEmptyCardState + extends State { + bool? _hasTrackedPageView = false; + + void trackPageView({ + required bool isRootFolder, + required bool isANewUser, + }) { + if (_hasTrackedPageView == false) { + if (isRootFolder && isANewUser) { + PlausibleEventTracker.trackPageview( + page: PlausiblePageView.newUserDriveEmptyPage); + } else if (isRootFolder && !isANewUser) { + PlausibleEventTracker.trackPageview( + page: PlausiblePageView.existingUserDriveEmptyPage); + } else { + PlausibleEventTracker.trackPageview( + page: PlausiblePageView.folderEmptyPage); + } + _hasTrackedPageView = true; + } + } + @override Widget build(BuildContext context) => buildArDriveCard(context); @@ -39,7 +67,7 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: !isRootFolder + child: !widget.isRootFolder ? _emptyFolder(context) : isANewUser ? _newUserEmptyRootFolder(context) @@ -56,7 +84,7 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { child: ArDriveCard( width: double.infinity, backgroundColor: colorTokens.containerL1, - content: !isRootFolder + content: !widget.isRootFolder ? _emptyFolder(context) : isANewUser ? _newUserEmptyRootFolder(context) @@ -73,6 +101,11 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; final width = MediaQuery.of(context).size.width; + trackPageView( + isRootFolder: false, + isANewUser: false, + ); + return ScreenTypeLayout.builder(mobile: (context) { return SingleChildScrollView( child: Column( @@ -83,6 +116,7 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { fontWeight: ArFontWeight.bold, ), ), + const SizedBox(height: 10), Text( 'Start by adding some content to your new folder. Explore the various options available to keep your drive neat and efficient.', style: typography.heading5( @@ -122,6 +156,7 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { fontWeight: ArFontWeight.bold, ), ), + const SizedBox(height: 10), Text( 'Start by adding some content to your new folder. Explore the various options available to keep your drive neat and efficient.', style: typography.heading5( @@ -152,9 +187,13 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { } Widget _existingUserEmptyRootFolder(BuildContext context) { + trackPageView( + isRootFolder: true, + isANewUser: false, + ); + final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - final width = MediaQuery.of(context).size.width; return ScreenTypeLayout.builder( @@ -168,8 +207,9 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { fontWeight: ArFontWeight.bold, ), ), + const SizedBox(height: 10), Text( - 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next. 👇', + 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next.', style: typography.paragraphLarge( color: colorTokens.textLow, ), @@ -210,8 +250,9 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { fontWeight: ArFontWeight.bold, ), ), + const SizedBox(height: 10), Text( - 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next. 👇', + 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next.', style: typography.heading5( color: colorTokens.textLow, ), @@ -244,6 +285,10 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { Widget _newUserEmptyRootFolder(BuildContext context) { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + trackPageView( + isRootFolder: true, + isANewUser: true, + ); return ScreenTypeLayout.builder( mobile: (context) { @@ -256,6 +301,7 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { fontWeight: ArFontWeight.bold, ), ), + const SizedBox(height: 10), Text( 'You have just made your first blockchain interaction, congrats! You can now use your new drive to manage, share, and organize just about any multimedia file.', style: typography.paragraphLarge( @@ -292,6 +338,7 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { fontWeight: ArFontWeight.bold, ), ), + const SizedBox(height: 10), Text( 'You have just made your first blockchain interaction, congrats! You can now use your new drive to manage, share, and organize just about any multimedia file.', style: typography.heading5( @@ -333,8 +380,8 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { onPressed: () { promptToUpload( context, - driveId: driveId, - parentFolderId: parentFolderId, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, isFolderUpload: false, ); }, @@ -353,7 +400,7 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { buttonText: 'Create Folder', onPressed: () { promptToCreateFolder(context, - driveId: driveId, parentFolderId: parentFolderId); + driveId: widget.driveId, parentFolderId: widget.parentFolderId); }, icon: ArDriveIcons.iconUploadFolder1(), ); @@ -371,8 +418,8 @@ class DriveDetailFolderEmptyCard extends StatelessWidget { onPressed: () { promptToUpload( context, - driveId: driveId, - parentFolderId: parentFolderId, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, isFolderUpload: true, ); }, diff --git a/lib/utils/plausible_event_tracker/plausible_custom_events.dart b/lib/utils/plausible_event_tracker/plausible_custom_events.dart index 94ce09ecac..e51f9d854f 100644 --- a/lib/utils/plausible_event_tracker/plausible_custom_events.dart +++ b/lib/utils/plausible_event_tracker/plausible_custom_events.dart @@ -65,6 +65,12 @@ enum PlausibleCustomEvent { /// Create Drive on Empty State clickCreatePrivateDriveButtonEmptyState, clickCreatePublicDriveButtonEmptyState, + + /// Drive Empty State + clickUploadFileEmptyState, + clickCreateFolderEmptyState, + clickUploadFolderEmptyState, + clickCreatePinEmptyState, } extension PlausibleCustomEventNames on PlausibleCustomEvent { @@ -184,6 +190,16 @@ extension PlausibleCustomEventNames on PlausibleCustomEvent { return 'clickCreatePrivateDriveButtonEmptyState'; case PlausibleCustomEvent.clickCreatePublicDriveButtonEmptyState: return 'clickCreatePublicDriveButtonEmptyState'; + + /// Drive Empty State + case PlausibleCustomEvent.clickUploadFileEmptyState: + return 'clickUploadFileEmptyState'; + case PlausibleCustomEvent.clickCreateFolderEmptyState: + return 'clickCreateFolderEmptyState'; + case PlausibleCustomEvent.clickUploadFolderEmptyState: + return 'clickUploadFolderEmptyState'; + case PlausibleCustomEvent.clickCreatePinEmptyState: + return 'clickCreatePinEmptyState'; } } } diff --git a/lib/utils/plausible_event_tracker/plausible_page_view_events.dart b/lib/utils/plausible_event_tracker/plausible_page_view_events.dart index 9ecec909dd..8efb2444ae 100644 --- a/lib/utils/plausible_event_tracker/plausible_page_view_events.dart +++ b/lib/utils/plausible_event_tracker/plausible_page_view_events.dart @@ -39,4 +39,9 @@ enum PlausiblePageView { /// Search searchPage, + + /// Empty State + folderEmptyPage, + existingUserDriveEmptyPage, + newUserDriveEmptyPage, } From 1f94a88fcf057e6c19fe006d304e4ee31f744882 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 30 May 2024 11:32:12 -0300 Subject: [PATCH 06/10] refactor + improve event tracking --- .../drive_detail_folder_empty_card.dart | 670 +++++++++++------- .../plausible_event_tracker.dart | 40 ++ 2 files changed, 472 insertions(+), 238 deletions(-) diff --git a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart index 71d9b0e88e..e3e79fdc0e 100644 --- a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart +++ b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart @@ -68,10 +68,19 @@ class _DriveDetailFolderEmptyCardState Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: !widget.isRootFolder - ? _emptyFolder(context) + ? _EmptyFolder( + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + ) : isANewUser - ? _newUserEmptyRootFolder(context) - : _existingUserEmptyRootFolder(context), + ? _NewUserEmptyRootFolder( + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + ) + : _ExistingUserEmptyRootFolder( + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + ), ), ], ), @@ -85,10 +94,19 @@ class _DriveDetailFolderEmptyCardState width: double.infinity, backgroundColor: colorTokens.containerL1, content: !widget.isRootFolder - ? _emptyFolder(context) + ? _EmptyFolder( + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + ) : isANewUser - ? _newUserEmptyRootFolder(context) - : _existingUserEmptyRootFolder(context), + ? _NewUserEmptyRootFolder( + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + ) + : _ExistingUserEmptyRootFolder( + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + ), ), ), ); @@ -96,45 +114,70 @@ class _DriveDetailFolderEmptyCardState }, ); } +} + +class _EmptyFolder extends StatefulWidget { + final String driveId; + final String parentFolderId; + + const _EmptyFolder({ + required this.driveId, + required this.parentFolderId, + }); + + @override + State<_EmptyFolder> createState() => _EmptyFolderState(); +} + +class _EmptyFolderState extends State<_EmptyFolder> { + @override + initState() { + super.initState(); + PlausibleEventTracker.trackPageview( + page: PlausiblePageView.folderEmptyPage); + } - Widget _emptyFolder(BuildContext context) { + @override + Widget build(BuildContext context) { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; final width = MediaQuery.of(context).size.width; - trackPageView( - isRootFolder: false, - isANewUser: false, - ); + const String headerText = 'Ready to organize your files?'; + const String descriptionText = + 'Start by adding some content to your new folder. Explore the various options available to keep your drive neat and efficient.'; - return ScreenTypeLayout.builder(mobile: (context) { - return SingleChildScrollView( + return ScreenTypeLayout.builder( + mobile: (context) => SingleChildScrollView( child: Column( children: [ - Text( - 'Ready to organize your files?', - style: typography.display( - fontWeight: ArFontWeight.bold, - ), + _buildHeaderText( + context: context, + text: headerText, + style: typography.display(fontWeight: ArFontWeight.bold), ), const SizedBox(height: 10), - Text( - 'Start by adding some content to your new folder. Explore the various options available to keep your drive neat and efficient.', - style: typography.heading5( - color: colorTokens.textLow, - ), - textAlign: TextAlign.center, + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.heading5(color: colorTokens.textLow), ), const SizedBox(height: 20), - _uploadFileCard(context), + _ActionCard.uploadFile(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.folderEmptyPage), const SizedBox(height: 20), - _uploadFolderCard(context), + _ActionCard.uploadFolder(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.folderEmptyPage), const SizedBox(height: 20), - _createPinCard(context), + _ActionCard.createPin(context, + page: PlausiblePageView.folderEmptyPage), ], ), - ); - }, desktop: (context) { - return Column( + ), + desktop: (context) => Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -144,244 +187,389 @@ class _DriveDetailFolderEmptyCardState mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ArDriveImage( - image: AssetImage(Resources.images.login.confetti), - ), + image: AssetImage(Resources.images.login.confetti)), Flexible( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - 'Ready to organize your files?', - style: typography.display( - fontWeight: ArFontWeight.bold, - ), + _buildHeaderText( + context: context, + text: headerText, + style: + typography.display(fontWeight: ArFontWeight.bold), ), const SizedBox(height: 10), - Text( - 'Start by adding some content to your new folder. Explore the various options available to keep your drive neat and efficient.', - style: typography.heading5( - color: colorTokens.textLow, - ), - textAlign: TextAlign.center, + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.heading5(color: colorTokens.textLow), ), ], ), ), ArDriveImage( - image: AssetImage(Resources.images.login.confetti), - ), + image: AssetImage(Resources.images.login.confetti)), ], ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _uploadFileCard(context), - _uploadFolderCard(context), - if (width > SMALL_DESKTOP) _createPinCard(context), + _ActionCard.uploadFile(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.folderEmptyPage), + _ActionCard.uploadFolder(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.folderEmptyPage), + if (width > SMALL_DESKTOP) + _ActionCard.createPin(context, + page: PlausiblePageView.folderEmptyPage), ], ) ], - ); - }); + ), + ); + } + + Widget _buildHeaderText({ + required BuildContext context, + required String text, + required TextStyle style, + }) { + return Text( + text, + style: style, + ); } - Widget _existingUserEmptyRootFolder(BuildContext context) { - trackPageView( - isRootFolder: true, - isANewUser: false, + Widget _buildDescriptionText({ + required BuildContext context, + required String text, + required TextStyle style, + }) { + return Text( + text, + style: style, + textAlign: TextAlign.center, ); + } +} + +class _NewUserEmptyRootFolder extends StatefulWidget { + final String driveId; + final String parentFolderId; + + const _NewUserEmptyRootFolder({ + required this.driveId, + required this.parentFolderId, + }); + @override + State<_NewUserEmptyRootFolder> createState() => + _NewUserEmptyRootFolderState(); +} + +class _NewUserEmptyRootFolderState extends State<_NewUserEmptyRootFolder> { + @override + initState() { + super.initState(); + PlausibleEventTracker.trackPageview( + page: PlausiblePageView.newUserDriveEmptyPage); + } + + @override + Widget build(BuildContext context) { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - final width = MediaQuery.of(context).size.width; + + const String headerText = 'You\'re on chain!'; + const String descriptionText = + 'You have just made your first blockchain interaction, congrats! You can now use your new drive to manage, share, and organize just about any multimedia file.'; return ScreenTypeLayout.builder( - mobile: (context) { - return SingleChildScrollView( - child: Column( - children: [ - Text( - 'Just look at this shiny new drive!', - style: typography.heading4( - fontWeight: ArFontWeight.bold, - ), - ), - const SizedBox(height: 10), - Text( - 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next.', - style: typography.paragraphLarge( - color: colorTokens.textLow, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 20), - _uploadFileCard(context), - const SizedBox(height: 20), - _uploadFolderCard(context), - const SizedBox(height: 20), - _createFolderCard(context), - const SizedBox(height: 20), - _createPinCard(context), - const SizedBox(height: 20), - ], - ), - ); - }, - desktop: (context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mobile: (context) => SingleChildScrollView( + child: Column( children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 66), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ArDriveImage( - image: AssetImage(Resources.images.login.confetti), - ), - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - 'Just look at this shiny new drive!', - style: typography.display( - fontWeight: ArFontWeight.bold, - ), - ), - const SizedBox(height: 10), - Text( - 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next.', - style: typography.heading5( - color: colorTokens.textLow, - ), - textAlign: TextAlign.center, - ), - ], - ), - ), - ArDriveImage( - image: AssetImage(Resources.images.login.confetti), - ), - ], - ), + _buildHeaderText( + context: context, + text: headerText, + style: typography.heading4(fontWeight: ArFontWeight.bold), + ), + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.paragraphLarge(color: colorTokens.textLow), + ), + const SizedBox(height: 20), + _ActionCard.uploadFile( + context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.newUserDriveEmptyPage, + ), + const SizedBox(height: 20), + _ActionCard.createFolder( + context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.newUserDriveEmptyPage, ), - Row( + ], + ), + ), + desktop: (context) => Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 66), + child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _uploadFileCard(context), - _uploadFolderCard(context), - if (width > SMALL_DESKTOP) _createFolderCard(context), - if (width > LARGE_DESKTOP) _createPinCard(context), + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), + Flexible( + child: Column( + children: [ + _buildHeaderText( + context: context, + text: headerText, + style: + typography.display(fontWeight: ArFontWeight.bold), + ), + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.heading5(color: colorTokens.textLow), + ), + ], + ), + ), + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), ], - ) - ], - ); - }, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _ActionCard.uploadFile(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.newUserDriveEmptyPage), + _ActionCard.createFolder(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.newUserDriveEmptyPage), + ], + ) + ], + ), ); } - Widget _newUserEmptyRootFolder(BuildContext context) { + Widget _buildHeaderText({ + required BuildContext context, + required String text, + required TextStyle style, + }) { + return Text( + text, + style: style, + ); + } + + Widget _buildDescriptionText({ + required BuildContext context, + required String text, + required TextStyle style, + }) { + return Text( + text, + style: style, + textAlign: TextAlign.center, + ); + } +} + +class _ExistingUserEmptyRootFolder extends StatefulWidget { + final String driveId; + final String parentFolderId; + + const _ExistingUserEmptyRootFolder({ + required this.driveId, + required this.parentFolderId, + }); + + @override + State<_ExistingUserEmptyRootFolder> createState() => + _ExistingUserEmptyRootFolderState(); +} + +class _ExistingUserEmptyRootFolderState + extends State<_ExistingUserEmptyRootFolder> { + @override + initState() { + super.initState(); + PlausibleEventTracker.trackPageview( + page: PlausiblePageView.existingUserDriveEmptyPage); + } + + @override + Widget build(BuildContext context) { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - trackPageView( - isRootFolder: true, - isANewUser: true, - ); + final width = MediaQuery.of(context).size.width; + const String headerText = 'Just look at this shiny new drive!'; + const String descriptionText = + 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next.'; return ScreenTypeLayout.builder( - mobile: (context) { - return SingleChildScrollView( - child: Column( - children: [ - Text( - 'You\'re on chain!', - style: typography.heading4( - fontWeight: ArFontWeight.bold, - ), - ), - const SizedBox(height: 10), - Text( - 'You have just made your first blockchain interaction, congrats! You can now use your new drive to manage, share, and organize just about any multimedia file.', - style: typography.paragraphLarge( - color: colorTokens.textLow, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 20), - _uploadFileCard(context), - const SizedBox(height: 20), - _createFolderCard(context), - ], - ), - ); - }, - desktop: (context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mobile: (context) => SingleChildScrollView( + child: Column( children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 66), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ArDriveImage( - image: AssetImage(Resources.images.login.confetti), - ), - Flexible( - child: Column( - children: [ - Text( - 'You\'re on chain!', - style: typography.display( - fontWeight: ArFontWeight.bold, - ), - ), - const SizedBox(height: 10), - Text( - 'You have just made your first blockchain interaction, congrats! You can now use your new drive to manage, share, and organize just about any multimedia file.', - style: typography.heading5( - color: colorTokens.textLow, - ), - textAlign: TextAlign.center, - ), - ], - ), - ), - ArDriveImage( - image: AssetImage(Resources.images.login.confetti), - ), - ], - ), + _buildHeaderText( + context: context, + text: headerText, + style: typography.heading4(fontWeight: ArFontWeight.bold), + ), + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.paragraphLarge(color: colorTokens.textLow), + ), + const SizedBox(height: 20), + _ActionCard.uploadFile( + context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage, ), - Row( + const SizedBox(height: 20), + _ActionCard.uploadFolder(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage), + const SizedBox(height: 20), + _ActionCard.createFolder(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage), + const SizedBox(height: 20), + _ActionCard.createPin(context, + page: PlausiblePageView.existingUserDriveEmptyPage), + const SizedBox(height: 20), + ], + ), + ), + desktop: (context) => Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 66), + child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _uploadFileCard(context), - _createFolderCard(context), + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildHeaderText( + context: context, + text: headerText, + style: + typography.display(fontWeight: ArFontWeight.bold), + ), + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.heading5(color: colorTokens.textLow), + ), + ], + ), + ), + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), ], - ) - ], - ); - }, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _ActionCard.uploadFile(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage), + _ActionCard.uploadFolder(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage), + if (width > SMALL_DESKTOP) + _ActionCard.createFolder(context, + driveId: widget.driveId, + parentFolderId: widget.parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage), + if (width > LARGE_DESKTOP) + _ActionCard.createPin(context, + page: PlausiblePageView.existingUserDriveEmptyPage), + ], + ) + ], + ), ); } - Widget _uploadFileCard( - BuildContext context, - ) { - return _actionCard( + Widget _buildHeaderText({ + required BuildContext context, + required String text, + required TextStyle style, + }) { + return Text( + text, + style: style, + ); + } + + Widget _buildDescriptionText({ + required BuildContext context, + required String text, + required TextStyle style, + }) { + return Text( + text, + style: style, + textAlign: TextAlign.center, + ); + } +} + +class _ActionCard { + static Widget uploadFile( + BuildContext context, { + required String driveId, + required String parentFolderId, + required PlausiblePageView page, + }) { + return _buildActionCard( context: context, title: 'Upload File(s)', description: 'Upload a file, or a selection of files, that are collectively under 100KB into your new drive.', buttonText: 'Upload', onPressed: () { + PlausibleEventTracker.trackClickUploadFileEmptyState(page); + promptToUpload( context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, + driveId: driveId, + parentFolderId: parentFolderId, isFolderUpload: false, ); }, @@ -389,37 +577,45 @@ class _DriveDetailFolderEmptyCardState ); } - Widget _createFolderCard( - BuildContext context, - ) { - return _actionCard( + static Widget createFolder( + BuildContext context, { + required String driveId, + required String parentFolderId, + required PlausiblePageView page, + }) { + return _buildActionCard( context: context, title: 'Create a Folder', description: 'Create a new folder to organize your files. You can create as many folders as you need to keep your drive organized.', buttonText: 'Create Folder', onPressed: () { + PlausibleEventTracker.trackClickCreateFolderEmptyState(page); + promptToCreateFolder(context, - driveId: widget.driveId, parentFolderId: widget.parentFolderId); + driveId: driveId, parentFolderId: parentFolderId); }, icon: ArDriveIcons.iconUploadFolder1(), ); } - Widget _uploadFolderCard( - BuildContext context, - ) { - return _actionCard( + static Widget uploadFolder(BuildContext context, + {required String driveId, + required String parentFolderId, + required PlausiblePageView page}) { + return _buildActionCard( context: context, title: 'Upload a Folder', description: 'Upload existing folders that total less than 100KB from a computer, other file storage apps, or an external HD.', buttonText: 'Upload Folder', onPressed: () { + PlausibleEventTracker.trackClickUploadFolderEmptyState(page); + promptToUpload( context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, + driveId: driveId, + parentFolderId: parentFolderId, isFolderUpload: true, ); }, @@ -427,23 +623,24 @@ class _DriveDetailFolderEmptyCardState ); } - Widget _createPinCard( - BuildContext context, - ) { - return _actionCard( + static Widget createPin(BuildContext context, + {required PlausiblePageView page}) { + return _buildActionCard( context: context, title: 'Create a Pin', description: - 'Pin any permaweb file to your Drive, to create inspiration boards, recipe collections, or NFT compilations. ', + 'Pin any permaweb file to your Drive, to create inspiration boards, recipe collections, or NFT compilations.', buttonText: 'Create Pin', onPressed: () { + PlausibleEventTracker.trackClickCreatePinEmptyState(page); + showPinFileDialog(context: context); }, icon: ArDriveIcons.pinWithCircle(), ); } - Widget _actionCard({ + static Widget _buildActionCard({ required BuildContext context, required String title, required String description, @@ -455,8 +652,8 @@ class _DriveDetailFolderEmptyCardState final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; return ArDriveCard( - width: 248 + 35, - height: 248 + 35, + width: 283, + height: 283, backgroundColor: colorTokens.containerL2, contentPadding: const EdgeInsets.all(31), content: Column( @@ -465,16 +662,13 @@ class _DriveDetailFolderEmptyCardState icon.copyWith(size: 25), Text( title, - style: typography.paragraphXLarge( - fontWeight: ArFontWeight.semiBold, - ), + style: + typography.paragraphXLarge(fontWeight: ArFontWeight.semiBold), ), const SizedBox(height: 10), Text( description, - style: typography.paragraphNormal( - color: colorTokens.textLow, - ), + style: typography.paragraphNormal(color: colorTokens.textLow), textAlign: TextAlign.center, ), const SizedBox(height: 20), @@ -483,7 +677,7 @@ class _DriveDetailFolderEmptyCardState typography: typography, onPressed: onPressed, variant: ButtonVariant.secondary, - ) + ), ], ), ); diff --git a/lib/utils/plausible_event_tracker/plausible_event_tracker.dart b/lib/utils/plausible_event_tracker/plausible_event_tracker.dart index 9c45fc4508..66e8ff7c80 100644 --- a/lib/utils/plausible_event_tracker/plausible_event_tracker.dart +++ b/lib/utils/plausible_event_tracker/plausible_event_tracker.dart @@ -481,6 +481,46 @@ abstract class PlausibleEventTracker { ); } + // clickUploadFileEmptyState, + static Future trackClickUploadFileEmptyState( + PlausiblePageView page, + ) { + return _trackCustomEvent( + page: page, + event: PlausibleCustomEvent.clickUploadFileEmptyState, + ); + } + + // clickCreateFolderEmptyState + static Future trackClickCreateFolderEmptyState( + PlausiblePageView page, + ) { + return _trackCustomEvent( + page: page, + event: PlausibleCustomEvent.clickCreateFolderEmptyState, + ); + } + + // clickUploadFolderEmptyState + static Future trackClickUploadFolderEmptyState( + PlausiblePageView page, + ) { + return _trackCustomEvent( + page: page, + event: PlausibleCustomEvent.clickUploadFolderEmptyState, + ); + } + + // clickCreatePinEmptyState + static Future trackClickCreatePinEmptyState( + PlausiblePageView page, + ) { + return _trackCustomEvent( + page: page, + event: PlausibleCustomEvent.clickCreatePinEmptyState, + ); + } + static Future _track({ required PlausibleApiData data, required PlausibleEventData eventData, From 7c517d0b894de81940ff74967e0f8c57c849a82d Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 30 May 2024 12:36:13 -0300 Subject: [PATCH 07/10] feat(empty state): improve copy and fix icon --- lib/components/folder_create_form.dart | 2 + lib/components/progress_dialog.dart | 2 + .../drive_detail_folder_empty_card.dart | 83 ++++++++++++------- .../ardrive_ui/lib/src/components/modal.dart | 17 ++-- 4 files changed, 67 insertions(+), 37 deletions(-) diff --git a/lib/components/folder_create_form.dart b/lib/components/folder_create_form.dart index 72bcb903c7..ff8f7dd18f 100644 --- a/lib/components/folder_create_form.dart +++ b/lib/components/folder_create_form.dart @@ -76,6 +76,7 @@ class _FolderCreateFormState extends State { showProgressDialog( context, title: appLocalizationsOf(context).creatingFolderEmphasized, + useNewArDriveUI: true, ); } else if (state is FolderCreateSuccess) { Navigator.pop(context); @@ -91,6 +92,7 @@ class _FolderCreateFormState extends State { description: appLocalizationsOf(context).entityAlreadyExists( state.folderName, ), + useNewArDriveUI: true, ); } }, diff --git a/lib/components/progress_dialog.dart b/lib/components/progress_dialog.dart index 86edf8f94a..84f02512cc 100644 --- a/lib/components/progress_dialog.dart +++ b/lib/components/progress_dialog.dart @@ -8,6 +8,7 @@ Future showProgressDialog( BuildContext context, { required String title, List? actions, + bool useNewArDriveUI = false, }) => showArDriveDialog( context, @@ -15,6 +16,7 @@ Future showProgressDialog( content: ProgressDialog( title: title, actions: actions ?? const [], + useNewArDriveUI: useNewArDriveUI, ), ); diff --git a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart index e3e79fdc0e..96d4934f5a 100644 --- a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart +++ b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart @@ -144,7 +144,7 @@ class _EmptyFolderState extends State<_EmptyFolder> { final width = MediaQuery.of(context).size.width; const String headerText = 'Ready to organize your files?'; const String descriptionText = - 'Start by adding some content to your new folder. Explore the various options available to keep your drive neat and efficient.'; + 'This folder is empty. You can move existing files into this folder, or upload new content.'; return ScreenTypeLayout.builder( mobile: (context) => SingleChildScrollView( @@ -159,7 +159,10 @@ class _EmptyFolderState extends State<_EmptyFolder> { _buildDescriptionText( context: context, text: descriptionText, - style: typography.heading5(color: colorTokens.textLow), + style: typography.heading5( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold, + ), ), const SizedBox(height: 20), _ActionCard.uploadFile(context, @@ -202,7 +205,10 @@ class _EmptyFolderState extends State<_EmptyFolder> { _buildDescriptionText( context: context, text: descriptionText, - style: typography.heading5(color: colorTokens.textLow), + style: typography.heading5( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold, + ), ), ], ), @@ -286,7 +292,7 @@ class _NewUserEmptyRootFolderState extends State<_NewUserEmptyRootFolder> { const String headerText = 'You\'re on chain!'; const String descriptionText = - 'You have just made your first blockchain interaction, congrats! You can now use your new drive to manage, share, and organize just about any multimedia file.'; + 'You have just made your first blockchain interaction, congratulations! You can now use your new drive to manage, share, and organize just about any multimedia file. Uploads collectively under 100KB are free!'; return ScreenTypeLayout.builder( mobile: (context) => SingleChildScrollView( @@ -301,7 +307,9 @@ class _NewUserEmptyRootFolderState extends State<_NewUserEmptyRootFolder> { _buildDescriptionText( context: context, text: descriptionText, - style: typography.paragraphLarge(color: colorTokens.textLow), + style: typography.paragraphLarge( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold), ), const SizedBox(height: 20), _ActionCard.uploadFile( @@ -331,21 +339,25 @@ class _NewUserEmptyRootFolderState extends State<_NewUserEmptyRootFolder> { ArDriveImage( image: AssetImage(Resources.images.login.confetti)), Flexible( - child: Column( - children: [ - _buildHeaderText( - context: context, - text: headerText, - style: - typography.display(fontWeight: ArFontWeight.bold), - ), - const SizedBox(height: 10), - _buildDescriptionText( - context: context, - text: descriptionText, - style: typography.heading5(color: colorTokens.textLow), - ), - ], + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + children: [ + _buildHeaderText( + context: context, + text: headerText, + style: + typography.display(fontWeight: ArFontWeight.bold), + ), + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: + typography.heading5(color: colorTokens.textLow), + ), + ], + ), ), ), ArDriveImage( @@ -423,9 +435,10 @@ class _ExistingUserEmptyRootFolderState final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; final width = MediaQuery.of(context).size.width; - const String headerText = 'Just look at this shiny new drive!'; + const String headerText = 'Your brand new drive!'; + // add gesture and link to turbo const String descriptionText = - 'When you are ready to benefit from blazingly fast, unlimited uploading, you can try out Turbo. Until then, check out some of the awesome FREE things you can do next.'; + 'When you are ready to benefit from blazingly fast, unlimited uploading you can try out Turbo. Until then, if your upload is collectively under 100KB, here are some of the awesome FREE things you can do next.'; return ScreenTypeLayout.builder( mobile: (context) => SingleChildScrollView( @@ -440,7 +453,10 @@ class _ExistingUserEmptyRootFolderState _buildDescriptionText( context: context, text: descriptionText, - style: typography.paragraphLarge(color: colorTokens.textLow), + style: typography.paragraphLarge( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold, + ), ), const SizedBox(height: 20), _ActionCard.uploadFile( @@ -490,7 +506,10 @@ class _ExistingUserEmptyRootFolderState _buildDescriptionText( context: context, text: descriptionText, - style: typography.heading5(color: colorTokens.textLow), + style: typography.heading5( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold, + ), ), ], ), @@ -559,9 +578,9 @@ class _ActionCard { }) { return _buildActionCard( context: context, - title: 'Upload File(s)', + title: 'Upload Files', description: - 'Upload a file, or a selection of files, that are collectively under 100KB into your new drive.', + 'Upload a file, or a selection of files into your new drive.', buttonText: 'Upload', onPressed: () { PlausibleEventTracker.trackClickUploadFileEmptyState(page); @@ -586,8 +605,7 @@ class _ActionCard { return _buildActionCard( context: context, title: 'Create a Folder', - description: - 'Create a new folder to organize your files. You can create as many folders as you need to keep your drive organized.', + description: 'Create folders to keep your drive organized.', buttonText: 'Create Folder', onPressed: () { PlausibleEventTracker.trackClickCreateFolderEmptyState(page); @@ -595,7 +613,7 @@ class _ActionCard { promptToCreateFolder(context, driveId: driveId, parentFolderId: parentFolderId); }, - icon: ArDriveIcons.iconUploadFolder1(), + icon: ArDriveIcons.iconNewFolder1(), ); } @@ -607,7 +625,7 @@ class _ActionCard { context: context, title: 'Upload a Folder', description: - 'Upload existing folders that total less than 100KB from a computer, other file storage apps, or an external HD.', + 'Upload existing folders from your computer, other file storage apps, or an external HD.', buttonText: 'Upload Folder', onPressed: () { PlausibleEventTracker.trackClickUploadFolderEmptyState(page); @@ -629,7 +647,7 @@ class _ActionCard { context: context, title: 'Create a Pin', description: - 'Pin any permaweb file to your Drive, to create inspiration boards, recipe collections, or NFT compilations.', + 'Pin any permaweb file to your drive, to create inspiration boards, recipe collections, or NFT galleries.', buttonText: 'Create Pin', onPressed: () { PlausibleEventTracker.trackClickCreatePinEmptyState(page); @@ -668,7 +686,8 @@ class _ActionCard { const SizedBox(height: 10), Text( description, - style: typography.paragraphNormal(color: colorTokens.textLow), + style: typography.paragraphNormal( + color: colorTokens.textMid, fontWeight: ArFontWeight.semiBold), textAlign: TextAlign.center, ), const SizedBox(height: 20), diff --git a/packages/ardrive_ui/lib/src/components/modal.dart b/packages/ardrive_ui/lib/src/components/modal.dart index b32fd93740..9fec76a30f 100644 --- a/packages/ardrive_ui/lib/src/components/modal.dart +++ b/packages/ardrive_ui/lib/src/components/modal.dart @@ -651,15 +651,22 @@ Future showStandardDialog( required String description, List? actions, bool barrierDismissible = true, + bool useNewArDriveUI = false, }) { return showAnimatedDialog( context, barrierDismissible: barrierDismissible, - content: ArDriveStandardModal( - description: description, - title: title, - actions: actions, - ), + content: useNewArDriveUI + ? ArDriveStandardModalNew( + description: description, + title: title, + actions: actions, + ) + : ArDriveStandardModal( + description: description, + title: title, + actions: actions, + ), ); } From 23a2ed7a3ba9b9d79ccb660c02529418707bc277 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 30 May 2024 14:14:50 -0300 Subject: [PATCH 08/10] feat(empty state): implements the plausible page view wrapper --- .../drive_detail_folder_empty_card.dart | 587 ++++++++---------- lib/pages/drive_detail/drive_detail_page.dart | 1 + .../plausible_page_view_wrapper.dart | 37 ++ 3 files changed, 306 insertions(+), 319 deletions(-) create mode 100644 lib/shared/components/plausible_page_view_wrapper.dart diff --git a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart index 96d4934f5a..2a87e4cc6a 100644 --- a/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart +++ b/lib/pages/drive_detail/components/drive_detail_folder_empty_card.dart @@ -21,27 +21,6 @@ class DriveDetailFolderEmptyCard extends StatefulWidget { class _DriveDetailFolderEmptyCardState extends State { - bool? _hasTrackedPageView = false; - - void trackPageView({ - required bool isRootFolder, - required bool isANewUser, - }) { - if (_hasTrackedPageView == false) { - if (isRootFolder && isANewUser) { - PlausibleEventTracker.trackPageview( - page: PlausiblePageView.newUserDriveEmptyPage); - } else if (isRootFolder && !isANewUser) { - PlausibleEventTracker.trackPageview( - page: PlausiblePageView.existingUserDriveEmptyPage); - } else { - PlausibleEventTracker.trackPageview( - page: PlausiblePageView.folderEmptyPage); - } - _hasTrackedPageView = true; - } - } - @override Widget build(BuildContext context) => buildArDriveCard(context); @@ -116,7 +95,7 @@ class _DriveDetailFolderEmptyCardState } } -class _EmptyFolder extends StatefulWidget { +class _EmptyFolder extends StatelessWidget { final String driveId; final String parentFolderId; @@ -125,18 +104,6 @@ class _EmptyFolder extends StatefulWidget { required this.parentFolderId, }); - @override - State<_EmptyFolder> createState() => _EmptyFolderState(); -} - -class _EmptyFolderState extends State<_EmptyFolder> { - @override - initState() { - super.initState(); - PlausibleEventTracker.trackPageview( - page: PlausiblePageView.folderEmptyPage); - } - @override Widget build(BuildContext context) { final typography = ArDriveTypographyNew.of(context); @@ -146,95 +113,98 @@ class _EmptyFolderState extends State<_EmptyFolder> { const String descriptionText = 'This folder is empty. You can move existing files into this folder, or upload new content.'; - return ScreenTypeLayout.builder( - mobile: (context) => SingleChildScrollView( - child: Column( - children: [ - _buildHeaderText( - context: context, - text: headerText, - style: typography.display(fontWeight: ArFontWeight.bold), - ), - const SizedBox(height: 10), - _buildDescriptionText( - context: context, - text: descriptionText, - style: typography.heading5( - color: colorTokens.textLow, - fontWeight: ArFontWeight.semiBold, + return PlausiblePageViewWrapper( + pageView: PlausiblePageView.folderEmptyPage, + child: ScreenTypeLayout.builder( + mobile: (context) => SingleChildScrollView( + child: Column( + children: [ + _buildHeaderText( + context: context, + text: headerText, + style: typography.display(fontWeight: ArFontWeight.bold), ), - ), - const SizedBox(height: 20), - _ActionCard.uploadFile(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.folderEmptyPage), - const SizedBox(height: 20), - _ActionCard.uploadFolder(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.folderEmptyPage), - const SizedBox(height: 20), - _ActionCard.createPin(context, - page: PlausiblePageView.folderEmptyPage), - ], - ), - ), - desktop: (context) => Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 66), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ArDriveImage( - image: AssetImage(Resources.images.login.confetti)), - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - _buildHeaderText( - context: context, - text: headerText, - style: - typography.display(fontWeight: ArFontWeight.bold), - ), - const SizedBox(height: 10), - _buildDescriptionText( - context: context, - text: descriptionText, - style: typography.heading5( - color: colorTokens.textLow, - fontWeight: ArFontWeight.semiBold, - ), - ), - ], - ), + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.heading5( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold, ), - ArDriveImage( - image: AssetImage(Resources.images.login.confetti)), - ], - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ + ), + const SizedBox(height: 20), _ActionCard.uploadFile(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, + driveId: driveId, + parentFolderId: parentFolderId, page: PlausiblePageView.folderEmptyPage), + const SizedBox(height: 20), _ActionCard.uploadFolder(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.folderEmptyPage), + const SizedBox(height: 20), + _ActionCard.createPin(context, page: PlausiblePageView.folderEmptyPage), - if (width > SMALL_DESKTOP) - _ActionCard.createPin(context, - page: PlausiblePageView.folderEmptyPage), ], - ) - ], + ), + ), + desktop: (context) => Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 66), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildHeaderText( + context: context, + text: headerText, + style: + typography.display(fontWeight: ArFontWeight.bold), + ), + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.heading5( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold, + ), + ), + ], + ), + ), + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _ActionCard.uploadFile(context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.folderEmptyPage), + _ActionCard.uploadFolder(context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.folderEmptyPage), + if (width > SMALL_DESKTOP) + _ActionCard.createPin(context, + page: PlausiblePageView.folderEmptyPage), + ], + ) + ], + ), ), ); } @@ -263,7 +233,7 @@ class _EmptyFolderState extends State<_EmptyFolder> { } } -class _NewUserEmptyRootFolder extends StatefulWidget { +class _NewUserEmptyRootFolder extends StatelessWidget { final String driveId; final String parentFolderId; @@ -272,19 +242,6 @@ class _NewUserEmptyRootFolder extends StatefulWidget { required this.parentFolderId, }); - @override - State<_NewUserEmptyRootFolder> createState() => - _NewUserEmptyRootFolderState(); -} - -class _NewUserEmptyRootFolderState extends State<_NewUserEmptyRootFolder> { - @override - initState() { - super.initState(); - PlausibleEventTracker.trackPageview( - page: PlausiblePageView.newUserDriveEmptyPage); - } - @override Widget build(BuildContext context) { final typography = ArDriveTypographyNew.of(context); @@ -294,91 +251,94 @@ class _NewUserEmptyRootFolderState extends State<_NewUserEmptyRootFolder> { const String descriptionText = 'You have just made your first blockchain interaction, congratulations! You can now use your new drive to manage, share, and organize just about any multimedia file. Uploads collectively under 100KB are free!'; - return ScreenTypeLayout.builder( - mobile: (context) => SingleChildScrollView( - child: Column( + return PlausiblePageViewWrapper( + pageView: PlausiblePageView.newUserDriveEmptyPage, + child: ScreenTypeLayout.builder( + mobile: (context) => SingleChildScrollView( + child: Column( + children: [ + _buildHeaderText( + context: context, + text: headerText, + style: typography.heading4(fontWeight: ArFontWeight.bold), + ), + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.paragraphLarge( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold), + ), + const SizedBox(height: 20), + _ActionCard.uploadFile( + context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.newUserDriveEmptyPage, + ), + const SizedBox(height: 20), + _ActionCard.createFolder( + context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.newUserDriveEmptyPage, + ), + ], + ), + ), + desktop: (context) => Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _buildHeaderText( - context: context, - text: headerText, - style: typography.heading4(fontWeight: ArFontWeight.bold), - ), - const SizedBox(height: 10), - _buildDescriptionText( - context: context, - text: descriptionText, - style: typography.paragraphLarge( - color: colorTokens.textLow, - fontWeight: ArFontWeight.semiBold), - ), - const SizedBox(height: 20), - _ActionCard.uploadFile( - context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.newUserDriveEmptyPage, - ), - const SizedBox(height: 20), - _ActionCard.createFolder( - context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.newUserDriveEmptyPage, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 66), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), + Flexible( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + children: [ + _buildHeaderText( + context: context, + text: headerText, + style: typography.display( + fontWeight: ArFontWeight.bold), + ), + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: + typography.heading5(color: colorTokens.textLow), + ), + ], + ), + ), + ), + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), + ], + ), ), - ], - ), - ), - desktop: (context) => Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 66), - child: Row( + Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - ArDriveImage( - image: AssetImage(Resources.images.login.confetti)), - Flexible( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Column( - children: [ - _buildHeaderText( - context: context, - text: headerText, - style: - typography.display(fontWeight: ArFontWeight.bold), - ), - const SizedBox(height: 10), - _buildDescriptionText( - context: context, - text: descriptionText, - style: - typography.heading5(color: colorTokens.textLow), - ), - ], - ), - ), - ), - ArDriveImage( - image: AssetImage(Resources.images.login.confetti)), + _ActionCard.uploadFile(context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.newUserDriveEmptyPage), + _ActionCard.createFolder(context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.newUserDriveEmptyPage), ], - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _ActionCard.uploadFile(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.newUserDriveEmptyPage), - _ActionCard.createFolder(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.newUserDriveEmptyPage), - ], - ) - ], + ) + ], + ), ), ); } @@ -407,7 +367,7 @@ class _NewUserEmptyRootFolderState extends State<_NewUserEmptyRootFolder> { } } -class _ExistingUserEmptyRootFolder extends StatefulWidget { +class _ExistingUserEmptyRootFolder extends StatelessWidget { final String driveId; final String parentFolderId; @@ -416,20 +376,6 @@ class _ExistingUserEmptyRootFolder extends StatefulWidget { required this.parentFolderId, }); - @override - State<_ExistingUserEmptyRootFolder> createState() => - _ExistingUserEmptyRootFolderState(); -} - -class _ExistingUserEmptyRootFolderState - extends State<_ExistingUserEmptyRootFolder> { - @override - initState() { - super.initState(); - PlausibleEventTracker.trackPageview( - page: PlausiblePageView.existingUserDriveEmptyPage); - } - @override Widget build(BuildContext context) { final typography = ArDriveTypographyNew.of(context); @@ -440,107 +386,110 @@ class _ExistingUserEmptyRootFolderState const String descriptionText = 'When you are ready to benefit from blazingly fast, unlimited uploading you can try out Turbo. Until then, if your upload is collectively under 100KB, here are some of the awesome FREE things you can do next.'; - return ScreenTypeLayout.builder( - mobile: (context) => SingleChildScrollView( - child: Column( - children: [ - _buildHeaderText( - context: context, - text: headerText, - style: typography.heading4(fontWeight: ArFontWeight.bold), - ), - const SizedBox(height: 10), - _buildDescriptionText( - context: context, - text: descriptionText, - style: typography.paragraphLarge( - color: colorTokens.textLow, - fontWeight: ArFontWeight.semiBold, + return PlausiblePageViewWrapper( + pageView: PlausiblePageView.existingUserDriveEmptyPage, + child: ScreenTypeLayout.builder( + mobile: (context) => SingleChildScrollView( + child: Column( + children: [ + _buildHeaderText( + context: context, + text: headerText, + style: typography.heading4(fontWeight: ArFontWeight.bold), ), - ), - const SizedBox(height: 20), - _ActionCard.uploadFile( - context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.existingUserDriveEmptyPage, - ), - const SizedBox(height: 20), - _ActionCard.uploadFolder(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.existingUserDriveEmptyPage), - const SizedBox(height: 20), - _ActionCard.createFolder(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.existingUserDriveEmptyPage), - const SizedBox(height: 20), - _ActionCard.createPin(context, - page: PlausiblePageView.existingUserDriveEmptyPage), - const SizedBox(height: 20), - ], + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.paragraphLarge( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold, + ), + ), + const SizedBox(height: 20), + _ActionCard.uploadFile( + context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage, + ), + const SizedBox(height: 20), + _ActionCard.uploadFolder(context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage), + const SizedBox(height: 20), + _ActionCard.createFolder(context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage), + const SizedBox(height: 20), + _ActionCard.createPin(context, + page: PlausiblePageView.existingUserDriveEmptyPage), + const SizedBox(height: 20), + ], + ), ), - ), - desktop: (context) => Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 66), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ArDriveImage( - image: AssetImage(Resources.images.login.confetti)), - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - _buildHeaderText( - context: context, - text: headerText, - style: - typography.display(fontWeight: ArFontWeight.bold), - ), - const SizedBox(height: 10), - _buildDescriptionText( - context: context, - text: descriptionText, - style: typography.heading5( - color: colorTokens.textLow, - fontWeight: ArFontWeight.semiBold, + desktop: (context) => Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 66), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildHeaderText( + context: context, + text: headerText, + style: + typography.display(fontWeight: ArFontWeight.bold), ), - ), - ], + const SizedBox(height: 10), + _buildDescriptionText( + context: context, + text: descriptionText, + style: typography.heading5( + color: colorTokens.textLow, + fontWeight: ArFontWeight.semiBold, + ), + ), + ], + ), ), - ), - ArDriveImage( - image: AssetImage(Resources.images.login.confetti)), - ], + ArDriveImage( + image: AssetImage(Resources.images.login.confetti)), + ], + ), ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _ActionCard.uploadFile(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.existingUserDriveEmptyPage), - _ActionCard.uploadFolder(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, - page: PlausiblePageView.existingUserDriveEmptyPage), - if (width > SMALL_DESKTOP) - _ActionCard.createFolder(context, - driveId: widget.driveId, - parentFolderId: widget.parentFolderId, + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _ActionCard.uploadFile(context, + driveId: driveId, + parentFolderId: parentFolderId, page: PlausiblePageView.existingUserDriveEmptyPage), - if (width > LARGE_DESKTOP) - _ActionCard.createPin(context, + _ActionCard.uploadFolder(context, + driveId: driveId, + parentFolderId: parentFolderId, page: PlausiblePageView.existingUserDriveEmptyPage), - ], - ) - ], + if (width > SMALL_DESKTOP) + _ActionCard.createFolder(context, + driveId: driveId, + parentFolderId: parentFolderId, + page: PlausiblePageView.existingUserDriveEmptyPage), + if (width > LARGE_DESKTOP) + _ActionCard.createPin(context, + page: PlausiblePageView.existingUserDriveEmptyPage), + ], + ) + ], + ), ), ); } diff --git a/lib/pages/drive_detail/drive_detail_page.dart b/lib/pages/drive_detail/drive_detail_page.dart index b338f2c046..6c9294d152 100644 --- a/lib/pages/drive_detail/drive_detail_page.dart +++ b/lib/pages/drive_detail/drive_detail_page.dart @@ -38,6 +38,7 @@ import 'package:ardrive/pages/drive_detail/components/unpreviewable_content.dart import 'package:ardrive/search/search_modal.dart'; import 'package:ardrive/search/search_text_field.dart'; import 'package:ardrive/services/services.dart'; +import 'package:ardrive/shared/components/plausible_page_view_wrapper.dart'; import 'package:ardrive/sharing/sharing_file_listener.dart'; import 'package:ardrive/sync/domain/cubit/sync_cubit.dart'; import 'package:ardrive/theme/theme.dart'; diff --git a/lib/shared/components/plausible_page_view_wrapper.dart b/lib/shared/components/plausible_page_view_wrapper.dart new file mode 100644 index 0000000000..a64056d548 --- /dev/null +++ b/lib/shared/components/plausible_page_view_wrapper.dart @@ -0,0 +1,37 @@ +import 'package:ardrive/utils/plausible_event_tracker/plausible_event_tracker.dart'; +import 'package:flutter/material.dart'; + +/// A wrapper widget that tracks a page view event when the widget is built. +/// +/// This widget should be used to wrap the root widget of a page to track a page view event. +class PlausiblePageViewWrapper extends StatefulWidget { + const PlausiblePageViewWrapper({ + super.key, + required this.pageView, + required this.child, + this.props, + }); + + final PlausiblePageView pageView; + final Widget child; + final Map? props; + + @override + State createState() => + _PlausiblePageViewWrapperState(); +} + +class _PlausiblePageViewWrapperState extends State { + @override + void initState() { + PlausibleEventTracker.trackPageview( + page: widget.pageView, props: widget.props); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return widget.child; + } +} From 8678e4e00f7ed14a1d77008a5198be8c5eed0b1b Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Fri, 31 May 2024 13:48:14 -0300 Subject: [PATCH 09/10] bump version and release notes --- android/fastlane/metadata/android/en-US/changelogs/130.txt | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 android/fastlane/metadata/android/en-US/changelogs/130.txt diff --git a/android/fastlane/metadata/android/en-US/changelogs/130.txt b/android/fastlane/metadata/android/en-US/changelogs/130.txt new file mode 100644 index 0000000000..5fbfa229a1 --- /dev/null +++ b/android/fastlane/metadata/android/en-US/changelogs/130.txt @@ -0,0 +1,2 @@ +- Improved handling of a few common errors +- Enhanced empty drive and folder view \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 94efce16a6..5105700f0f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Secure, permanent storage publish_to: 'none' -version: 2.46.0 +version: 2.47.0 environment: sdk: '>=3.2.0 <4.0.0' From 52b6fd2295ca885d505c5de2c4dfc5b23254f22a Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Fri, 31 May 2024 13:55:46 -0300 Subject: [PATCH 10/10] Update 130.txt --- android/fastlane/metadata/android/en-US/changelogs/130.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/fastlane/metadata/android/en-US/changelogs/130.txt b/android/fastlane/metadata/android/en-US/changelogs/130.txt index 5fbfa229a1..5a6f1ea623 100644 --- a/android/fastlane/metadata/android/en-US/changelogs/130.txt +++ b/android/fastlane/metadata/android/en-US/changelogs/130.txt @@ -1,2 +1,2 @@ -- Improved handling of a few common errors -- Enhanced empty drive and folder view \ No newline at end of file +- Improves handling of a few common errors +- Enhances empty drive and folder view \ No newline at end of file