diff --git a/backend/internal/notify/events.go b/backend/internal/notify/events.go index 795f287..7fdf6e4 100644 --- a/backend/internal/notify/events.go +++ b/backend/internal/notify/events.go @@ -155,6 +155,13 @@ func Notification(userID uuid.UUID, kind string) Intent { return Intent{UserID: userID, Kind: KindNotification, Payload: b.FinishedBytes(), EventID: eventID()} } +// ProfileChanged is a payload-free "re-fetch your profile" signal to userID, emitted +// when the viewer's own account changed out of band — an email confirmed through the +// one-tap deeplink opened in another browser. +func ProfileChanged(userID uuid.UUID) Intent { + return Notification(userID, NotifyProfile) +} + // NotificationAccount builds a lobby notification of one of the friend_* kinds carrying the // account it concerns (the requester, the new friend or the decliner), so the client updates its // requests/friends lists and the in-game "add friend" state without a refetch. diff --git a/backend/internal/notify/notify.go b/backend/internal/notify/notify.go index 4b2204a..e95f0f2 100644 --- a/backend/internal/notify/notify.go +++ b/backend/internal/notify/notify.go @@ -69,6 +69,10 @@ const ( // it re-fetches profile.get to show or hide the banner. It carries no payload (the // banner set rides the profile response). In-app only. NotifyBanner = "banner" + // NotifyProfile tells the client that the viewer's own profile changed out of band + // (e.g. an email was confirmed via the one-tap deeplink opened in another browser), + // so it re-fetches profile.get. It carries no payload. In-app only. + NotifyProfile = "profile" // NotifyUserBlocked confirms to the blocker that a per-user block took effect, // carrying the blocked account, so every one of the blocker's sessions updates the // in-game block/add-friend controls and the struck name in place. It is delivered diff --git a/backend/internal/server/handlers_auth.go b/backend/internal/server/handlers_auth.go index e13138f..edfc8a5 100644 --- a/backend/internal/server/handlers_auth.go +++ b/backend/internal/server/handlers_auth.go @@ -9,6 +9,7 @@ import ( "go.uber.org/zap" "scrabble/backend/internal/account" + "scrabble/backend/internal/notify" ) // The /api/v1/internal/sessions/* endpoints are gateway-only: the gateway has @@ -252,11 +253,14 @@ func (s *Server) handleEmailConfirmLink(c *gin.Context) { c.JSON(http.StatusOK, confirmLinkResponse{Purpose: "login", Status: "confirmed", Session: &sess}) return } - status := "confirmed" if res.NeedsMerge { - status = "merge_required" + c.JSON(http.StatusOK, confirmLinkResponse{Purpose: "link", Status: "merge_required"}) + return } - c.JSON(http.StatusOK, confirmLinkResponse{Purpose: "link", Status: status}) + // A free link attached the email; nudge the account's in-app session(s) to re-fetch + // the profile, so a link confirmed in another browser reflects at once. + s.notifier.Publish(notify.ProfileChanged(res.Account)) + c.JSON(http.StatusOK, confirmLinkResponse{Purpose: "link", Status: "confirmed"}) } // tokenRequest carries an opaque session token. diff --git a/gateway/internal/backendclient/api.go b/gateway/internal/backendclient/api.go index 9ee4bd0..2d4299c 100644 --- a/gateway/internal/backendclient/api.go +++ b/gateway/internal/backendclient/api.go @@ -276,9 +276,9 @@ func (c *Client) GuestAuth(ctx context.Context, browserTz string) (SessionResp, // EmailRequest asks the backend to mail a login code, provisioning the account on // first contact; browserTz (the client's detected "±HH:MM" UTC offset) seeds the new // account's time zone, since the email account is created here, not at login. -func (c *Client) EmailRequest(ctx context.Context, email, browserTz string) error { +func (c *Client) EmailRequest(ctx context.Context, email, browserTz, language string) error { return c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/email/request", "", "", - map[string]string{"email": email, "browser_tz": browserTz}, nil) + map[string]string{"email": email, "browser_tz": browserTz, "language": language}, nil) } // EmailLogin verifies a login code and mints a session. @@ -289,6 +289,23 @@ func (c *Client) EmailLogin(ctx context.Context, email, code string) (SessionRes return out, err } +// ConfirmLinkResp is the backend's outcome of a one-tap deeplink confirmation: for a +// login Session carries the minted credential; for a link Status is "confirmed" or +// "merge_required". +type ConfirmLinkResp struct { + Purpose string `json:"purpose"` + Status string `json:"status"` + Session *SessionResp `json:"session,omitempty"` +} + +// EmailConfirmLink verifies a one-tap deeplink token; the token is the authorization. +func (c *Client) EmailConfirmLink(ctx context.Context, token string) (ConfirmLinkResp, error) { + var out ConfirmLinkResp + err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/email/confirm-link", "", "", + map[string]string{"token": token}, &out) + return out, err +} + // ResolveSession maps a token to its account id and guest flag (gateway // session-cache miss). The guest flag lets the edge gate guest-forbidden ops. func (c *Client) ResolveSession(ctx context.Context, token string) (string, bool, error) { diff --git a/gateway/internal/transcode/encode.go b/gateway/internal/transcode/encode.go index 7ef473f..e2d9f8c 100644 --- a/gateway/internal/transcode/encode.go +++ b/gateway/internal/transcode/encode.go @@ -37,6 +37,35 @@ func encodeAck(ok bool) []byte { return b.FinishedBytes() } +// encodeConfirmLinkResult builds an EmailConfirmLinkResult payload, embedding the +// minted Session for a login. All strings and the nested Session table are built +// before the result table is opened. +func encodeConfirmLinkResult(r backendclient.ConfirmLinkResp) []byte { + b := flatbuffers.NewBuilder(160) + purpose := b.CreateString(r.Purpose) + status := b.CreateString(r.Status) + var session flatbuffers.UOffsetT + if r.Session != nil { + token := b.CreateString(r.Session.Token) + uid := b.CreateString(r.Session.UserID) + name := b.CreateString(r.Session.DisplayName) + fb.SessionStart(b) + fb.SessionAddToken(b, token) + fb.SessionAddUserId(b, uid) + fb.SessionAddIsGuest(b, r.Session.IsGuest) + fb.SessionAddDisplayName(b, name) + session = fb.SessionEnd(b) + } + fb.EmailConfirmLinkResultStart(b) + fb.EmailConfirmLinkResultAddPurpose(b, purpose) + fb.EmailConfirmLinkResultAddStatus(b, status) + if r.Session != nil { + fb.EmailConfirmLinkResultAddSession(b, session) + } + b.Finish(fb.EmailConfirmLinkResultEnd(b)) + return b.FinishedBytes() +} + // encodeProfile builds a Profile payload, including the advertising-banner block // when the backend marked the viewer eligible. func encodeProfile(p backendclient.ProfileResp) []byte { diff --git a/gateway/internal/transcode/transcode.go b/gateway/internal/transcode/transcode.go index 26c775f..9a99a97 100644 --- a/gateway/internal/transcode/transcode.go +++ b/gateway/internal/transcode/transcode.go @@ -19,37 +19,38 @@ import ( // Message types in the vertical slice. const ( - MsgAuthTelegram = "auth.telegram" - MsgAuthVK = "auth.vk" - MsgAuthGuest = "auth.guest" - MsgAuthEmailReq = "auth.email.request" - MsgAuthEmailLogin = "auth.email.login" - MsgProfileGet = "profile.get" - MsgBlockStatus = "account.block_status" - MsgGameSubmitPlay = "game.submit_play" - MsgGameState = "game.state" - MsgLobbyEnqueue = "lobby.enqueue" - MsgLobbyCancel = "lobby.cancel" - MsgLobbyPoll = "lobby.poll" - MsgChatPost = "chat.post" - MsgGamesList = "games.list" - MsgGamePass = "game.pass" - MsgGameExchange = "game.exchange" - MsgGameResign = "game.resign" - MsgGameHint = "game.hint" - MsgGameEvaluate = "game.evaluate" - MsgGameCheckWord = "game.check_word" - MsgGameComplaint = "game.complaint" - MsgGameHistory = "game.history" - MsgChatList = "chat.list" - MsgChatNudge = "chat.nudge" - MsgChatRead = "chat.read" - MsgDraftGet = "draft.get" - MsgDraftSave = "draft.save" - MsgGameHide = "game.hide" - MsgFeedbackSubmit = "feedback.submit" - MsgFeedbackGet = "feedback.get" - MsgFeedbackUnread = "feedback.unread" + MsgAuthTelegram = "auth.telegram" + MsgAuthVK = "auth.vk" + MsgAuthGuest = "auth.guest" + MsgAuthEmailReq = "auth.email.request" + MsgAuthEmailLogin = "auth.email.login" + MsgAuthEmailConfirmLink = "auth.email.confirm_link" + MsgProfileGet = "profile.get" + MsgBlockStatus = "account.block_status" + MsgGameSubmitPlay = "game.submit_play" + MsgGameState = "game.state" + MsgLobbyEnqueue = "lobby.enqueue" + MsgLobbyCancel = "lobby.cancel" + MsgLobbyPoll = "lobby.poll" + MsgChatPost = "chat.post" + MsgGamesList = "games.list" + MsgGamePass = "game.pass" + MsgGameExchange = "game.exchange" + MsgGameResign = "game.resign" + MsgGameHint = "game.hint" + MsgGameEvaluate = "game.evaluate" + MsgGameCheckWord = "game.check_word" + MsgGameComplaint = "game.complaint" + MsgGameHistory = "game.history" + MsgChatList = "chat.list" + MsgChatNudge = "chat.nudge" + MsgChatRead = "chat.read" + MsgDraftGet = "draft.get" + MsgDraftSave = "draft.save" + MsgGameHide = "game.hide" + MsgFeedbackSubmit = "feedback.submit" + MsgFeedbackGet = "feedback.get" + MsgFeedbackUnread = "feedback.unread" ) // Request is one decoded Execute call. @@ -101,6 +102,7 @@ func NewRegistry(backend *backendclient.Client, tg TelegramValidator, opts ...Op r.ops[MsgAuthGuest] = Op{Handler: authGuestHandler(backend)} r.ops[MsgAuthEmailReq] = Op{Handler: authEmailRequestHandler(backend), Email: true} r.ops[MsgAuthEmailLogin] = Op{Handler: authEmailLoginHandler(backend), Email: true} + r.ops[MsgAuthEmailConfirmLink] = Op{Handler: authEmailConfirmLinkHandler(backend)} r.ops[MsgProfileGet] = Op{Handler: profileHandler(backend), Auth: true} r.ops[MsgBlockStatus] = Op{Handler: blockStatusHandler(backend), Auth: true} r.ops[MsgGameSubmitPlay] = Op{Handler: submitPlayHandler(backend), Auth: true} @@ -238,7 +240,7 @@ func authGuestHandler(backend *backendclient.Client) Handler { func authEmailRequestHandler(backend *backendclient.Client) Handler { return func(ctx context.Context, req Request) ([]byte, error) { in := fb.GetRootAsEmailRequestRequest(req.Payload, 0) - if err := backend.EmailRequest(ctx, string(in.Email()), string(in.BrowserTz())); err != nil { + if err := backend.EmailRequest(ctx, string(in.Email()), string(in.BrowserTz()), string(in.Language())); err != nil { return nil, err } return encodeAck(true), nil @@ -256,6 +258,17 @@ func authEmailLoginHandler(backend *backendclient.Client) Handler { } } +func authEmailConfirmLinkHandler(backend *backendclient.Client) Handler { + return func(ctx context.Context, req Request) ([]byte, error) { + in := fb.GetRootAsEmailConfirmLinkRequest(req.Payload, 0) + res, err := backend.EmailConfirmLink(ctx, string(in.Token())) + if err != nil { + return nil, err + } + return encodeConfirmLinkResult(res), nil + } +} + func profileHandler(backend *backendclient.Client) Handler { return func(ctx context.Context, req Request) ([]byte, error) { p, err := backend.Profile(ctx, req.UserID) diff --git a/pkg/fbs/scrabble.fbs b/pkg/fbs/scrabble.fbs index de830fc..5d6559f 100644 --- a/pkg/fbs/scrabble.fbs +++ b/pkg/fbs/scrabble.fbs @@ -132,10 +132,12 @@ table GuestLoginRequest { // EmailRequestRequest asks the backend to send a login confirm-code to email. It // also provisions the account on first contact, so browser_tz (the detected UTC -// offset) is seeded into its time zone here, not at the later login step. +// offset) is seeded into its time zone here, not at the later login step; language +// (the client's UI language) seeds the new account's language and localises the email. table EmailRequestRequest { email:string; browser_tz:string; + language:string; } // EmailLoginRequest logs in to the account owning email (provisioned at the @@ -145,6 +147,21 @@ table EmailLoginRequest { code:string; } +// EmailConfirmLinkRequest verifies a one-tap deeplink token from a confirmation +// email. The token, not a session, is the authorization. +table EmailConfirmLinkRequest { + token:string; +} + +// EmailConfirmLinkResult is the outcome of a deeplink confirmation: purpose is +// "login" or "link"; for a login session carries the minted credential; for a link +// status is "confirmed" or "merge_required" (the app completes the merge). +table EmailConfirmLinkResult { + purpose:string; + status:string; + session:Session; +} + // Session is the minted credential returned by every auth operation. table Session { token:string; diff --git a/pkg/fbs/scrabblefb/EmailConfirmLinkRequest.go b/pkg/fbs/scrabblefb/EmailConfirmLinkRequest.go new file mode 100644 index 0000000..fbeec41 --- /dev/null +++ b/pkg/fbs/scrabblefb/EmailConfirmLinkRequest.go @@ -0,0 +1,60 @@ +// Code generated by the FlatBuffers compiler. DO NOT EDIT. + +package scrabblefb + +import ( + flatbuffers "github.com/google/flatbuffers/go" +) + +type EmailConfirmLinkRequest struct { + _tab flatbuffers.Table +} + +func GetRootAsEmailConfirmLinkRequest(buf []byte, offset flatbuffers.UOffsetT) *EmailConfirmLinkRequest { + n := flatbuffers.GetUOffsetT(buf[offset:]) + x := &EmailConfirmLinkRequest{} + x.Init(buf, n+offset) + return x +} + +func FinishEmailConfirmLinkRequestBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { + builder.Finish(offset) +} + +func GetSizePrefixedRootAsEmailConfirmLinkRequest(buf []byte, offset flatbuffers.UOffsetT) *EmailConfirmLinkRequest { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &EmailConfirmLinkRequest{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + +func FinishSizePrefixedEmailConfirmLinkRequestBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { + builder.FinishSizePrefixed(offset) +} + +func (rcv *EmailConfirmLinkRequest) Init(buf []byte, i flatbuffers.UOffsetT) { + rcv._tab.Bytes = buf + rcv._tab.Pos = i +} + +func (rcv *EmailConfirmLinkRequest) Table() flatbuffers.Table { + return rcv._tab +} + +func (rcv *EmailConfirmLinkRequest) Token() []byte { + o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) + if o != 0 { + return rcv._tab.ByteVector(o + rcv._tab.Pos) + } + return nil +} + +func EmailConfirmLinkRequestStart(builder *flatbuffers.Builder) { + builder.StartObject(1) +} +func EmailConfirmLinkRequestAddToken(builder *flatbuffers.Builder, token flatbuffers.UOffsetT) { + builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(token), 0) +} +func EmailConfirmLinkRequestEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { + return builder.EndObject() +} diff --git a/pkg/fbs/scrabblefb/EmailConfirmLinkResult.go b/pkg/fbs/scrabblefb/EmailConfirmLinkResult.go new file mode 100644 index 0000000..78ef0dd --- /dev/null +++ b/pkg/fbs/scrabblefb/EmailConfirmLinkResult.go @@ -0,0 +1,87 @@ +// Code generated by the FlatBuffers compiler. DO NOT EDIT. + +package scrabblefb + +import ( + flatbuffers "github.com/google/flatbuffers/go" +) + +type EmailConfirmLinkResult struct { + _tab flatbuffers.Table +} + +func GetRootAsEmailConfirmLinkResult(buf []byte, offset flatbuffers.UOffsetT) *EmailConfirmLinkResult { + n := flatbuffers.GetUOffsetT(buf[offset:]) + x := &EmailConfirmLinkResult{} + x.Init(buf, n+offset) + return x +} + +func FinishEmailConfirmLinkResultBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { + builder.Finish(offset) +} + +func GetSizePrefixedRootAsEmailConfirmLinkResult(buf []byte, offset flatbuffers.UOffsetT) *EmailConfirmLinkResult { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &EmailConfirmLinkResult{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + +func FinishSizePrefixedEmailConfirmLinkResultBuffer(builder *flatbuffers.Builder, offset flatbuffers.UOffsetT) { + builder.FinishSizePrefixed(offset) +} + +func (rcv *EmailConfirmLinkResult) Init(buf []byte, i flatbuffers.UOffsetT) { + rcv._tab.Bytes = buf + rcv._tab.Pos = i +} + +func (rcv *EmailConfirmLinkResult) Table() flatbuffers.Table { + return rcv._tab +} + +func (rcv *EmailConfirmLinkResult) Purpose() []byte { + o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) + if o != 0 { + return rcv._tab.ByteVector(o + rcv._tab.Pos) + } + return nil +} + +func (rcv *EmailConfirmLinkResult) Status() []byte { + o := flatbuffers.UOffsetT(rcv._tab.Offset(6)) + if o != 0 { + return rcv._tab.ByteVector(o + rcv._tab.Pos) + } + return nil +} + +func (rcv *EmailConfirmLinkResult) Session(obj *Session) *Session { + o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) + if o != 0 { + x := rcv._tab.Indirect(o + rcv._tab.Pos) + if obj == nil { + obj = new(Session) + } + obj.Init(rcv._tab.Bytes, x) + return obj + } + return nil +} + +func EmailConfirmLinkResultStart(builder *flatbuffers.Builder) { + builder.StartObject(3) +} +func EmailConfirmLinkResultAddPurpose(builder *flatbuffers.Builder, purpose flatbuffers.UOffsetT) { + builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(purpose), 0) +} +func EmailConfirmLinkResultAddStatus(builder *flatbuffers.Builder, status flatbuffers.UOffsetT) { + builder.PrependUOffsetTSlot(1, flatbuffers.UOffsetT(status), 0) +} +func EmailConfirmLinkResultAddSession(builder *flatbuffers.Builder, session flatbuffers.UOffsetT) { + builder.PrependUOffsetTSlot(2, flatbuffers.UOffsetT(session), 0) +} +func EmailConfirmLinkResultEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { + return builder.EndObject() +} diff --git a/pkg/fbs/scrabblefb/EmailRequestRequest.go b/pkg/fbs/scrabblefb/EmailRequestRequest.go index 567b8ba..9f95230 100644 --- a/pkg/fbs/scrabblefb/EmailRequestRequest.go +++ b/pkg/fbs/scrabblefb/EmailRequestRequest.go @@ -57,8 +57,16 @@ func (rcv *EmailRequestRequest) BrowserTz() []byte { return nil } +func (rcv *EmailRequestRequest) Language() []byte { + o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) + if o != 0 { + return rcv._tab.ByteVector(o + rcv._tab.Pos) + } + return nil +} + func EmailRequestRequestStart(builder *flatbuffers.Builder) { - builder.StartObject(2) + builder.StartObject(3) } func EmailRequestRequestAddEmail(builder *flatbuffers.Builder, email flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(email), 0) @@ -66,6 +74,9 @@ func EmailRequestRequestAddEmail(builder *flatbuffers.Builder, email flatbuffers func EmailRequestRequestAddBrowserTz(builder *flatbuffers.Builder, browserTz flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(1, flatbuffers.UOffsetT(browserTz), 0) } +func EmailRequestRequestAddLanguage(builder *flatbuffers.Builder, language flatbuffers.UOffsetT) { + builder.PrependUOffsetTSlot(2, flatbuffers.UOffsetT(language), 0) +} func EmailRequestRequestEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/ui/src/gen/fbs/scrabblefb.ts b/ui/src/gen/fbs/scrabblefb.ts index a5aece4..f8b81e2 100644 --- a/ui/src/gen/fbs/scrabblefb.ts +++ b/ui/src/gen/fbs/scrabblefb.ts @@ -17,6 +17,8 @@ export { ComplaintRequest } from './scrabblefb/complaint-request.js'; export { CreateInvitationRequest } from './scrabblefb/create-invitation-request.js'; export { DraftRequest } from './scrabblefb/draft-request.js'; export { DraftView } from './scrabblefb/draft-view.js'; +export { EmailConfirmLinkRequest } from './scrabblefb/email-confirm-link-request.js'; +export { EmailConfirmLinkResult } from './scrabblefb/email-confirm-link-result.js'; export { EmailLoginRequest } from './scrabblefb/email-login-request.js'; export { EmailRequestRequest } from './scrabblefb/email-request-request.js'; export { EnqueueRequest } from './scrabblefb/enqueue-request.js'; diff --git a/ui/src/gen/fbs/scrabblefb/email-confirm-link-request.ts b/ui/src/gen/fbs/scrabblefb/email-confirm-link-request.ts new file mode 100644 index 0000000..e6d8274 --- /dev/null +++ b/ui/src/gen/fbs/scrabblefb/email-confirm-link-request.ts @@ -0,0 +1,48 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import * as flatbuffers from 'flatbuffers'; + +export class EmailConfirmLinkRequest { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):EmailConfirmLinkRequest { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsEmailConfirmLinkRequest(bb:flatbuffers.ByteBuffer, obj?:EmailConfirmLinkRequest):EmailConfirmLinkRequest { + return (obj || new EmailConfirmLinkRequest()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsEmailConfirmLinkRequest(bb:flatbuffers.ByteBuffer, obj?:EmailConfirmLinkRequest):EmailConfirmLinkRequest { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new EmailConfirmLinkRequest()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +token():string|null +token(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null +token(optionalEncoding?:any):string|Uint8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; +} + +static startEmailConfirmLinkRequest(builder:flatbuffers.Builder) { + builder.startObject(1); +} + +static addToken(builder:flatbuffers.Builder, tokenOffset:flatbuffers.Offset) { + builder.addFieldOffset(0, tokenOffset, 0); +} + +static endEmailConfirmLinkRequest(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static createEmailConfirmLinkRequest(builder:flatbuffers.Builder, tokenOffset:flatbuffers.Offset):flatbuffers.Offset { + EmailConfirmLinkRequest.startEmailConfirmLinkRequest(builder); + EmailConfirmLinkRequest.addToken(builder, tokenOffset); + return EmailConfirmLinkRequest.endEmailConfirmLinkRequest(builder); +} +} diff --git a/ui/src/gen/fbs/scrabblefb/email-confirm-link-result.ts b/ui/src/gen/fbs/scrabblefb/email-confirm-link-result.ts new file mode 100644 index 0000000..29551ab --- /dev/null +++ b/ui/src/gen/fbs/scrabblefb/email-confirm-link-result.ts @@ -0,0 +1,66 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import * as flatbuffers from 'flatbuffers'; + +import { Session } from '../scrabblefb/session.js'; + + +export class EmailConfirmLinkResult { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):EmailConfirmLinkResult { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsEmailConfirmLinkResult(bb:flatbuffers.ByteBuffer, obj?:EmailConfirmLinkResult):EmailConfirmLinkResult { + return (obj || new EmailConfirmLinkResult()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsEmailConfirmLinkResult(bb:flatbuffers.ByteBuffer, obj?:EmailConfirmLinkResult):EmailConfirmLinkResult { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new EmailConfirmLinkResult()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +purpose():string|null +purpose(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null +purpose(optionalEncoding?:any):string|Uint8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; +} + +status():string|null +status(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null +status(optionalEncoding?:any):string|Uint8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; +} + +session(obj?:Session):Session|null { + const offset = this.bb!.__offset(this.bb_pos, 8); + return offset ? (obj || new Session()).__init(this.bb!.__indirect(this.bb_pos + offset), this.bb!) : null; +} + +static startEmailConfirmLinkResult(builder:flatbuffers.Builder) { + builder.startObject(3); +} + +static addPurpose(builder:flatbuffers.Builder, purposeOffset:flatbuffers.Offset) { + builder.addFieldOffset(0, purposeOffset, 0); +} + +static addStatus(builder:flatbuffers.Builder, statusOffset:flatbuffers.Offset) { + builder.addFieldOffset(1, statusOffset, 0); +} + +static addSession(builder:flatbuffers.Builder, sessionOffset:flatbuffers.Offset) { + builder.addFieldOffset(2, sessionOffset, 0); +} + +static endEmailConfirmLinkResult(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +} diff --git a/ui/src/gen/fbs/scrabblefb/email-request-request.ts b/ui/src/gen/fbs/scrabblefb/email-request-request.ts index 6ecbb46..47212e5 100644 --- a/ui/src/gen/fbs/scrabblefb/email-request-request.ts +++ b/ui/src/gen/fbs/scrabblefb/email-request-request.ts @@ -34,8 +34,15 @@ browserTz(optionalEncoding?:any):string|Uint8Array|null { return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; } +language():string|null +language(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null +language(optionalEncoding?:any):string|Uint8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 8); + return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; +} + static startEmailRequestRequest(builder:flatbuffers.Builder) { - builder.startObject(2); + builder.startObject(3); } static addEmail(builder:flatbuffers.Builder, emailOffset:flatbuffers.Offset) { @@ -46,15 +53,20 @@ static addBrowserTz(builder:flatbuffers.Builder, browserTzOffset:flatbuffers.Off builder.addFieldOffset(1, browserTzOffset, 0); } +static addLanguage(builder:flatbuffers.Builder, languageOffset:flatbuffers.Offset) { + builder.addFieldOffset(2, languageOffset, 0); +} + static endEmailRequestRequest(builder:flatbuffers.Builder):flatbuffers.Offset { const offset = builder.endObject(); return offset; } -static createEmailRequestRequest(builder:flatbuffers.Builder, emailOffset:flatbuffers.Offset, browserTzOffset:flatbuffers.Offset):flatbuffers.Offset { +static createEmailRequestRequest(builder:flatbuffers.Builder, emailOffset:flatbuffers.Offset, browserTzOffset:flatbuffers.Offset, languageOffset:flatbuffers.Offset):flatbuffers.Offset { EmailRequestRequest.startEmailRequestRequest(builder); EmailRequestRequest.addEmail(builder, emailOffset); EmailRequestRequest.addBrowserTz(builder, browserTzOffset); + EmailRequestRequest.addLanguage(builder, languageOffset); return EmailRequestRequest.endEmailRequestRequest(builder); } }