feat(profile): sign-in methods matrix — email add/change, provider link/unlink

Profile shows the account's sign-in methods for guests and durable accounts alike:
add or change email, link Telegram on the web (login widget), and unlink a linked
provider. Email is never unlinked (it is changed); unlink is offered only when
another identity remains, and a change to an address owned by another account shows
the non-disclosing 'check the address or contact support'. Add-VK-on-web stays
deferred (no VK OAuth).

Wires linkUnlink / changeEmailRequest / changeEmailConfirm through the client,
transport, mock and codec (encodeLinkUnlink + LinkResult 'unlinked'/'changed'
statuses); codec wire tests; ru/en i18n.
This commit is contained in:
Ilia Denisov
2026-07-03 09:57:05 +02:00
parent b918217497
commit e4fc7f033d
9 changed files with 269 additions and 24 deletions
+5 -4
View File
@@ -346,13 +346,14 @@ export interface Session {
displayName: string;
}
// LinkResult is the outcome of an account link/merge step. status is
// LinkResult is the outcome of an account link/merge/unlink/change step. status is
// 'linked' (bound to the current account), 'merge_required' (the identity belongs to
// another account — the secondary* fields summarise it for the irreversible
// confirmation) or 'merged'. session is set only when the active account switched
// (a guest initiator whose durable counterpart won); the client adopts it.
// confirmation), 'merged', 'unlinked' (a provider detached) or 'changed' (the email was
// switched). session is set only when the active account switched (a guest initiator
// whose durable counterpart won); the client adopts it.
export interface LinkResult {
status: 'linked' | 'merge_required' | 'merged';
status: 'linked' | 'merge_required' | 'merged' | 'unlinked' | 'changed';
secondaryUserId: string;
secondaryDisplayName: string;
secondaryGames: number;