feat(app,learn): invite to a shared studyset by email or link, and join

The apps' half of ADR 0044's step 5 (D37 to D40), on the instance's
half of the commit before.

halkyon_app's SharedSpaceGateway asks for inviting by mail, the
preview of a link, the code proving an address, accepting a mailed
invitation, a space's link (read, switched, joined) and marking a
share seen; SpaceInfo says whether a share is new and who made it,
a member whether they were invited by mail. joinTokenOf reads a link
or its token as pasted. The fake keeps the mails and codes it would
send, for the apps' tests.

In Learn:
- the Owner's Share sheet invites by handle or by email, lists an
  invitation mailed by its role alone, and switches the space's link,
  copying it while on;
- the Library's menu has Join with a link, and a link opened on
  Android (/join/<token> on learn.halkyon.eu, verified through the
  instance's assetlinks) opens the same on a screen of its own: who
  invites and as whom, then Join, or for a mailed invitation the
  address and the code mailed there;
- a studyset someone shared is new until opened: in the Inbox
  ("@alice shared Japanese with you", Open or Leave), on the Profile
  tab's badge and as New on its library card (NewSharesCubit, read
  again after every merge).

Whats-New: Invite people to a shared studyset by email or with a link, join one from the Library's Join with a link, and see in the Inbox what was shared with you.
Whats-New-De: Lade Leute per E-Mail oder Link zu einem geteilten Lernset ein, tritt einem über „Mit einem Link beitreten“ in der Bibliothek bei und sieh im Posteingang, was mit dir geteilt wurde.

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 19:09:12 +01:00
commit fb6b78fa19
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -13405,6 +13405,234 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'You can only view this studyset.'**
String get studysetViewOnlyRefused;
/// Inviting someone by their @handle, a choice in the invite dialog
///
/// In en, this message translates to:
/// **'Handle'**
String get shareInviteByHandle;
/// Inviting someone by email address, a choice in the invite dialog
///
/// In en, this message translates to:
/// **'Email'**
String get shareInviteByEmail;
/// Label of the invite dialog's address field
///
/// In en, this message translates to:
/// **'Email address'**
String get shareInviteAddress;
/// Below the address field of the invite dialog
///
/// In en, this message translates to:
/// **'They get a link by mail. Only whoever reads that mailbox can join, and the instance keeps the address only as a fingerprint.'**
String get shareInviteEmailHint;
/// Snack bar once an invitation by email went out
///
/// In en, this message translates to:
/// **'Invitation mailed to {address}'**
String shareInviteMailed(String address);
/// A member row's name for an invitation mailed to an address, which members never see
///
/// In en, this message translates to:
/// **'Invited by email'**
String get shareInvitedByEmail;
/// Switch on the Share sheet letting people join by the studyset's link
///
/// In en, this message translates to:
/// **'Join by link'**
String get shareLinkTitle;
/// Below the switch for joining by link
///
/// In en, this message translates to:
/// **'Anyone with the link can join as a Viewer. Switching it off ends the link for good.'**
String get shareLinkHint;
/// Button copying the studyset's join link
///
/// In en, this message translates to:
/// **'Copy link'**
String get shareLinkCopy;
/// Snack bar once the join link is copied
///
/// In en, this message translates to:
/// **'Link copied'**
String get shareLinkCopied;
/// An invitation or code refused for an address that isn't one
///
/// In en, this message translates to:
/// **'That isn\'t an email address.'**
String get shareRefusedNotAnAddress;
/// An invitation or code refused as too many went out this hour
///
/// In en, this message translates to:
/// **'Too many mails to that address for now. Try again in an hour.'**
String get shareRefusedTooManyMails;
/// An invitation or code whose mail failed
///
/// In en, this message translates to:
/// **'The mail couldn\'t be sent. Try again later.'**
String get shareRefusedMailFailed;
/// An invitation by email refused as the person has no handle
///
/// In en, this message translates to:
/// **'An invitation by mail names you by your handle: choose one in your profile first.'**
String get shareRefusedHandleNeeded;
/// The Library's menu entry joining a studyset shared by link or invitation
///
/// In en, this message translates to:
/// **'Join with a link'**
String get libraryJoin;
/// Field of the Join sheet taking a pasted link
///
/// In en, this message translates to:
/// **'Link or code'**
String get joinLinkLabel;
/// Button looking up a pasted join link
///
/// In en, this message translates to:
/// **'Continue'**
String get joinContinue;
/// What a join link offers
///
/// In en, this message translates to:
/// **'{inviter} wants to share a studyset with you'**
String joinInvites(String inviter);
/// Who invites, where they have no public profile
///
/// In en, this message translates to:
/// **'Someone'**
String get joinSomeone;
/// What joining as a Viewer gives
///
/// In en, this message translates to:
/// **'You can view and study it.'**
String get joinAsViewer;
/// What joining as an Editor gives
///
/// In en, this message translates to:
/// **'You can view, study and change it.'**
String get joinAsEditor;
/// Above the address field of an invitation by email
///
/// In en, this message translates to:
/// **'This invitation went to an email address. Type it, and you\'ll get a code there that shows it\'s yours.'**
String get joinEmailHint;
/// Button mailing a code to accept an invitation
///
/// In en, this message translates to:
/// **'Send code'**
String get joinSendCode;
/// Once a code to join was mailed
///
/// In en, this message translates to:
/// **'A code is on its way to {address}.'**
String joinCodeSent(String address);
/// Field taking the mailed code
///
/// In en, this message translates to:
/// **'Code from the mail'**
String get joinCodeLabel;
/// Button joining the shared studyset
///
/// In en, this message translates to:
/// **'Join'**
String get joinAction;
/// Once joined
///
/// In en, this message translates to:
/// **'You joined. The studyset arrives in your Library with the next sync.'**
String get joinDone;
/// A join link to a studyset the person is in already
///
/// In en, this message translates to:
/// **'You\'re a member already.'**
String get joinAlreadyMember;
/// A join link that holds no more
///
/// In en, this message translates to:
/// **'This link holds no more: it was switched off, has lapsed or was used already.'**
String get joinNotFound;
/// A mailed code typed wrong
///
/// In en, this message translates to:
/// **'That code isn\'t right.'**
String get joinWrongCode;
/// A mailed code used, tried too often or too old
///
/// In en, this message translates to:
/// **'This code is no longer valid. Send a new one.'**
String get joinCodeExpired;
/// A code proving another address than the one invited
///
/// In en, this message translates to:
/// **'This invitation went to another address.'**
String get joinOtherAddress;
/// Heading of the Inbox's studysets shared with the person
///
/// In en, this message translates to:
/// **'Shared with you'**
String get inboxShared;
/// An Inbox entry of a studyset shared with the person
///
/// In en, this message translates to:
/// **'@{handle} shared {title} with you'**
String inboxSharedBy(String handle, String title);
/// An Inbox entry of a studyset shared by someone without a public profile
///
/// In en, this message translates to:
/// **'{title} was shared with you'**
String inboxSharedNoHandle(String title);
/// The title of a studyset shared with the person that hasn't synced yet
///
/// In en, this message translates to:
/// **'A studyset on its way'**
String get inboxSharedArriving;
/// Button opening a studyset shared with the person
///
/// In en, this message translates to:
/// **'Open'**
String get inboxSharedOpen;
/// Mark on a library card of a studyset someone shared with the person, until opened
///
/// In en, this message translates to:
/// **'New'**
String get studysetNew;
}
class _AppLocalizationsDelegate