diff --git a/.github/workflows/deploy-dev.yaml b/.github/workflows/deploy-dev.yaml index 308b10c4f7..0be010ac5b 100644 --- a/.github/workflows/deploy-dev.yaml +++ b/.github/workflows/deploy-dev.yaml @@ -20,7 +20,7 @@ jobs: persist-credentials: false - uses: subosito/flutter-action@v1 with: - flutter-version: '2.2.x' + flutter-version: '2.2.3' - name: Build app run: | flutter pub get diff --git a/.github/workflows/deploy-prod.yaml b/.github/workflows/deploy-prod.yaml index 52f41e03b1..9c1103d2dc 100644 --- a/.github/workflows/deploy-prod.yaml +++ b/.github/workflows/deploy-prod.yaml @@ -20,7 +20,7 @@ jobs: persist-credentials: false - uses: subosito/flutter-action@v1 with: - flutter-version: '2.2.x' + flutter-version: '2.2.3' - name: Build app run: | flutter pub get diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 8e38f2350e..ca4c582d75 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -28,7 +28,7 @@ jobs: persist-credentials: false - uses: subosito/flutter-action@v1 with: - flutter-version: '2.2.x' + flutter-version: '2.2.3' - name: Build app run: | flutter pub get diff --git a/lib/blocs/sync/sync_cubit.dart b/lib/blocs/sync/sync_cubit.dart index b45f11ad66..b1743e7c1e 100644 --- a/lib/blocs/sync/sync_cubit.dart +++ b/lib/blocs/sync/sync_cubit.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:math'; import 'package:ardrive/entities/constants.dart'; import 'package:ardrive/entities/entities.dart'; @@ -92,13 +91,18 @@ class SyncCubit extends Cubit { } // Sync the contents of each drive attached in the app. - final driveIds = await _driveDao.allDrives().map((d) => d.id).get(); - final driveSyncProcesses = driveIds - .map((driveId) => _syncDrive(driveId).onError((error, stackTrace) { - print('Error syncing drive with id $driveId'); - print(error.toString() + stackTrace.toString()); - addError(error!); - })); + final drives = await _driveDao.allDrives().map((d) => d).get(); + final currentBlockHeight = await arweave.getCurrentBlockHeight(); + + final driveSyncProcesses = drives.map((drive) => _syncDrive( + drive.id, + lastBlockHeight: drive.lastBlockHeight!, + currentBlockheight: currentBlockHeight, + ).onError((error, stackTrace) { + print('Error syncing drive with id ${drive.id}'); + print(error.toString() + stackTrace.toString()); + addError(error!); + })); await Future.wait(driveSyncProcesses); await Future.wait([ @@ -112,7 +116,12 @@ class SyncCubit extends Cubit { emit(SyncIdle()); } - Future _syncDrive(String driveId) async { + Future _syncDrive( + String driveId, { + required int currentBlockheight, + required int lastBlockHeight, + String? syncCursor, + }) async { final drive = await _driveDao.driveById(driveId: driveId).getSingle(); final owner = await arweave.getOwnerForDriveEntityWithId(driveId); SecretKey? driveKey; @@ -126,16 +135,10 @@ class SyncCubit extends Cubit { return; } } - final entityHistory = await _arweave.getNewEntitiesForDrive( drive.id, - // Syncs from lastBlockHeight - 5 and paginates through them using the syncCursor - // Starts syncing from lastBlock - 5. 5 is an arbitrary position, - // we are just starting 5 blocks before the lastBlockHeight to make sure it - // picks up all files. 'after' indicates the cursor where it should start - // syncing from. For first sync 'after' should be null or an empty string. - lastBlockHeight: max(drive.lastBlockHeight! - 5, drive.lastBlockHeight!), - after: drive.syncCursor, + lastBlockHeight: lastBlockHeight, + after: syncCursor, driveKey: driveKey, owner: owner, ); @@ -144,15 +147,15 @@ class SyncCubit extends Cubit { final newEntities = entityHistory.blockHistory .map((b) => b.entities) .expand((entities) => entities); - - //Handle newEntities being empty, i.e; There's nothing more to sync - if (newEntities.isEmpty) { - //Reset the sync cursor after every sync to pick up files from other instances of the app. - //(Different tab, different window, mobile, desktop etc) + // Handle newEntities being empty, i.e; There's nothing more to sync + if ((newEntities.isEmpty && entityHistory.cursor == null)) { + // Reset the sync cursor after every sync to pick up files from other instances of the app. + // (Different tab, different window, mobile, desktop etc) await _driveDao.writeToDrive(DrivesCompanion( - id: Value(drive.id), - lastBlockHeight: Value(entityHistory.lastBlockHeight), - syncCursor: Value(null))); + id: Value(drive.id), + lastBlockHeight: Value(currentBlockheight), + syncCursor: Value(null), + )); emit(SyncEmpty()); return; } @@ -165,7 +168,7 @@ class SyncCubit extends Cubit { final latestFileRevisions = await _addNewFileEntityRevisions( driveId, newEntities.whereType()); - //Check and handle cases where there's no more revisions + // Check and handle cases where there's no more revisions final updatedDrive = latestDriveRevision != null ? await _computeRefreshedDriveFromRevision(latestDriveRevision) : null; @@ -191,27 +194,23 @@ class SyncCubit extends Cubit { }); await generateFsEntryPaths(driveId, updatedFoldersById, updatedFilesById); - //Saves lastBlockHeight to query from for next sync. syncCursor is used to - //paginate through results. - await _driveDao.writeToDrive(DrivesCompanion( - id: Value(drive.id), - lastBlockHeight: Value(entityHistory.lastBlockHeight), - syncCursor: Value(entityHistory.cursor))); }); - //In case of very large drives, instead of waiting 2 minutes for the next sync, - //check if there's more to sync and start syncing here itself - if (entityHistory.cursor != null) { - await _syncDrive(driveId); - } + + // If there are more results to process, recurse. + await _syncDrive( + driveId, + syncCursor: entityHistory.cursor, + lastBlockHeight: lastBlockHeight, + currentBlockheight: currentBlockheight, + ); } /// Computes the new drive revisions from the provided entities, inserts them into the database, /// and returns the latest revision. Future _addNewDriveEntityRevisions( - Iterable newEntities, { - String? owner, - }) async { + Iterable newEntities, + ) async { DriveRevisionsCompanion? latestRevision; final newRevisions = []; @@ -315,7 +314,8 @@ class SyncCubit extends Cubit { final newRevisions = []; for (final entity in newEntities) { - if (!latestRevisions.containsKey(entity.id)) { + if (!latestRevisions.containsKey(entity.id) && + entity.parentFolderId != null) { final revisions = await _driveDao .latestFileRevisionByFileId(driveId: driveId, fileId: entity.id!) .getSingleOrNull(); @@ -329,6 +329,9 @@ class SyncCubit extends Cubit { if (revisionPerformedAction == null) { continue; } + // If Parent-Folder-Id is missing for a file, put it in the rootfolder + + entity.parentFolderId = entity.parentFolderId ?? rootPath; final revision = entity.toRevisionCompanion(performedAction: revisionPerformedAction); @@ -497,22 +500,25 @@ class SyncCubit extends Cubit { final staleOrphanFiles = filesByIdMap.values .where((f) => !foldersByIdMap.containsKey(f.parentFolderId)); for (final staleOrphanFile in staleOrphanFiles) { - final parentPath = await _driveDao - .folderById( - driveId: driveId, folderId: staleOrphanFile.parentFolderId.value) - .map((f) => f.path) - .getSingleOrNull(); + if (staleOrphanFile.parentFolderId.value.isNotEmpty) { + final parentPath = await _driveDao + .folderById( + driveId: driveId, + folderId: staleOrphanFile.parentFolderId.value) + .map((f) => f.path) + .getSingleOrNull(); - if (parentPath != null) { - final filePath = parentPath + '/' + staleOrphanFile.name.value; + if (parentPath != null) { + final filePath = parentPath + '/' + staleOrphanFile.name.value; - await _driveDao.writeToFile(FileEntriesCompanion( - id: staleOrphanFile.id, - driveId: staleOrphanFile.driveId, - path: Value(filePath))); - } else { - print( - 'Stale orphan file ${staleOrphanFile.id.value} parent folder ${staleOrphanFile.parentFolderId.value} could not be found.'); + await _driveDao.writeToFile(FileEntriesCompanion( + id: staleOrphanFile.id, + driveId: staleOrphanFile.driveId, + path: Value(filePath))); + } else { + print( + 'Stale orphan file ${staleOrphanFile.id.value} parent folder ${staleOrphanFile.parentFolderId.value} could not be found.'); + } } } } diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index 817b708e1f..db50a9d051 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -104,8 +104,9 @@ class UploadCubit extends Cubit { return; } emit(UploadPreparationInProgress()); - final sizeLimit = - _targetDrive.isPrivate ? math.pow(10, 8) : 1.25 * math.pow(10, 9); + final sizeLimit = (_targetDrive.isPrivate + ? math.pow(10, 8) + : 1.25 * math.pow(10, 9)) as int; final tooLargeFiles = [ for (final file in files) if (await file.length() > sizeLimit) file.name diff --git a/lib/models/queries/drive_queries.moor b/lib/models/queries/drive_queries.moor index 818ef5f130..9693cb7726 100644 --- a/lib/models/queries/drive_queries.moor +++ b/lib/models/queries/drive_queries.moor @@ -32,7 +32,8 @@ folderById: WHERE driveId = :driveId AND id = :folderId; folderWithPath: SELECT * FROM folder_entries - WHERE driveId = :driveId AND path = :path; + WHERE driveId = :driveId AND path = :path + LIMIT 1; foldersInFolder ($order = ''): SELECT * FROM folder_entries diff --git a/lib/services/arweave/arweave_service.dart b/lib/services/arweave/arweave_service.dart index 4aa8d4dab0..b86e7ded08 100644 --- a/lib/services/arweave/arweave_service.dart +++ b/lib/services/arweave/arweave_service.dart @@ -22,6 +22,9 @@ class ArweaveService { .get('wallet/$address/balance') .then((res) => BigInt.parse(res.body)); + Future getCurrentBlockHeight() => + client.api.get('/').then((res) => json.decode(res.body)['height']); + /// Returns the pending transaction fees of the specified address that is not reflected by `getWalletBalance()`. Future getPendingTxFees(String address) async { final query = await _gql.execute(PendingTxFeesQuery( @@ -72,6 +75,7 @@ class ArweaveService { // If we encounter a transaction that has yet to be mined, we stop moving through history. // We can continue once the transaction is mined. if (transaction.block == null) { + // TODO: Revisit break; } @@ -97,7 +101,7 @@ class ArweaveService { driveKey: driveKey, ); } - + //TODO: Revisit if (blockHistory.isEmpty || transaction.block!.height != blockHistory.last.blockHeight) { blockHistory.add(BlockEntities(transaction.block!.height)); diff --git a/pubspec.lock b/pubspec.lock index 8b4b06939f..265172f5b3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -58,21 +58,21 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.1" + version: "2.6.1" bloc: dependency: transitive description: name: bloc url: "https://pub.dartlang.org" source: hosted - version: "7.2.1" + version: "8.0.0-dev.3" bloc_test: dependency: "direct dev" description: name: bloc_test url: "https://pub.dartlang.org" source: hosted - version: "8.2.0" + version: "9.0.0-dev.3" boolean_selector: dependency: transitive description: @@ -156,7 +156,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.3.1" + version: "1.2.0" checked_yaml: dependency: transitive description: @@ -170,7 +170,7 @@ packages: name: cli_util url: "https://pub.dartlang.org" source: hosted - version: "0.3.3" + version: "0.3.5" clock: dependency: transitive description: @@ -226,7 +226,7 @@ packages: name: cryptography url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" dart_style: dependency: transitive description: @@ -268,14 +268,14 @@ packages: name: file url: "https://pub.dartlang.org" source: hosted - version: "6.1.2" + version: "6.1.0" file_selector: dependency: "direct main" description: name: file_selector url: "https://pub.dartlang.org" source: hosted - version: "0.8.2" + version: "0.8.2+1" file_selector_macos: dependency: "direct main" description: @@ -322,7 +322,7 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "7.3.0" + version: "8.0.0-dev.2" flutter_driver: dependency: "direct dev" description: flutter @@ -403,7 +403,7 @@ packages: name: gql url: "https://pub.dartlang.org" source: hosted - version: "0.13.0" + version: "0.13.1-alpha+1633799193898" gql_code_builder: dependency: transitive description: @@ -550,14 +550,14 @@ packages: name: mime url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" mocktail: dependency: "direct dev" description: name: mocktail url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.2.0" moor: dependency: "direct main" description: @@ -599,7 +599,7 @@ packages: name: package_info_plus url: "https://pub.dartlang.org" source: hosted - version: "1.0.6" + version: "1.3.0" package_info_plus_linux: dependency: transitive description: @@ -613,7 +613,7 @@ packages: name: package_info_plus_macos url: "https://pub.dartlang.org" source: hosted - version: "1.1.1" + version: "1.3.0" package_info_plus_platform_interface: dependency: transitive description: @@ -634,7 +634,7 @@ packages: name: package_info_plus_windows url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.0.4" path: dependency: transitive description: @@ -648,7 +648,7 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.4" path_provider_linux: dependency: transitive description: @@ -697,7 +697,7 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" pointycastle: dependency: transitive description: @@ -718,7 +718,7 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.2.3" + version: "4.2.1" provider: dependency: transitive description: @@ -835,7 +835,7 @@ packages: name: sqlite3 url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.1.2" sqlparser: dependency: transitive description: @@ -891,21 +891,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.17.10" + version: "1.16.8" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.3.0" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.0" + version: "0.3.19" timeago: dependency: "direct main" description: @@ -933,7 +933,7 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.12" + version: "6.0.10" url_launcher_linux: dependency: transitive description: @@ -975,7 +975,7 @@ packages: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "3.0.4" + version: "3.0.5" vector_math: dependency: transitive description: @@ -989,7 +989,7 @@ packages: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "7.1.1" + version: "6.2.0" watcher: dependency: transitive description: @@ -1024,7 +1024,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.2.9" + version: "2.2.10" xdg_directories: dependency: transitive description: @@ -1040,5 +1040,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" \ No newline at end of file + dart: ">=2.13.0 <3.0.0" + flutter: ">=2.2.0" diff --git a/pubspec.yaml b/pubspec.yaml index 908a641ebc..52561c70bf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ publish_to: 'none' # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.2.2 +version: 1.2.3 environment: sdk: '>=2.12.0 <3.0.0' @@ -29,7 +29,7 @@ dependencies: url: https://github.com/ardriveapp/arweave-dart ref: fbd54e119b7e0357bd688a91aa49532c520e5b3a cryptography: ^2.0.1 - flutter_bloc: ^7.0.1 + flutter_bloc: ^8.0.0-dev.2 flutter_portal: ^0.4.0 file_selector: ^0.8.2 file_selector_web: ^0.8.1 @@ -38,7 +38,7 @@ dependencies: google_fonts: ^2.1.0 intersperse: ^2.0.0 intl: ^0.17.0 - json_annotation: ^4.0.1 + json_annotation: ^4.1.0 mime: ^1.0.0 moor: ^4.4.1 path_provider: ^2.0.2 @@ -49,7 +49,7 @@ dependencies: url_launcher: ^6.0.6 uuid: ^3.0.4 http_client: ^1.5.1 - flutter_dropzone: ^2.0.1 + flutter_dropzone: ^2.1.0 responsive_builder: ^0.4.1 package_info_plus: ^1.0.3 js: ^0.6.3 @@ -62,11 +62,11 @@ dev_dependencies: flutter_driver: sdk: flutter test: ^1.16.8 - bloc_test: ^8.0.2 + bloc_test: ^9.0.0-dev.3 build_runner: ^2.0.4 moor_generator: ^4.4.1 - mocktail: ^0.1.4 - json_serializable: ^4.1.3 + mocktail: ^0.2.0 + json_serializable: ^4.1.0 dependency_overrides: meta: 1.7.0 diff --git a/web/index.html b/web/index.html index f6ba7f4491..b48e05dffd 100644 --- a/web/index.html +++ b/web/index.html @@ -35,7 +35,7 @@ - +