Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 87 additions & 51 deletions lib/providers/folder_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:icarus/providers/collab/remote_library_provider.dart';
import 'package:icarus/providers/library_workspace_provider.dart';
import 'package:icarus/providers/pinned_items_provider.dart';
import 'package:icarus/providers/strategy_provider.dart';
import 'package:icarus/services/cloud_library_action.dart';
import 'package:icarus/strategy/strategy_models.dart';
import 'package:icarus/strategy/strategy_page_models.dart';
import 'package:uuid/uuid.dart';
Expand Down Expand Up @@ -144,26 +145,32 @@ class FolderProvider extends Notifier<String?> {
.firstOrNull;
}

Future<void> deleteFolder(
Future<CloudLibraryActionResult> deleteFolder(
String folderID, {
LibraryWorkspace? workspace,
}) async {
final targetWorkspace = workspace ?? _currentWorkspace;
if (targetWorkspace == LibraryWorkspace.cloud) {
try {
await ref.read(convexStrategyRepositoryProvider).deleteFolder(folderID);
} catch (error, stackTrace) {
await _maybeReportCloudUnauthenticated(
source: 'folder:delete',
error: error,
stackTrace: stackTrace,
);
return;
}
final result = await _runCloudAction(
action: () async {
await ref
.read(convexStrategyRepositoryProvider)
.deleteFolder(folderID);
return true;
},
source: 'folder:delete',
failureMessage: "Couldn't delete this cloud folder. Try again.",
);
if (!result.didSucceed) return result;

await ref.read(pinnedItemsProvider.notifier).removePin(folderID);
if (_currentFolderIdForWorkspace(LibraryWorkspace.cloud) == folderID) {
updateWorkspaceFolderId(LibraryWorkspace.cloud, null);
}
return;
ref.invalidate(cloudFoldersProvider);
ref.invalidate(cloudAllFoldersProvider);
ref.invalidate(cloudStrategiesProvider);
return result;
}

await ref.read(pinnedItemsProvider.notifier).removePin(folderID);
Expand All @@ -186,9 +193,10 @@ class FolderProvider extends Notifier<String?> {
}

await Hive.box<Folder>(HiveBoxNames.foldersBox).delete(folderID);
return CloudLibraryActionResult.succeeded;
}

void editFolder({
Future<CloudLibraryActionResult> editFolder({
required Folder folder,
required String newName,
required int newIconId,
Expand All @@ -201,59 +209,65 @@ class FolderProvider extends Notifier<String?> {
final newIcon = FolderIconRegistry.resolve(newIconId).iconData;
final iconFontFamily = newIcon?.fontFamily;
final iconFontPackage = newIcon?.fontPackage;
try {
await ref.read(convexStrategyRepositoryProvider).updateFolder(
folderPublicId: folder.id,
name: newName,
iconId: newIconId,
iconCodePoint: newIcon?.codePoint,
iconFontFamily: iconFontFamily,
clearIconFontFamily: iconFontFamily == null,
iconFontPackage: iconFontPackage,
clearIconFontPackage: iconFontPackage == null,
color: newColor.name,
customColorValue: newCustomColor?.toARGB32(),
clearCustomColorValue: newCustomColor == null,
);
ref.invalidate(cloudFoldersProvider);
ref.invalidate(cloudAllFoldersProvider);
} catch (error, stackTrace) {
await _maybeReportCloudUnauthenticated(
source: 'folder:update',
error: error,
stackTrace: stackTrace,
);
}
return;
final result = await _runCloudAction(
action: () async {
await ref.read(convexStrategyRepositoryProvider).updateFolder(
folderPublicId: folder.id,
name: newName,
iconId: newIconId,
iconCodePoint: newIcon?.codePoint,
iconFontFamily: iconFontFamily,
clearIconFontFamily: iconFontFamily == null,
iconFontPackage: iconFontPackage,
clearIconFontPackage: iconFontPackage == null,
color: newColor.name,
customColorValue: newCustomColor?.toARGB32(),
clearCustomColorValue: newCustomColor == null,
);
return true;
},
source: 'folder:update',
failureMessage: "Couldn't update this cloud folder. Try again.",
);
if (!result.didSucceed) return result;

ref.invalidate(cloudFoldersProvider);
ref.invalidate(cloudAllFoldersProvider);
return result;
}

folder.name = newName;
folder.iconId = newIconId;
folder.customColor = newCustomColor;
folder.color = newColor;
await folder.save();
return CloudLibraryActionResult.succeeded;
}

void moveToFolder({
Future<CloudLibraryActionResult> moveToFolder({
required String folderID,
String? parentID,
LibraryWorkspace? workspace,
}) async {
final targetWorkspace = workspace ?? _currentWorkspace;
if (targetWorkspace == LibraryWorkspace.cloud) {
try {
await ref.read(convexStrategyRepositoryProvider).moveFolder(
folderPublicId: folderID,
parentFolderPublicId: parentID,
);
} catch (error, stackTrace) {
await _maybeReportCloudUnauthenticated(
source: 'folder:move',
error: error,
stackTrace: stackTrace,
);
}
return;
final result = await _runCloudAction(
action: () async {
await ref.read(convexStrategyRepositoryProvider).moveFolder(
folderPublicId: folderID,
parentFolderPublicId: parentID,
);
return true;
},
source: 'folder:move',
failureMessage: "Couldn't move this cloud folder. Try again.",
showFailureMessage: true,
);
if (!result.didSucceed) return result;

ref.invalidate(cloudFoldersProvider);
ref.invalidate(cloudAllFoldersProvider);
return result;
}

final folder = findLocalFolderByID(folderID);
Expand All @@ -262,6 +276,28 @@ class FolderProvider extends Notifier<String?> {
folder.parentID = parentID;
await folder.save();
}
return CloudLibraryActionResult.succeeded;
}

Future<CloudLibraryActionResult> _runCloudAction({
required Future<bool> Function() action,
required String source,
required String failureMessage,
bool showFailureMessage = false,
}) {
return ref.read(cloudLibraryActionReporterProvider).run(
action: action,
source: source,
failureMessage: failureMessage,
showFailureMessage: showFailureMessage,
reportAuthenticationFailure: (error, stackTrace) => ref
.read(authProvider.notifier)
.reportConvexUnauthenticated(
source: source,
error: error,
stackTrace: stackTrace,
),
);
}

LibraryWorkspace get _currentWorkspace => ref.read(libraryWorkspaceProvider);
Expand Down
108 changes: 67 additions & 41 deletions lib/providers/strategy_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import 'package:icarus/providers/collab/strategy_op_queue_provider.dart';
import 'package:icarus/providers/strategy_page_session_provider.dart';
import 'package:icarus/providers/strategy_save_state_provider.dart';
import 'package:icarus/services/analytics_service.dart';
import 'package:icarus/services/cloud_library_action.dart';
import 'package:icarus/strategy/strategy_migrator.dart';
import 'package:icarus/strategy/strategy_models.dart';
import 'package:icarus/strategy/strategy_page_models.dart';
Expand Down Expand Up @@ -1423,42 +1424,55 @@ class StrategyProvider extends Notifier<StrategyState> {
await strategyBox.put(duplicatedStrategy.id, duplicatedStrategy);
}

Future<void> deleteStrategy(
Future<CloudLibraryActionResult> deleteStrategy(
String strategyID, {
StrategySource? source,
}) async {
await ref.read(pinnedItemsProvider.notifier).removePin(strategyID);
final resolvedSource = source ?? _resolveLibraryMutationSource();
if (resolvedSource == StrategySource.cloud) {
try {
final shell = await ref
.read(convexStrategyRepositoryProvider)
.fetchShell(strategyID);
await ref.read(convexStrategyRepositoryProvider).deleteStrategy(
strategyPublicId: strategyID,
expectedRevision: shell.header.revision,
);
} catch (error, stackTrace) {
final handled = await _reportCloudUnauthenticated(
source: 'strategy:delete',
error: error,
stackTrace: stackTrace,
);
if (!handled) rethrow;
}
const sourceName = 'strategy:delete';
final result = await ref.read(cloudLibraryActionReporterProvider).run(
action: () async {
final shell = await ref
.read(convexStrategyRepositoryProvider)
.fetchShell(strategyID);
await ref.read(convexStrategyRepositoryProvider).deleteStrategy(
strategyPublicId: strategyID,
expectedRevision: shell.header.revision,
);
return true;
},
source: sourceName,
failureMessage:
"Couldn't delete this cloud strategy. Try again.",
reportAuthenticationFailure: (error, stackTrace) => ref
.read(authProvider.notifier)
.reportConvexUnauthenticated(
source: sourceName,
error: error,
stackTrace: stackTrace,
),
);
if (!result.didSucceed) return result;

await ref.read(pinnedItemsProvider.notifier).removePin(strategyID);
ref.invalidate(cloudStrategiesProvider);
return;
return result;
}

await ref.read(pinnedItemsProvider.notifier).removePin(strategyID);
await Hive.box<StrategyData>(HiveBoxNames.strategiesBox).delete(strategyID);

final directory = await getApplicationSupportDirectory();

final customDirectory = Directory(path.join(directory.path, strategyID));

if (!await customDirectory.exists()) return;
if (!await customDirectory.exists()) {
return CloudLibraryActionResult.succeeded;
}

await customDirectory.delete(recursive: true);
return CloudLibraryActionResult.succeeded;
}

Future<void> saveToHive(String id) async {
Expand Down Expand Up @@ -1656,42 +1670,54 @@ class StrategyProvider extends Notifier<StrategyState> {
return StrategySettings();
}

void moveToFolder({
Future<CloudLibraryActionResult> moveToFolder({
required String strategyID,
required String? parentID,
StrategySource? source,
}) {
}) async {
final resolvedSource = source ?? _resolveLibraryMutationSource();
if (resolvedSource == StrategySource.cloud) {
unawaited(() async {
try {
final shell = await ref
.read(convexStrategyRepositoryProvider)
.fetchShell(strategyID);
await ref.read(convexStrategyRepositoryProvider).moveStrategy(
strategyPublicId: strategyID,
folderPublicId: parentID,
expectedRevision: shell.header.revision,
);
} catch (error, stackTrace) {
await _reportCloudUnauthenticated(
source: 'strategy:move',
error: error,
stackTrace: stackTrace,
const sourceName = 'strategy:move';
final result = await ref.read(cloudLibraryActionReporterProvider).run(
action: () async {
final shell = await ref
.read(convexStrategyRepositoryProvider)
.fetchShell(strategyID);
await ref.read(convexStrategyRepositoryProvider).moveStrategy(
strategyPublicId: strategyID,
folderPublicId: parentID,
expectedRevision: shell.header.revision,
);
return true;
},
source: sourceName,
failureMessage: "Couldn't move this cloud strategy. Try again.",
showFailureMessage: true,
reportAuthenticationFailure: (error, stackTrace) => ref
.read(authProvider.notifier)
.reportConvexUnauthenticated(
source: sourceName,
error: error,
stackTrace: stackTrace,
),
);
}
}());
if (!result.didSucceed) return result;

ref.invalidate(cloudStrategiesProvider);
return;
return result;
}
final strategyBox = Hive.box<StrategyData>(HiveBoxNames.strategiesBox);
final strategy = strategyBox.get(strategyID);

if (strategy != null) {
strategy.folderID = parentID;
strategy.save();
await strategy.save();
return CloudLibraryActionResult.succeeded;
} else {
log("Strategy with ID $strategyID not found.");
return CloudLibraryActionResult.failed(
"Couldn't move this strategy. Try again.",
);
}
}
}
Loading
Loading