diff --git a/pkg/hicli/hicli.go b/pkg/hicli/hicli.go index 4c1a97e..4c3fea2 100644 --- a/pkg/hicli/hicli.go +++ b/pkg/hicli/hicli.go @@ -60,6 +60,7 @@ type HiClient struct { syncLock sync.Mutex stopSync atomic.Pointer[context.CancelFunc] encryptLock sync.Mutex + loginLock sync.Mutex requestQueueWakeup chan struct{} diff --git a/pkg/hicli/login.go b/pkg/hicli/login.go index ae45597..fb67540 100644 --- a/pkg/hicli/login.go +++ b/pkg/hicli/login.go @@ -36,6 +36,12 @@ func (h *HiClient) LoginPassword(ctx context.Context, homeserverURL, username, p } func (h *HiClient) Login(ctx context.Context, req *mautrix.ReqLogin) error { + h.loginLock.Lock() + defer h.loginLock.Unlock() + if h.IsLoggedIn() { + return fmt.Errorf("already logged in") + } + err := h.CheckServerVersions(ctx) if err != nil { return err diff --git a/pkg/hicli/paginate.go b/pkg/hicli/paginate.go index eeb41d9..0a7cf5e 100644 --- a/pkg/hicli/paginate.go +++ b/pkg/hicli/paginate.go @@ -101,6 +101,8 @@ func (h *HiClient) processGetRoomState(ctx context.Context, roomID id.RoomID, fe room, err := h.DB.Room.Get(ctx, roomID) if err != nil { return fmt.Errorf("failed to get room from database: %w", err) + } else if room == nil { + return fmt.Errorf("room not found") } updatedRoom := &database.Room{ ID: room.ID, diff --git a/pkg/hicli/sync.go b/pkg/hicli/sync.go index 0d8e86c..c1aa8e3 100644 --- a/pkg/hicli/sync.go +++ b/pkg/hicli/sync.go @@ -126,15 +126,16 @@ func (h *HiClient) maybeDiscardOutboundSession(ctx context.Context, newMembershi prevMembership = event.Membership(gjson.GetBytes(evt.Unsigned.PrevContent.VeryRaw, "membership").Str) } if prevMembership == "unknown" || prevMembership == "" { - cs, err := h.DB.CurrentState.Get(ctx, evt.RoomID, event.StateMember, h.Account.UserID.String()) + cs, err := h.DB.CurrentState.Get(ctx, evt.RoomID, event.StateMember, evt.GetStateKey()) if err != nil { zerolog.Ctx(ctx).Warn().Err(err). Stringer("room_id", evt.RoomID). Str("user_id", evt.GetStateKey()). Msg("Failed to get previous membership") return false + } else if cs != nil { + prevMembership = event.Membership(gjson.GetBytes(cs.Content, "membership").Str) } - prevMembership = event.Membership(gjson.GetBytes(cs.Content, "membership").Str) } if prevMembership == newMembership || (prevMembership == event.MembershipInvite && newMembership == event.MembershipJoin && h.shouldShareKeysToInvitedUsers(ctx, evt.RoomID)) || diff --git a/web/src/ui/login/BeeperLogin.tsx b/web/src/ui/login/BeeperLogin.tsx index 95b2d4d..5f59485 100644 --- a/web/src/ui/login/BeeperLogin.tsx +++ b/web/src/ui/login/BeeperLogin.tsx @@ -26,6 +26,7 @@ const BeeperLogin = ({ domain, client }: BeeperLoginProps) => { const [email, setEmail] = useState("") const [requestID, setRequestID] = useState("") const [code, setCode] = useState("") + const [loading, setLoading] = useState(false) const [error, setError] = useState("") const onChangeEmail = (evt: React.ChangeEvent) => { @@ -41,16 +42,18 @@ const BeeperLogin = ({ domain, client }: BeeperLoginProps) => { const requestCode = (evt: React.FormEvent) => { evt.preventDefault() + setLoading(true) beeper.doStartLogin(domain).then( request => beeper.doRequestCode(domain, request, email).then( () => setRequestID(request), err => setError(`Failed to request code: ${err}`), ), err => setError(`Failed to start login: ${err}`), - ) + ).finally(() => setLoading(false)) } const submitCode = (evt: React.FormEvent) => { evt.preventDefault() + setLoading(true) beeper.doSubmitCode(domain, requestID, code).then( token => { client.rpc.loginCustom(`https://matrix.${domain}`, { @@ -59,7 +62,7 @@ const BeeperLogin = ({ domain, client }: BeeperLoginProps) => { }).catch(err => setError(`Failed to login with token: ${err}`)) }, err => setError(`Failed to submit code: ${err}`), - ) + ).finally(() => setLoading(false)) } return
@@ -83,6 +86,7 @@ const BeeperLogin = ({ domain, client }: BeeperLoginProps) => { {error &&
{error} diff --git a/web/src/ui/login/LoginScreen.tsx b/web/src/ui/login/LoginScreen.tsx index 3de5a55..772e9f9 100644 --- a/web/src/ui/login/LoginScreen.tsx +++ b/web/src/ui/login/LoginScreen.tsx @@ -31,9 +31,11 @@ export const LoginScreen = ({ client }: LoginScreenProps) => { const [password, setPassword] = useState("") const [homeserverURL, setHomeserverURL] = useState("") const [loginFlows, setLoginFlows] = useState(null) + const [loading, setLoading] = useState(false) const [error, setError] = useState("") const loginSSO = () => { + setLoading(true) fetch("_gomuks/sso", { method: "POST", body: JSON.stringify({ homeserver_url: homeserverURL }), @@ -51,7 +53,7 @@ export const LoginScreen = ({ client }: LoginScreenProps) => { window.location.href = `${homeserverURL}/_matrix/client/v3/login/sso/redirect?redirectUrl=${redir}` }, err => setError(`Failed to start SSO login: ${err}`), - ) + ).finally(() => setLoading(false)) } const login = (evt: React.FormEvent) => { @@ -61,10 +63,11 @@ export const LoginScreen = ({ client }: LoginScreenProps) => { } else if (!loginFlows.includes("m.login.password")) { loginSSO() } else { + setLoading(true) client.rpc.login(homeserverURL, username, password).then( () => {}, err => setError(err.toString()), - ) + ).finally(() => setLoading(false)) } } @@ -143,11 +146,13 @@ export const LoginScreen = ({ client }: LoginScreenProps) => { {supportsSSO && } {supportsPassword && }
{error &&