feat(base)!: removing a profile makes it private (ADR 0020, D27)
Every account keeps a handle now, so removing a public profile no longer deletes it and frees the handle: it takes away what the profile shows and leaves it private, as a profile is until its person goes public (D5). - `profile.remove` and the register's `removeProfile` (members ask the register) empty the display name, bio, links with their checks and the avatar, keep the row with its handle, and return what is left, null for none. `/@handle` and `profiles.byHandle` then answer as for a handle nobody has. Once the person has published, removing is refused as before. - Learn: the profile form starts afresh from what is left, "Remove profile" shows only while something is filled in, and the dialog says the handle stays. - The module's README and ADR 0020 amended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
630965d920
commit
d5651c5beb
5 changed files with 255 additions and 60 deletions
|
|
@ -82,10 +82,12 @@ class ProfileEndpoint extends Endpoint {
|
|||
Future<Profile> removeAvatar(Session session) =>
|
||||
_register(session).removeAvatar(session, _caller(session));
|
||||
|
||||
/// Removes your public profile, freeing its handle; nothing when you have
|
||||
/// none. Throws [ProfileRefusedException] once your handle is fixed:
|
||||
/// others refer to it.
|
||||
Future<void> remove(Session session) =>
|
||||
/// Removes what your public profile shows (display name, bio, links,
|
||||
/// avatar), making it private again: the handle stays yours (Halkyon
|
||||
/// Learn's ADR 0020, D27). Returns it; null when you have none. Throws
|
||||
/// [ProfileRefusedException] once your handle is fixed: others refer to
|
||||
/// it.
|
||||
Future<Profile?> remove(Session session) =>
|
||||
_register(session).removeProfile(session, _caller(session));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,9 +102,11 @@ abstract interface class AccountRegister {
|
|||
/// `ProfileNotFoundException` while they have no profile.
|
||||
Future<Profile> removeAvatar(Session session, UuidValue person);
|
||||
|
||||
/// Removes [person]'s public profile, freeing its handle; nothing when they
|
||||
/// have none. Throws `ProfileRefusedException` once their handle is fixed.
|
||||
Future<void> removeProfile(Session session, UuidValue person);
|
||||
/// Removes what [person]'s public profile shows (display name, bio, links,
|
||||
/// avatar), making it private again, the handle kept theirs (Halkyon
|
||||
/// Learn's ADR 0020, D27); returns it, null when they have none. Throws
|
||||
/// `ProfileRefusedException` once their handle is fixed.
|
||||
Future<Profile?> removeProfile(Session session, UuidValue person);
|
||||
|
||||
/// Fixes [person]'s handle, as others now refer to it (Halkyon Learn: their
|
||||
/// first publish); nothing when it is fixed already or they have no
|
||||
|
|
|
|||
|
|
@ -166,8 +166,11 @@ Future<Map<String, Object?>?> _answer(
|
|||
);
|
||||
return {'profile': profile.toJson()};
|
||||
case RegisterOperation.removeProfile:
|
||||
await register.removeProfile(session, arguments.id('person'));
|
||||
return const {};
|
||||
final profile = await register.removeProfile(
|
||||
session,
|
||||
arguments.id('person'),
|
||||
);
|
||||
return {'profile': profile?.toJson()};
|
||||
case RegisterOperation.fixHandle:
|
||||
await register.fixHandle(session, arguments.id('person'));
|
||||
return const {};
|
||||
|
|
|
|||
|
|
@ -318,22 +318,37 @@ final class LocalRegister implements AccountRegister {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> removeProfile(Session session, UuidValue person) async {
|
||||
Future<Profile?> removeProfile(Session session, UuidValue person) async {
|
||||
final mine = await _rowOf(session, person);
|
||||
if (mine == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
if (mine.firstPublishedAt != null) {
|
||||
throw ProfileRefusedException(reason: ProfileRefusal.published);
|
||||
}
|
||||
// DELETE FROM "public_profile" WHERE "authUserId" = $1
|
||||
await PublicProfile.db.deleteWhere(
|
||||
// UPDATE "public_profile" SET "displayName" = '', "bio" = '',
|
||||
// "links" = '[]', "verifiedLinks" = NULL, "avatarHash" = NULL,
|
||||
// "updatedAt" = $1 WHERE "id" = $2
|
||||
final emptied = await PublicProfile.db.updateRow(
|
||||
session,
|
||||
where: (t) => t.authUserId.equals(person),
|
||||
mine.copyWith(
|
||||
displayName: '',
|
||||
bio: '',
|
||||
links: const [],
|
||||
// Not the default: the generated copyWith tells an explicit null
|
||||
// (clear it) from no argument (keep it), which the lint can't see.
|
||||
// ignore: avoid_redundant_argument_values
|
||||
verifiedLinks: null,
|
||||
// As above.
|
||||
// ignore: avoid_redundant_argument_values
|
||||
avatarHash: null,
|
||||
updatedAt: clock.now().toUtc(),
|
||||
),
|
||||
);
|
||||
if (mine.avatarHash case final hash?) {
|
||||
await forgetAvatar(session, hash);
|
||||
}
|
||||
return await profileViewOf(session, emptied);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -222,8 +222,12 @@ final class RemoteRegister implements AccountRegister {
|
|||
)!;
|
||||
|
||||
@override
|
||||
Future<void> removeProfile(Session session, UuidValue person) =>
|
||||
_ask(RegisterOperation.removeProfile, {'person': person.uuid});
|
||||
Future<Profile?> removeProfile(Session session, UuidValue person) async =>
|
||||
_profile(
|
||||
(await _ask(RegisterOperation.removeProfile, {
|
||||
'person': person.uuid,
|
||||
}))['profile'],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> fixHandle(Session session, UuidValue person) =>
|
||||
|
|
|
|||
Loading…
Reference in a new issue