feat: public profiles, a handle on the account domain with name, bio, links and picture

Stage 1 of the public tier (ADR 0005). A person with an identity has a
public profile: a handle by Mastodon's rules (1 to 30 letters, digits or
underscores, unique whatever its case, a few kept for the instance),
changeable until they first publish, on the instance's account domain
(ACCOUNT_DOMAIN, else the server's host; the instance info names it), so
`raisondetredev@halkyon.eu`; a display name, a bio, up to four links and
an avatar. The table is server only: the app gets a Profile without the
person's id. ProfileEndpoint (identified only) reads, saves and removes
the caller's own; ProfilesEndpoint finds anyone's by handle.

An avatar is a PNG of at most 1 MB that the app draws anew from the
picture chosen, its middle square at 400 pixels, so a photo's metadata
never leaves the device. The server keeps it in the public storage named
by its content, so its URL tells nothing about whose it is, and deletes it
once no profile shows it. Deleting a person deletes their profile and its
picture.

In the app, the Profile tab holds the profile as a form, laid out as the
old app's; anonymous people are told GitHub sign-in gives them one.

Migration 20261009184924904-public-profiles creates public_profile with a
unique index on the person and one on the handle in lower case; it touches
no other table, and the old server never sees it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-09 20:55:16 +02:00
commit 7f6147d689
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -1389,6 +1389,131 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get profileSwitchConfirm => 'Switch';
@override
String get publicProfileTitle => 'Public profile';
@override
String get publicProfileIntro =>
'How others see you once you publish: the handle they find you by, your name, a few words and links. Nothing here shows until you save it.';
@override
String get publicProfileAnonymousHint =>
'Sign in with GitHub to have a public profile and publish.';
@override
String get publicProfileDisplayName => 'Display name';
@override
String get publicProfileDisplayNameHint =>
'The name shown beside your handle';
@override
String get publicProfileHandle => 'Handle';
@override
String get publicProfileHandleHint =>
'Letters, digits and underscores, up to 30';
@override
String publicProfileAddress(String address) {
return 'Others find you as @$address';
}
@override
String get publicProfileHandleFixed =>
'Fixed since you published: others refer to it.';
@override
String get publicProfileBio => 'Bio';
@override
String get publicProfileLinks => 'Links';
@override
String get publicProfileLinksHint => 'Your site or other accounts, up to 4.';
@override
String get publicProfileLinkHint => 'https://…';
@override
String get publicProfileAddLink => 'Add link';
@override
String get publicProfileRemoveLink => 'Remove link';
@override
String get publicProfileSaved => 'Profile saved.';
@override
String get publicProfileRemove => 'Remove profile';
@override
String get publicProfileRemoveTitle => 'Remove your public profile?';
@override
String get publicProfileRemoveMessage =>
'Your handle becomes free for others to take.';
@override
String get publicProfileRemoved => 'Profile removed.';
@override
String get publicProfileFailed => 'Couldn\'t reach the instance. Try again.';
@override
String get publicProfileUnreachable =>
'Your public profile can\'t be reached right now.';
@override
String get publicProfileRetry => 'Retry';
@override
String get profileProblemHandleInvalid =>
'A handle is 1 to 30 letters (a to z), digits or underscores.';
@override
String get profileProblemHandleReserved =>
'This handle is kept for the instance.';
@override
String get profileProblemHandleTaken => 'Someone else has this handle.';
@override
String get profileProblemPublished =>
'You have published: your handle can\'t change, nor your profile go.';
@override
String get profileProblemDisplayNameTooLong => 'At most 30 characters.';
@override
String get profileProblemBioTooLong => 'At most 500 characters.';
@override
String get profileProblemTooManyLinks => 'At most 4 links.';
@override
String get profileProblemLinkInvalid =>
'Links start with https:// or http://.';
@override
String get profileProblemAvatarInvalid =>
'This picture can\'t be used: choose a PNG, JPEG, WebP or GIF image.';
@override
String get publicProfilePicture => 'Picture';
@override
String get publicProfileChoosePicture => 'Choose picture';
@override
String get publicProfileRemovePicture => 'Remove picture';
@override
String get publicProfilePictureType => 'Images';
@override
String get publicProfilePictureLater => 'Save your profile to add a picture.';
@override
String get settingsTitle => 'Settings';