feat(base,learn): spaces people share, kept by halkyon_base

ADR 0044, step 2 (D1, D2, D11, D13, and D15 to D17, decided today).
halkyon_base keeps the spaces several people share, for every app:
shared_space, its members with their roles (Owner, Editor, Viewer), and
invitations by handle waiting for someone with no account on the
service yet. The sharedSpace endpoint creates a space, lists its
members, invites by handle, changes roles, removes members, hands the
space on, leaves and deletes it.

Who may do what:
- Only people with an identity reach the endpoint.
- A space the caller isn't in can't be told from one that doesn't
  exist.
- Members are named by their membership, never by their account's ID.
- Only the Owner manages the members, unless the space lets members
  invite.
- The Owner's role passes only by handing the space on, and the Owner
  leaves only naming who takes it on.
- An invitation counts as an answer about a handle, as it tells whether
  one exists.
Every rule has a test asserting the refusal.

The module stays free of the sync engine: a host hands it a SpaceGrants,
and every call runs in the module's transaction, so the engine's grants
follow the roles or nothing changes. The Owner and an Editor write; a
Viewer reads. Learn hands over SyncSpaceGrants on OfflineSyncSpaces. Todo
hands over none yet, so its server refuses until its projects are
shared.

Deleting an account hands each space the person owns to the Editor who
joined first, else the Viewer who joined first. A space with no other
member goes. This runs before the host's own deletions. A person's first
sign-in on a service makes their waiting invitations memberships, in the
same transaction that makes them a person there.

Migrations: the module's and every host's (Learn, Todo, Train, Meditate)
create the three tables, their indexes and the two cascading foreign
keys to shared_space, and nothing else. The old server keeps working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-11 10:33:12 +01:00
commit d08d8ad545
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
8 changed files with 108233 additions and 18 deletions

View file

@ -78,6 +78,12 @@ import 'package:halkyon_base_client/src/protocol/profile/profile.dart'
as _i8qdmv6n;
import 'package:halkyon_base_client/src/protocol/profile/profile_refusal.dart'
as _ih722tf9;
import 'package:halkyon_base_client/src/protocol/shared_space/shared_space_info.dart'
as _i9dv6y4h;
import 'package:halkyon_base_client/src/protocol/shared_space/shared_space_member_info.dart'
as _iyon0cg8;
import 'package:halkyon_base_client/src/protocol/shared_space/shared_space_role.dart'
as _i1fyjuki;
import 'package:halkyon_base_client/src/protocol/simplelogin/simplelogin_completed.dart'
as _ianfx7qj;
import 'package:halkyon_base_client/src/protocol/simplelogin/simplelogin_return.dart'
@ -1309,6 +1315,133 @@ class EndpointProfiles extends _isc.EndpointRef {
);
}
/// Spaces you share with others (ADR 0044): making one, its members and
/// their roles, inviting by handle, handing it on, leaving and deleting it.
/// Only for people with an identity. The person always comes from the
/// session: a space you aren't a member of can't be told from one that
/// doesn't exist ([SharedSpaceNotFoundException]), and members are named
/// by their membership, never by their account's ID.
/// {@category Endpoint}
class EndpointSharedSpace extends _isc.EndpointRef {
EndpointSharedSpace(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'halkyon_base.sharedSpace';
/// Makes a shared space holding [kind] (the app's word, `studyset`), with
/// you its Owner.
_ida.Future<_i9dv6y4h.SharedSpaceInfo> create(String kind) =>
caller.callServerEndpoint<_i9dv6y4h.SharedSpaceInfo>(
'halkyon_base.sharedSpace',
'create',
{'kind': kind},
);
/// The shared spaces you are a member of, with your role in each.
_ida.Future<List<_i9dv6y4h.SharedSpaceInfo>> mine() =>
caller.callServerEndpoint<List<_i9dv6y4h.SharedSpaceInfo>>(
'halkyon_base.sharedSpace',
'mine',
{},
);
/// The members of [space] and those invited, the Owner first, then as
/// they joined.
_ida.Future<List<_iyon0cg8.SharedSpaceMemberInfo>> members(
_isc.UuidValue space,
) => caller.callServerEndpoint<List<_iyon0cg8.SharedSpaceMemberInfo>>(
'halkyon_base.sharedSpace',
'members',
{'space': space},
);
/// Invites the person with [handle] (whatever its case) to [space] as
/// [role]: a member at once where they have an account here, else
/// invited until they first sign in here (ADR 0044, D13). The Owner's,
/// or any member's where the space lets members invite. Counts as an
/// answer about a handle (`countHandleCheck`), as it tells whether one
/// exists.
_ida.Future<_iyon0cg8.SharedSpaceMemberInfo> invite(
_isc.UuidValue space,
String handle,
_i1fyjuki.SharedSpaceRole role,
) => caller.callServerEndpoint<_iyon0cg8.SharedSpaceMemberInfo>(
'halkyon_base.sharedSpace',
'invite',
{
'space': space,
'handle': handle,
'role': role,
},
);
/// Gives the member [member] of [space] the role [role]; the Owner's.
/// The Owner's own role passes only by handing on ([handOn]).
_ida.Future<void> setRole(
_isc.UuidValue space,
_isc.UuidValue member,
_i1fyjuki.SharedSpaceRole role,
) => caller.callServerEndpoint<void>(
'halkyon_base.sharedSpace',
'setRole',
{
'space': space,
'member': member,
'role': role,
},
);
/// Removes the member or invitation [member] from [space]; the Owner's.
_ida.Future<void> remove(
_isc.UuidValue space,
_isc.UuidValue member,
) => caller.callServerEndpoint<void>(
'halkyon_base.sharedSpace',
'remove',
{
'space': space,
'member': member,
},
);
/// Makes the member [member] the Owner of [space], and you, its Owner, an
/// Editor.
_ida.Future<void> handOn(
_isc.UuidValue space,
_isc.UuidValue member,
) => caller.callServerEndpoint<void>(
'halkyon_base.sharedSpace',
'handOn',
{
'space': space,
'member': member,
},
);
/// Leaves [space]. Its Owner names the member who takes it on
/// ([nextOwner]) and leaves only then (ADR 0044, D11); its only member
/// deletes it instead ([delete]).
_ida.Future<void> leave(
_isc.UuidValue space, {
_isc.UuidValue? nextOwner,
}) => caller.callServerEndpoint<void>(
'halkyon_base.sharedSpace',
'leave',
{
'space': space,
'nextOwner': nextOwner,
},
);
/// Deletes [space] with everything in it; its Owner's.
_ida.Future<void> delete(_isc.UuidValue space) =>
caller.callServerEndpoint<void>(
'halkyon_base.sharedSpace',
'delete',
{'space': space},
);
}
/// Signing in with SimpleLogin (ADR 0020, 3 to 5), signed out or, to add it
/// to one's account, signed in. The instance's register keeps the attempts
/// and checks SimpleLogin's codes (ADR 0016); a session is this server's.
@ -1463,6 +1596,7 @@ class Caller extends _isc.ModuleEndpointCaller {
passwordSignIn = EndpointPasswordSignIn(this);
profile = EndpointProfile(this);
profiles = EndpointProfiles(this);
sharedSpace = EndpointSharedSpace(this);
simpleLogin = EndpointSimpleLogin(this);
}
@ -1496,6 +1630,8 @@ class Caller extends _isc.ModuleEndpointCaller {
late final EndpointProfiles profiles;
late final EndpointSharedSpace sharedSpace;
late final EndpointSimpleLogin simpleLogin;
@override
@ -1515,6 +1651,7 @@ class Caller extends _isc.ModuleEndpointCaller {
'halkyon_base.passwordSignIn': passwordSignIn,
'halkyon_base.profile': profile,
'halkyon_base.profiles': profiles,
'halkyon_base.sharedSpace': sharedSpace,
'halkyon_base.simpleLogin': simpleLogin,
};
}

View file

@ -19,6 +19,10 @@ import 'package:halkyon_base_client/src/protocol/operator/account_overview.dart'
as _ijg5xrj3;
import 'package:halkyon_base_client/src/protocol/password/authenticator.dart'
as _ivfmflch;
import 'package:halkyon_base_client/src/protocol/shared_space/shared_space_info.dart'
as _i9dv6y4h;
import 'package:halkyon_base_client/src/protocol/shared_space/shared_space_member_info.dart'
as _iyon0cg8;
import 'package:halkyon_oidc_client/halkyon_oidc_client.dart' as _ibkpwzgl;
import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
as _iacc;
@ -90,6 +94,12 @@ import 'profile/profile.dart' as _ia5j8fch;
import 'profile/profile_not_found_exception.dart' as _iim3hfcf;
import 'profile/profile_refusal.dart' as _i6j6efd1;
import 'profile/profile_refused_exception.dart' as _idvc5e7p;
import 'shared_space/shared_space_info.dart' as _i1rvc02f;
import 'shared_space/shared_space_member_info.dart' as _iz8z2oju;
import 'shared_space/shared_space_not_found_exception.dart' as _ixm56fil;
import 'shared_space/shared_space_refusal.dart' as _irg3s7i7;
import 'shared_space/shared_space_refused_exception.dart' as _iooym5cy;
import 'shared_space/shared_space_role.dart' as _i2fude1g;
import 'simplelogin/simplelogin_completed.dart' as _i51ry5s2;
import 'simplelogin/simplelogin_outcome.dart' as _i5ar1hzl;
import 'simplelogin/simplelogin_return.dart' as _i8dcjd27;
@ -158,6 +168,12 @@ export 'profile/profile.dart';
export 'profile/profile_not_found_exception.dart';
export 'profile/profile_refusal.dart';
export 'profile/profile_refused_exception.dart';
export 'shared_space/shared_space_info.dart';
export 'shared_space/shared_space_member_info.dart';
export 'shared_space/shared_space_not_found_exception.dart';
export 'shared_space/shared_space_refusal.dart';
export 'shared_space/shared_space_refused_exception.dart';
export 'shared_space/shared_space_role.dart';
export 'simplelogin/simplelogin_completed.dart';
export 'simplelogin/simplelogin_outcome.dart';
export 'simplelogin/simplelogin_return.dart';
@ -403,6 +419,24 @@ class Protocol extends _isc.SerializationManager {
if (t == _idvc5e7p.ProfileRefusedException) {
return _idvc5e7p.ProfileRefusedException.fromJson(data) as T;
}
if (t == _i1rvc02f.SharedSpaceInfo) {
return _i1rvc02f.SharedSpaceInfo.fromJson(data) as T;
}
if (t == _iz8z2oju.SharedSpaceMemberInfo) {
return _iz8z2oju.SharedSpaceMemberInfo.fromJson(data) as T;
}
if (t == _ixm56fil.SharedSpaceNotFoundException) {
return _ixm56fil.SharedSpaceNotFoundException.fromJson(data) as T;
}
if (t == _irg3s7i7.SharedSpaceRefusal) {
return _irg3s7i7.SharedSpaceRefusal.fromJson(data) as T;
}
if (t == _iooym5cy.SharedSpaceRefusedException) {
return _iooym5cy.SharedSpaceRefusedException.fromJson(data) as T;
}
if (t == _i2fude1g.SharedSpaceRole) {
return _i2fude1g.SharedSpaceRole.fromJson(data) as T;
}
if (t == _i51ry5s2.SimpleLoginCompleted) {
return _i51ry5s2.SimpleLoginCompleted.fromJson(data) as T;
}
@ -684,6 +718,36 @@ class Protocol extends _isc.SerializationManager {
: null)
as T;
}
if (t == _isc.getType<_i1rvc02f.SharedSpaceInfo?>()) {
return (data != null ? _i1rvc02f.SharedSpaceInfo.fromJson(data) : null)
as T;
}
if (t == _isc.getType<_iz8z2oju.SharedSpaceMemberInfo?>()) {
return (data != null
? _iz8z2oju.SharedSpaceMemberInfo.fromJson(data)
: null)
as T;
}
if (t == _isc.getType<_ixm56fil.SharedSpaceNotFoundException?>()) {
return (data != null
? _ixm56fil.SharedSpaceNotFoundException.fromJson(data)
: null)
as T;
}
if (t == _isc.getType<_irg3s7i7.SharedSpaceRefusal?>()) {
return (data != null ? _irg3s7i7.SharedSpaceRefusal.fromJson(data) : null)
as T;
}
if (t == _isc.getType<_iooym5cy.SharedSpaceRefusedException?>()) {
return (data != null
? _iooym5cy.SharedSpaceRefusedException.fromJson(data)
: null)
as T;
}
if (t == _isc.getType<_i2fude1g.SharedSpaceRole?>()) {
return (data != null ? _i2fude1g.SharedSpaceRole.fromJson(data) : null)
as T;
}
if (t == _isc.getType<_i51ry5s2.SimpleLoginCompleted?>()) {
return (data != null
? _i51ry5s2.SimpleLoginCompleted.fromJson(data)
@ -783,6 +847,18 @@ class Protocol extends _isc.SerializationManager {
.toList()
as T;
}
if (t == List<_i9dv6y4h.SharedSpaceInfo>) {
return (data as List)
.map((e) => deserialize<_i9dv6y4h.SharedSpaceInfo>(e))
.toList()
as T;
}
if (t == List<_iyon0cg8.SharedSpaceMemberInfo>) {
return (data as List)
.map((e) => deserialize<_iyon0cg8.SharedSpaceMemberInfo>(e))
.toList()
as T;
}
try {
return _ibkpwzgl.Protocol().deserialize<T>(data, t);
} on _isc.DeserializationTypeNotFoundException catch (_) {}
@ -862,6 +938,12 @@ class Protocol extends _isc.SerializationManager {
_iim3hfcf.ProfileNotFoundException => 'ProfileNotFoundException',
_i6j6efd1.ProfileRefusal => 'ProfileRefusal',
_idvc5e7p.ProfileRefusedException => 'ProfileRefusedException',
_i1rvc02f.SharedSpaceInfo => 'SharedSpaceInfo',
_iz8z2oju.SharedSpaceMemberInfo => 'SharedSpaceMemberInfo',
_ixm56fil.SharedSpaceNotFoundException => 'SharedSpaceNotFoundException',
_irg3s7i7.SharedSpaceRefusal => 'SharedSpaceRefusal',
_iooym5cy.SharedSpaceRefusedException => 'SharedSpaceRefusedException',
_i2fude1g.SharedSpaceRole => 'SharedSpaceRole',
_i51ry5s2.SimpleLoginCompleted => 'SimpleLoginCompleted',
_i5ar1hzl.SimpleLoginOutcome => 'SimpleLoginOutcome',
_i8dcjd27.SimpleLoginReturn => 'SimpleLoginReturn',
@ -1012,6 +1094,18 @@ class Protocol extends _isc.SerializationManager {
return 'ProfileRefusal';
case _idvc5e7p.ProfileRefusedException():
return 'ProfileRefusedException';
case _i1rvc02f.SharedSpaceInfo():
return 'SharedSpaceInfo';
case _iz8z2oju.SharedSpaceMemberInfo():
return 'SharedSpaceMemberInfo';
case _ixm56fil.SharedSpaceNotFoundException():
return 'SharedSpaceNotFoundException';
case _irg3s7i7.SharedSpaceRefusal():
return 'SharedSpaceRefusal';
case _iooym5cy.SharedSpaceRefusedException():
return 'SharedSpaceRefusedException';
case _i2fude1g.SharedSpaceRole():
return 'SharedSpaceRole';
case _i51ry5s2.SimpleLoginCompleted():
return 'SimpleLoginCompleted';
case _i5ar1hzl.SimpleLoginOutcome():
@ -1223,6 +1317,24 @@ class Protocol extends _isc.SerializationManager {
if (dataClassName == 'ProfileRefusedException') {
return deserialize<_idvc5e7p.ProfileRefusedException>(data['data']);
}
if (dataClassName == 'SharedSpaceInfo') {
return deserialize<_i1rvc02f.SharedSpaceInfo>(data['data']);
}
if (dataClassName == 'SharedSpaceMemberInfo') {
return deserialize<_iz8z2oju.SharedSpaceMemberInfo>(data['data']);
}
if (dataClassName == 'SharedSpaceNotFoundException') {
return deserialize<_ixm56fil.SharedSpaceNotFoundException>(data['data']);
}
if (dataClassName == 'SharedSpaceRefusal') {
return deserialize<_irg3s7i7.SharedSpaceRefusal>(data['data']);
}
if (dataClassName == 'SharedSpaceRefusedException') {
return deserialize<_iooym5cy.SharedSpaceRefusedException>(data['data']);
}
if (dataClassName == 'SharedSpaceRole') {
return deserialize<_i2fude1g.SharedSpaceRole>(data['data']);
}
if (dataClassName == 'SimpleLoginCompleted') {
return deserialize<_i51ry5s2.SimpleLoginCompleted>(data['data']);
}

View file

@ -0,0 +1,124 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import '../shared_space/shared_space_role.dart' as _igsxp9el;
/// A shared space as its member sees it.
abstract class SharedSpaceInfo
implements _isc.SerializableModel, _isc.ProtocolSerialization {
SharedSpaceInfo._({
required this.id,
required this.kind,
required this.role,
required this.createdAt,
});
factory SharedSpaceInfo({
required _isc.UuidValue id,
required String kind,
required _igsxp9el.SharedSpaceRole role,
required DateTime createdAt,
}) = _SharedSpaceInfoImpl;
factory SharedSpaceInfo.fromJson(Map<String, dynamic> jsonSerialization) {
return SharedSpaceInfo(
id: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
kind: jsonSerialization['kind'] as String,
role: _igsxp9el.SharedSpaceRole.fromJson(
(jsonSerialization['role'] as String),
),
createdAt: _isc.DateTimeJsonExtension.fromJson(
jsonSerialization['createdAt'],
),
);
}
/// The sync engine's space.
_isc.UuidValue id;
/// What it holds, in the host's words.
String kind;
/// The caller's role in it.
_igsxp9el.SharedSpaceRole role;
DateTime createdAt;
/// Returns a shallow copy of this [SharedSpaceInfo]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
SharedSpaceInfo copyWith({
_isc.UuidValue? id,
String? kind,
_igsxp9el.SharedSpaceRole? role,
DateTime? createdAt,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.SharedSpaceInfo',
'id': id.toJson(),
'kind': kind,
'role': role.toJson(),
'createdAt': createdAt.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.SharedSpaceInfo',
'id': id.toJson(),
'kind': kind,
'role': role.toJson(),
'createdAt': createdAt.toJson(),
};
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _SharedSpaceInfoImpl extends SharedSpaceInfo {
_SharedSpaceInfoImpl({
required _isc.UuidValue id,
required String kind,
required _igsxp9el.SharedSpaceRole role,
required DateTime createdAt,
}) : super._(
id: id,
kind: kind,
role: role,
createdAt: createdAt,
);
/// Returns a shallow copy of this [SharedSpaceInfo]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
SharedSpaceInfo copyWith({
_isc.UuidValue? id,
String? kind,
_igsxp9el.SharedSpaceRole? role,
DateTime? createdAt,
}) {
return SharedSpaceInfo(
id: id ?? this.id,
kind: kind ?? this.kind,
role: role ?? this.role,
createdAt: createdAt ?? this.createdAt,
);
}
}

View file

@ -0,0 +1,170 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import '../shared_space/shared_space_role.dart' as _igsxp9el;
/// A member of a shared space, or someone invited who hasn't joined yet,
/// as the space's members see them: by their public profile, never by
/// their account's ID.
abstract class SharedSpaceMemberInfo
implements _isc.SerializableModel, _isc.ProtocolSerialization {
SharedSpaceMemberInfo._({
required this.id,
this.handle,
required this.displayName,
required this.role,
this.joinedAt,
required this.invited,
required this.you,
});
factory SharedSpaceMemberInfo({
required _isc.UuidValue id,
String? handle,
required String displayName,
required _igsxp9el.SharedSpaceRole role,
DateTime? joinedAt,
required bool invited,
required bool you,
}) = _SharedSpaceMemberInfoImpl;
factory SharedSpaceMemberInfo.fromJson(
Map<String, dynamic> jsonSerialization,
) {
return SharedSpaceMemberInfo(
id: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
handle: jsonSerialization['handle'] as String?,
displayName: jsonSerialization['displayName'] as String,
role: _igsxp9el.SharedSpaceRole.fromJson(
(jsonSerialization['role'] as String),
),
joinedAt: jsonSerialization['joinedAt'] == null
? null
: _isc.DateTimeJsonExtension.fromJson(jsonSerialization['joinedAt']),
invited: _isc.BoolJsonExtension.fromJson(jsonSerialization['invited']),
you: _isc.BoolJsonExtension.fromJson(jsonSerialization['you']),
);
}
/// What names them in calls about the space: their membership, or the
/// invitation while it waits.
_isc.UuidValue id;
/// Their handle; null without a public profile.
String? handle;
/// Their display name; empty without one.
String displayName;
_igsxp9el.SharedSpaceRole role;
/// When they joined; null while invited.
DateTime? joinedAt;
/// Whether the invitation waits for them to use the app.
bool invited;
/// Whether this is the caller.
bool you;
/// Returns a shallow copy of this [SharedSpaceMemberInfo]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
SharedSpaceMemberInfo copyWith({
_isc.UuidValue? id,
String? handle,
String? displayName,
_igsxp9el.SharedSpaceRole? role,
DateTime? joinedAt,
bool? invited,
bool? you,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.SharedSpaceMemberInfo',
'id': id.toJson(),
if (handle != null) 'handle': handle,
'displayName': displayName,
'role': role.toJson(),
if (joinedAt != null) 'joinedAt': joinedAt?.toJson(),
'invited': invited,
'you': you,
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.SharedSpaceMemberInfo',
'id': id.toJson(),
if (handle != null) 'handle': handle,
'displayName': displayName,
'role': role.toJson(),
if (joinedAt != null) 'joinedAt': joinedAt?.toJson(),
'invited': invited,
'you': you,
};
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _Undefined {}
class _SharedSpaceMemberInfoImpl extends SharedSpaceMemberInfo {
_SharedSpaceMemberInfoImpl({
required _isc.UuidValue id,
String? handle,
required String displayName,
required _igsxp9el.SharedSpaceRole role,
DateTime? joinedAt,
required bool invited,
required bool you,
}) : super._(
id: id,
handle: handle,
displayName: displayName,
role: role,
joinedAt: joinedAt,
invited: invited,
you: you,
);
/// Returns a shallow copy of this [SharedSpaceMemberInfo]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
SharedSpaceMemberInfo copyWith({
_isc.UuidValue? id,
Object? handle = _Undefined,
String? displayName,
_igsxp9el.SharedSpaceRole? role,
Object? joinedAt = _Undefined,
bool? invited,
bool? you,
}) {
return SharedSpaceMemberInfo(
id: id ?? this.id,
handle: handle is String? ? handle : this.handle,
displayName: displayName ?? this.displayName,
role: role ?? this.role,
joinedAt: joinedAt is DateTime? ? joinedAt : this.joinedAt,
invited: invited ?? this.invited,
you: you ?? this.you,
);
}
}

View file

@ -0,0 +1,76 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// No shared space of that ID that the caller is a member of, or no member
/// of that ID in it: said alike for each, so a caller learns nothing of
/// spaces they are not in.
abstract class SharedSpaceNotFoundException
implements
_isc.SerializableException,
_isc.SerializableModel,
_isc.ProtocolSerialization {
SharedSpaceNotFoundException._({required this.what});
factory SharedSpaceNotFoundException({required String what}) =
_SharedSpaceNotFoundExceptionImpl;
factory SharedSpaceNotFoundException.fromJson(
Map<String, dynamic> jsonSerialization,
) {
return SharedSpaceNotFoundException(
what: jsonSerialization['what'] as String,
);
}
/// `space` or `member`.
String what;
/// Returns a shallow copy of this [SharedSpaceNotFoundException]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
SharedSpaceNotFoundException copyWith({String? what});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.SharedSpaceNotFoundException',
'what': what,
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.SharedSpaceNotFoundException',
'what': what,
};
}
@override
String toString() {
return 'SharedSpaceNotFoundException(what: $what)';
}
}
class _SharedSpaceNotFoundExceptionImpl extends SharedSpaceNotFoundException {
_SharedSpaceNotFoundExceptionImpl({required String what})
: super._(what: what);
/// Returns a shallow copy of this [SharedSpaceNotFoundException]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
SharedSpaceNotFoundException copyWith({String? what}) {
return SharedSpaceNotFoundException(what: what ?? this.what);
}
}

View file

@ -0,0 +1,71 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// Why a call about a shared space was refused (ADR 0044); nothing changed.
enum SharedSpaceRefusal implements _isc.SerializableModel {
/// The host keeps no shared spaces.
unavailable,
/// Only the Owner does that (or a member who may invite, inviting).
notAllowed,
/// No account has that handle.
noSuchHandle,
/// They are a member or invited already.
alreadyMember,
/// The Owner's role passes only by handing the space on.
ownerRole,
/// Not on oneself: the Owner hands on or leaves instead.
self,
/// The Owner leaves only naming who takes the space on.
nextOwnerNeeded,
/// The only member doesn't leave: they delete the space.
lastMember;
static SharedSpaceRefusal fromJson(String name) {
switch (name) {
case 'unavailable':
return SharedSpaceRefusal.unavailable;
case 'notAllowed':
return SharedSpaceRefusal.notAllowed;
case 'noSuchHandle':
return SharedSpaceRefusal.noSuchHandle;
case 'alreadyMember':
return SharedSpaceRefusal.alreadyMember;
case 'ownerRole':
return SharedSpaceRefusal.ownerRole;
case 'self':
return SharedSpaceRefusal.self;
case 'nextOwnerNeeded':
return SharedSpaceRefusal.nextOwnerNeeded;
case 'lastMember':
return SharedSpaceRefusal.lastMember;
default:
throw ArgumentError(
'Value "$name" cannot be converted to "SharedSpaceRefusal"',
);
}
}
@override
String toJson() => name;
@override
String toString() => name;
}

View file

@ -0,0 +1,78 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import '../shared_space/shared_space_refusal.dart' as _i7m30y33;
/// A call about a shared space was refused; nothing changed.
abstract class SharedSpaceRefusedException
implements
_isc.SerializableException,
_isc.SerializableModel,
_isc.ProtocolSerialization {
SharedSpaceRefusedException._({required this.reason});
factory SharedSpaceRefusedException({
required _i7m30y33.SharedSpaceRefusal reason,
}) = _SharedSpaceRefusedExceptionImpl;
factory SharedSpaceRefusedException.fromJson(
Map<String, dynamic> jsonSerialization,
) {
return SharedSpaceRefusedException(
reason: _i7m30y33.SharedSpaceRefusal.fromJson(
(jsonSerialization['reason'] as String),
),
);
}
_i7m30y33.SharedSpaceRefusal reason;
/// Returns a shallow copy of this [SharedSpaceRefusedException]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
SharedSpaceRefusedException copyWith({_i7m30y33.SharedSpaceRefusal? reason});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.SharedSpaceRefusedException',
'reason': reason.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.SharedSpaceRefusedException',
'reason': reason.toJson(),
};
}
@override
String toString() {
return 'SharedSpaceRefusedException(reason: $reason)';
}
}
class _SharedSpaceRefusedExceptionImpl extends SharedSpaceRefusedException {
_SharedSpaceRefusedExceptionImpl({
required _i7m30y33.SharedSpaceRefusal reason,
}) : super._(reason: reason);
/// Returns a shallow copy of this [SharedSpaceRefusedException]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
SharedSpaceRefusedException copyWith({_i7m30y33.SharedSpaceRefusal? reason}) {
return SharedSpaceRefusedException(reason: reason ?? this.reason);
}
}

View file

@ -0,0 +1,46 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// What a member may do in a shared space (ADR 0044, D2).
enum SharedSpaceRole implements _isc.SerializableModel {
/// Everything: manage the members, hand the space on, delete it.
owner,
/// Changes its content.
editor,
/// Reads and studies it.
viewer;
static SharedSpaceRole fromJson(String name) {
switch (name) {
case 'owner':
return SharedSpaceRole.owner;
case 'editor':
return SharedSpaceRole.editor;
case 'viewer':
return SharedSpaceRole.viewer;
default:
throw ArgumentError(
'Value "$name" cannot be converted to "SharedSpaceRole"',
);
}
}
@override
String toJson() => name;
@override
String toString() => name;
}