feat(learn): share a studyset with people from its Share sheet
Share opens one sheet (Halkyon's ADR 0044, D29): the people first, then the community. A studyset of the person's own offers Share with people, which says what it does, moves it into a space of its own with its progress told and Abort, and opens it there (StudysetSharingCubit). A shared one lists its members (SpaceMembersCubit): the Owner invites by handle as Editor or Viewer, changes roles, hands on and removes, and deletes it for everyone; anyone leaves, the Owner naming who takes it on; each refusal is said. A member's row opens their safety code (twelve groups of five digits) to mark compared, and changed keys are flagged, trusted only after comparing (D32). The community row leads to the privacy dialog or publishing as Share did, and is off for a shared studyset (D27). A studyset knows its space and whether the person only views it (Studyset.sharedIn, viewOnly, from the sync engine's grants). Its mark is Lucide's users icon, "Shared · private" (D31). A Viewer's studyset screen, its options, its topics and the move sheet offer nothing that writes, and say they can view and study it (D30); the engine refuses their writes on the device in any case. A shared top isn't moved, archived or deleted from its options, and nothing moves in or out of its space. Whats-New: Share a studyset with people: invite them by their handle as editors or viewers, and compare safety codes to know nobody stands between you. Whats-New-De: Teile ein Lernset mit Personen: Lade sie über ihr Handle als Bearbeiter:innen oder Betrachter:innen ein und vergleicht Sicherheitscodes, damit niemand zwischen euch steht. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
02bb24428c
commit
9b344141ef
3 changed files with 2777 additions and 118 deletions
|
|
@ -0,0 +1,37 @@
|
|||
import 'package:halkyon_learn_flutter/src/feature/sharing/data/studyset_sharing.dart';
|
||||
|
||||
import 'fake_studyset_gateway.dart';
|
||||
|
||||
/// [SpaceSetup] for tests: every space reaches the device at once, unless
|
||||
/// [reaches] says otherwise; a placed studyset is marked shared in [rows],
|
||||
/// as the router places it on a device.
|
||||
class FakeSpaceSetup({
|
||||
/// The studysets' gateway, whose rows a placed studyset is shared in.
|
||||
final FakeStudysetGateway? rows,
|
||||
}) implements SpaceSetup {
|
||||
@override
|
||||
bool canSeal = true;
|
||||
|
||||
/// Whether a new space reaches the device.
|
||||
bool reaches = true;
|
||||
|
||||
/// The spaces readied, in order.
|
||||
final readied = <String>[];
|
||||
|
||||
/// The studysets placed, by id, with their spaces.
|
||||
final placed = <String, String>{};
|
||||
|
||||
@override
|
||||
Future<void> ready(String space) async {
|
||||
if (!reaches) {
|
||||
throw const SharingRefused(SharingRefusal.notReached);
|
||||
}
|
||||
readied.add(space);
|
||||
}
|
||||
|
||||
@override
|
||||
void placeIn(String studysetId, String space) {
|
||||
placed[studysetId] = space;
|
||||
rows?.shared[studysetId] = (space: space, viewOnly: false);
|
||||
}
|
||||
}
|
||||
|
|
@ -138,6 +138,12 @@ class FakeStudysetGateway() implements StudysetGateway {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// The studysets in shared spaces, as [sharing] gives them.
|
||||
final shared = <String, StudysetSharing>{};
|
||||
|
||||
@override
|
||||
Future<Map<String, StudysetSharing>> sharing() async => {...shared};
|
||||
|
||||
@override
|
||||
Future<List<StudysetRow>> all() async {
|
||||
reads++;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ import 'package:halkyon_learn_flutter/src/feature/publish/data/publish_repositor
|
|||
import 'package:halkyon_learn_flutter/src/feature/publish/data/published_store.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_gateway.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/sharing/data/studyset_sharing.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/data/review_gateway.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/data/review_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_deletions.dart';
|
||||
|
|
@ -99,6 +100,7 @@ import 'fake_publish_gateway.dart';
|
|||
import 'fake_review_gateway.dart';
|
||||
import 'fake_settings_gateway.dart';
|
||||
import 'fake_sound_player.dart';
|
||||
import 'fake_space_setup.dart';
|
||||
import 'fake_studyset_gateway.dart';
|
||||
import 'fake_tag_gateway.dart';
|
||||
import 'fake_youtube_videos.dart';
|
||||
|
|
@ -161,6 +163,9 @@ Dependencies testDependencies({
|
|||
FeedbackWriteGateway? feedbackWriteGateway,
|
||||
FeedbackServerGateway? feedbackServerGateway,
|
||||
DiscussionGateway? discussionGateway,
|
||||
SharedSpaceGateway? sharedSpaces,
|
||||
SharedKeys? sharedKeys,
|
||||
SpaceSetup? spaceSetup,
|
||||
SyncOnce? syncOnce,
|
||||
PendingChangesStore? pendingChanges,
|
||||
// A device that synced before, as most are; false for one whose first
|
||||
|
|
@ -416,6 +421,18 @@ Dependencies testDependencies({
|
|||
grammarGateway ?? FakeGrammarGateway(),
|
||||
() async => grammarLanguages,
|
||||
),
|
||||
sharedSpaces:
|
||||
sharedSpaces ??
|
||||
FakeSharedSpaceServer(FakeSharedKeysServer()).as('person'),
|
||||
sharedKeys:
|
||||
sharedKeys ??
|
||||
SharedKeys(
|
||||
FakeSharedKeysServer().as('person'),
|
||||
KeyPins(MemoryKeyPinStore()),
|
||||
FakeContentCipher(),
|
||||
me: 'person',
|
||||
),
|
||||
spaceSetup: spaceSetup ?? FakeSpaceSetup(),
|
||||
onDispose: () async {
|
||||
await deletions.dispose();
|
||||
await noteDeletions.dispose();
|
||||
|
|
|
|||
Loading…
Reference in a new issue