From 415e9abd698868a2ce13e733b967d530f34de9fe Mon Sep 17 00:00:00 2001 From: Jess Moore Date: Tue, 28 Jul 2026 08:35:21 +1000 Subject: [PATCH 1/6] adds callback with recipienttype --- lib/src/widgets/grant_permission_form.dart | 14 ++++++++++++++ lib/src/widgets/grant_permission_ui.dart | 8 ++++++++ lib/src/widgets/grant_permission_ui_state.dart | 1 + lib/src/widgets/share_resource_button.dart | 11 +++++++++++ 4 files changed, 34 insertions(+) diff --git a/lib/src/widgets/grant_permission_form.dart b/lib/src/widgets/grant_permission_form.dart index c2c773f..8d8859c 100644 --- a/lib/src/widgets/grant_permission_form.dart +++ b/lib/src/widgets/grant_permission_form.dart @@ -134,6 +134,15 @@ class GrantPermissionForm extends StatefulWidget { final VoidCallback? onPermissionGranted; + /// Callback called when permissions are granted successfully, with the + /// [RecipientType] and resource names that were just granted. Fires + /// alongside [onPermissionGranted] — added so callers can distinguish a + /// public/authenticated-user grant from an individual/group grant, which + /// the plain [onPermissionGranted] cannot do. + + final void Function(RecipientType recipientType, List resourceNames)? + onRecipientTypeGranted; + /// Optional human-readable name for the resource, used in notification /// messages sent to recipients upon successful permission granting. @@ -159,6 +168,7 @@ class GrantPermissionForm extends StatefulWidget { required this.updatePermissionGrantedFunction, this.dataFilesMap = const {}, this.onPermissionGranted, + this.onRecipientTypeGranted, this.resourceDisplayName, this.inviteConfig, }); @@ -635,6 +645,10 @@ class _GrantPermissionFormState extends State { // Trigger the onPermissionGranted callback if provided widget.onPermissionGranted?.call(); + widget.onRecipientTypeGranted?.call( + selectedRecipientType, + widget.resourceNames, + ); } else if (result == SolidFunctionCallStatus.fail) { if (!context.mounted) return; await showGrantPermissionErrorDialog( diff --git a/lib/src/widgets/grant_permission_ui.dart b/lib/src/widgets/grant_permission_ui.dart index ffce6a3..729c811 100644 --- a/lib/src/widgets/grant_permission_ui.dart +++ b/lib/src/widgets/grant_permission_ui.dart @@ -89,6 +89,7 @@ class GrantPermissionUi extends StatefulWidget { this.buttonColor, this.customAppBar, this.onPermissionGranted, + this.onRecipientTypeGranted, this.onNavigateBack, this.resourceDisplayName, this.shareButtonColor, @@ -191,6 +192,13 @@ class GrantPermissionUi extends StatefulWidget { final VoidCallback? onPermissionGranted; + /// Callback called when permissions are granted successfully, with the + /// [RecipientType] and resource names that were just granted. See + /// [GrantPermissionForm.onRecipientTypeGranted]. + + final void Function(RecipientType recipientType, List resourceNames)? + onRecipientTypeGranted; + /// Callback function called when navigating back from the screen. final VoidCallback? onNavigateBack; diff --git a/lib/src/widgets/grant_permission_ui_state.dart b/lib/src/widgets/grant_permission_ui_state.dart index a945e5b..73346cd 100644 --- a/lib/src/widgets/grant_permission_ui_state.dart +++ b/lib/src/widgets/grant_permission_ui_state.dart @@ -464,6 +464,7 @@ class GrantPermissionUiState extends State isFile: getIsFile(), dataFilesMap: widget.dataFilesMap, onPermissionGranted: widget.onPermissionGranted, + onRecipientTypeGranted: widget.onRecipientTypeGranted, resourceDisplayName: widget.resourceDisplayName, buttonColor: widget.shareButtonColor ?? widget.buttonColor, diff --git a/lib/src/widgets/share_resource_button.dart b/lib/src/widgets/share_resource_button.dart index 48716ab..b933566 100644 --- a/lib/src/widgets/share_resource_button.dart +++ b/lib/src/widgets/share_resource_button.dart @@ -32,6 +32,8 @@ library; import 'package:flutter/material.dart'; +import 'package:solidpod/solidpod.dart' show RecipientType; + import 'package:solidui/src/utils/solid_alert.dart'; import 'package:solidui/src/widgets/grant_permission_form.dart'; import 'package:solidui/src/widgets/solid_invite_others_models.dart'; @@ -108,6 +110,13 @@ class ShareResourceButton extends StatefulWidget { final VoidCallback? onPermissionGranted; + /// Callback called when permissions are granted successfully, with the + /// [RecipientType] and resource names that were just granted. See + /// [GrantPermissionForm.onRecipientTypeGranted]. + + final void Function(RecipientType recipientType, List resourceNames)? + onRecipientTypeGranted; + /// Optional human-readable name for the resource, used in notification /// messages sent to recipients upon successful permission granting. @@ -138,6 +147,7 @@ class ShareResourceButton extends StatefulWidget { required this.isFile, this.dataFilesMap = const {}, this.onPermissionGranted, + this.onRecipientTypeGranted, this.resourceDisplayName, this.buttonColor, this.inviteConfig, @@ -225,6 +235,7 @@ class _ShareResourceButtonState extends State { dataFilesMap: widget.dataFilesMap, updatePermissionGrantedFunction: _updatePermissionGrantedStatus, onPermissionGranted: widget.onPermissionGranted, + onRecipientTypeGranted: widget.onRecipientTypeGranted, resourceDisplayName: widget.resourceDisplayName, inviteConfig: widget.inviteConfig, ); From 8916d58c37bd87650cad3d0ef30d72fdfb6c5752 Mon Sep 17 00:00:00 2001 From: Jess Moore Date: Tue, 28 Jul 2026 08:37:48 +1000 Subject: [PATCH 2/6] update changelog and bump minor version --- CHANGELOG.md | 1 + pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff6d443..5d5626c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The package is available from ## 1.1 Consolidate Android Login ++ Add sharing permission recipientType callback [1.0.23 20260728 jesscmoore] + Add webid to profile [1.0.22 20260726 jesscmoore] + Cache profile so immediate load on app startup [1.0.21 20260718 gjw] + Add a BACKUP option to SolidScaffold [1.0.20 20260717 tonypioneer] diff --git a/pubspec.yaml b/pubspec.yaml index 84992d3..fa8e8d7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: solidui description: 'A UI library for building Solid applications with Flutter.' -version: 1.0.22 +version: 1.0.23 homepage: https://github.com/anusii/solidui # Scaffold a new Solid Pod file-browser app (a pod browser, with navigation From f4f9691c2dc03264798446bbc95bc28fe2ddc64c Mon Sep 17 00:00:00 2001 From: Jess Moore Date: Tue, 28 Jul 2026 21:47:38 +1000 Subject: [PATCH 3/6] linted --- lib/src/widgets/grant_permission_dialogs.dart | 81 +++++++++++++++++++ lib/src/widgets/grant_permission_form.dart | 45 +++++++++++ 2 files changed, 126 insertions(+) diff --git a/lib/src/widgets/grant_permission_dialogs.dart b/lib/src/widgets/grant_permission_dialogs.dart index 841b427..a08a892 100644 --- a/lib/src/widgets/grant_permission_dialogs.dart +++ b/lib/src/widgets/grant_permission_dialogs.dart @@ -262,6 +262,87 @@ Future confirmPublicSharingDecryption( return confirmed ?? false; } +/// Confirm with the user before an individual/group grant revokes an +/// existing Public/Authenticated User class grant and re-encrypts the +/// resource (see `grantPermission`'s `revokePublicAccessOnSpecificGrant`). +/// +/// [existingClasses] is whichever of [RecipientType.public]/`.authUser` +/// currently have a grant on the resource (one, the other, or both) — +/// used to word the "public"/"auth user" and "publicly"/"to all +/// authenticated users" phrases correctly. + +Future confirmRevokeSharedAccessForSpecificGrant( + BuildContext context, + Set existingClasses, + RecipientType newRecipientType, + String recipientLabel, +) async { + final accessLabel = [ + if (existingClasses.contains(RecipientType.public)) 'public', + if (existingClasses.contains(RecipientType.authUser)) 'signed-in user', + ].join('/'); + final audience = [ + if (existingClasses.contains(RecipientType.public)) 'publicly', + if (existingClasses.contains(RecipientType.authUser)) + 'to all signed-in users', + ].join(' and '); + final recipientKind = + newRecipientType == RecipientType.individual ? 'individual' : 'group'; + + final confirmed = await showDialog( + context: context, + builder: (dialogContext) => AlertDialog( + title: Text( + 'Revoke $accessLabel access and share privately to $recipientLabel?', + ), + content: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: alertMaxWidthForCharsPerLine(defaultAlertMaxCharsPerLine), + ), + child: Text( + 'This file is currently shared $audience. The $recipientKind you ' + 'wish to grant access to currently already has access. ' + 'Do you want to remove $accessLabel access and ' + 'just share privately to $recipientLabel?', + ), + ), + actions: [ + MarkdownTooltip( + message: ''' + + **Cancel** + + Close this dialog without changing the file. Your file remains + decrypted and shared $audience. + + ''', + child: TextButton( + onPressed: () => Navigator.of(dialogContext).pop(false), + child: const Text('Cancel'), + ), + ), + MarkdownTooltip( + message: ''' + + **Share Privately** + + Encrypt this file in your POD, share privately, and revoke access to public and signed-in users. + + ''', + child: TextButton( + onPressed: () => Navigator.of(dialogContext).pop(true), + style: TextButton.styleFrom( + foregroundColor: ActionColors.warning, + ), + child: const Text('Share Privately'), + ), + ), + ], + ), + ); + return confirmed ?? false; +} + /// Priority order used when several WebIDs in the group list fail. We /// surface a single dialog and prefer the most actionable failure mode. diff --git a/lib/src/widgets/grant_permission_form.dart b/lib/src/widgets/grant_permission_form.dart index 8d8859c..d5853ec 100644 --- a/lib/src/widgets/grant_permission_form.dart +++ b/lib/src/widgets/grant_permission_form.dart @@ -49,6 +49,7 @@ import 'package:solidui/solidui.dart' successMsg, updatePermissionMsg; import 'package:solidui/src/utils/solid_alert.dart'; +import 'package:solidui/src/utils/web_id_parser.dart' show WebIdParts; import 'package:solidui/src/utils/webid_message.dart' show webIdCheckMessage; import 'package:solidui/src/widgets/grant_permission_dialogs.dart'; import 'package:solidui/src/widgets/grant_permission_helpers_ui.dart'; @@ -559,6 +560,50 @@ class _GrantPermissionFormState extends State { } if (!context.mounted) return; + // Granting to a specific individual/group revokes any existing + // Public/Authenticated User grant and re-encrypts the resource + // (see `grantPermission`'s `revokePublicAccessOnSpecificGrant`). + // Confirm with the user before doing so, mirroring the warning + // above for the reverse direction. + if (selectedRecipientType == RecipientType.individual || + selectedRecipientType == RecipientType.group) { + final existingClassPerms = >{}; + for (final name in widget.resourceNames) { + existingClassPerms.addAll( + await getUserClassPermissions( + fileName: name, + isFile: widget.isFile, + isExternalRes: widget.isExternalRes, + ), + ); + } + if (!context.mounted) return; + if (existingClassPerms.isNotEmpty) { + final recipientLabel = + selectedRecipientType == RecipientType.individual + ? (finalWebIdList.isNotEmpty + ? (WebIdParts.tryParse( + finalWebIdList.first.toString(), + )?.username ?? + finalWebIdList.first.toString()) + : 'this recipient') + : selectedGroupName; + final proceed = await confirmRevokeSharedAccessForSpecificGrant( + context, + existingClassPerms.keys.toSet(), + selectedRecipientType, + recipientLabel, + ); + if (!context.mounted) return; + if (!proceed) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Action cancelled')), + ); + return; + } + } + } + // Capture the ScaffoldMessenger now, while the dialog and its host // page are still mounted, so the success feedback can be shown // after this dialog is popped. From 0b5536bda63b0d7abbb668c58373f8cc983454d4 Mon Sep 17 00:00:00 2001 From: Jess Moore Date: Tue, 28 Jul 2026 21:53:15 +1000 Subject: [PATCH 4/6] fetch solidpod from jess/694_revert_public --- pubspec.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pubspec.yaml b/pubspec.yaml index fa8e8d7..5d37fd1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,6 +59,12 @@ dev_dependencies: sdk: flutter window_manager: ^0.5.1 +dependency_overrides: + solidpod: + git: + https://github.com/anusii/solidpod.git + ref: jess/694_revert_public + flutter: uses-material-design: true config: From 9036c72c747c059b2335bbb4c83ad940fb37bd16 Mon Sep 17 00:00:00 2001 From: Jess Moore Date: Tue, 28 Jul 2026 21:55:38 +1000 Subject: [PATCH 5/6] fix typo --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 5d37fd1..4e4dafd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,7 +62,7 @@ dev_dependencies: dependency_overrides: solidpod: git: - https://github.com/anusii/solidpod.git + url: https://github.com/anusii/solidpod.git ref: jess/694_revert_public flutter: From a96b0c788659e6bd6fc604dc7ef11b1b11b7ee4a Mon Sep 17 00:00:00 2001 From: Jess Moore Date: Tue, 28 Jul 2026 22:15:16 +1000 Subject: [PATCH 6/6] update changelog and bump minor version --- CHANGELOG.md | 1 + pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d5626c..eef8fde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The package is available from ## 1.1 Consolidate Android Login ++ Confirm switch public to private sharing [1.0.24 20260728 jesscmoore] + Add sharing permission recipientType callback [1.0.23 20260728 jesscmoore] + Add webid to profile [1.0.22 20260726 jesscmoore] + Cache profile so immediate load on app startup [1.0.21 20260718 gjw] diff --git a/pubspec.yaml b/pubspec.yaml index 4e4dafd..bed51b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: solidui description: 'A UI library for building Solid applications with Flutter.' -version: 1.0.23 +version: 1.0.24 homepage: https://github.com/anusii/solidui # Scaffold a new Solid Pod file-browser app (a pod browser, with navigation