feat(email): one-tap confirm deeplink + client language (PR1b) #162

Merged
developer merged 10 commits from feature/email-relay-pr1b into development 2026-07-03 06:58:12 +00:00
14 changed files with 418 additions and 41 deletions
Showing only changes of commit 762155a55e - Show all commits
+7
View File
@@ -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.
+4
View File
@@ -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
+7 -3
View File
@@ -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.
+19 -2
View File
@@ -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) {
+29
View File
@@ -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 {
+45 -32
View File
@@ -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)
+18 -1
View File
@@ -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;
@@ -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()
}
@@ -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()
}
+12 -1
View File
@@ -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()
}
+2
View File
@@ -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';
@@ -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);
}
}
@@ -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;
}
}
@@ -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);
}
}