mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-09 16:53:36 -05:00
chore(doc): document client controller
This commit is contained in:
parent
31220db8fe
commit
0e86f19da2
2 changed files with 90 additions and 22 deletions
|
@ -10,6 +10,9 @@ import Auth from "../../mobx/stores/Auth";
|
||||||
import { modalController } from "../modals/ModalController";
|
import { modalController } from "../modals/ModalController";
|
||||||
import Session from "./Session";
|
import Session from "./Session";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls the lifecycles of clients
|
||||||
|
*/
|
||||||
class ClientController {
|
class ClientController {
|
||||||
/**
|
/**
|
||||||
* API client
|
* API client
|
||||||
|
@ -36,6 +39,7 @@ class ClientController {
|
||||||
apiURL: import.meta.env.VITE_API_URL,
|
apiURL: import.meta.env.VITE_API_URL,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ! FIXME: loop until success infinitely
|
||||||
this.apiClient
|
this.apiClient
|
||||||
.fetchConfiguration()
|
.fetchConfiguration()
|
||||||
.then(() => (this.configuration = this.apiClient.configuration!));
|
.then(() => (this.configuration = this.apiClient.configuration!));
|
||||||
|
@ -70,26 +74,51 @@ class ClientController {
|
||||||
this.pickNextSession();
|
this.pickNextSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently selected session
|
||||||
|
* @returns Active Session
|
||||||
|
*/
|
||||||
@computed getActiveSession() {
|
@computed getActiveSession() {
|
||||||
return this.sessions.get(this.current!);
|
return this.sessions.get(this.current!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an unauthenticated instance of the Revolt.js Client
|
||||||
|
* @returns API Client
|
||||||
|
*/
|
||||||
@computed getAnonymousClient() {
|
@computed getAnonymousClient() {
|
||||||
return this.apiClient;
|
return this.apiClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next available client (either from session or API)
|
||||||
|
* @returns Revolt.js Client
|
||||||
|
*/
|
||||||
@computed getAvailableClient() {
|
@computed getAvailableClient() {
|
||||||
return this.getActiveSession()?.client ?? this.apiClient;
|
return this.getActiveSession()?.client ?? this.apiClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch server configuration
|
||||||
|
* @returns Server Configuration
|
||||||
|
*/
|
||||||
@computed getServerConfig() {
|
@computed getServerConfig() {
|
||||||
return this.configuration;
|
return this.configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether we are logged in right now
|
||||||
|
* @returns Whether we are logged in
|
||||||
|
*/
|
||||||
@computed isLoggedIn() {
|
@computed isLoggedIn() {
|
||||||
return this.current === null;
|
return this.current === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new client lifecycle
|
||||||
|
* @param entry Session Information
|
||||||
|
* @param knowledge Whether the session is new or existing
|
||||||
|
*/
|
||||||
@action addSession(
|
@action addSession(
|
||||||
entry: { session: SessionPrivate; apiUrl?: string },
|
entry: { session: SessionPrivate; apiUrl?: string },
|
||||||
knowledge: "new" | "existing",
|
knowledge: "new" | "existing",
|
||||||
|
@ -119,6 +148,10 @@ class ClientController {
|
||||||
this.pickNextSession();
|
this.pickNextSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login given a set of credentials
|
||||||
|
* @param credentials Credentials
|
||||||
|
*/
|
||||||
async login(credentials: API.DataLogin) {
|
async login(credentials: API.DataLogin) {
|
||||||
const browser = detect();
|
const browser = detect();
|
||||||
|
|
||||||
|
@ -181,35 +214,19 @@ class ClientController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start client lifecycle
|
||||||
this.addSession(
|
this.addSession(
|
||||||
{
|
{
|
||||||
session,
|
session,
|
||||||
},
|
},
|
||||||
"new",
|
"new",
|
||||||
);
|
);
|
||||||
|
|
||||||
/*const s = session;
|
|
||||||
|
|
||||||
client.session = session;
|
|
||||||
(client as any).$updateHeaders();
|
|
||||||
|
|
||||||
async function login() {
|
|
||||||
state.auth.setSession(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { onboarding } = await client.api.get("/onboard/hello");
|
|
||||||
|
|
||||||
if (onboarding) {
|
|
||||||
openScreen({
|
|
||||||
id: "onboarding",
|
|
||||||
callback: async (username: string) =>
|
|
||||||
client.completeOnboarding({ username }, false).then(login),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
login();
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log out of a specific user session
|
||||||
|
* @param user_id Target User ID
|
||||||
|
*/
|
||||||
@action logout(user_id: string) {
|
@action logout(user_id: string) {
|
||||||
const session = this.sessions.get(user_id);
|
const session = this.sessions.get(user_id);
|
||||||
if (session) {
|
if (session) {
|
||||||
|
@ -223,12 +240,19 @@ class ClientController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout of the current session
|
||||||
|
*/
|
||||||
@action logoutCurrent() {
|
@action logoutCurrent() {
|
||||||
if (this.current) {
|
if (this.current) {
|
||||||
this.logout(this.current);
|
this.logout(this.current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch to another user session
|
||||||
|
* @param user_id Target User ID
|
||||||
|
*/
|
||||||
@action switchAccount(user_id: string) {
|
@action switchAccount(user_id: string) {
|
||||||
this.current = user_id;
|
this.current = user_id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,14 @@ import { state } from "../../mobx/State";
|
||||||
|
|
||||||
import { __thisIsAHack } from "../../context/intermediate/Intermediate";
|
import { __thisIsAHack } from "../../context/intermediate/Intermediate";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current lifecycle state
|
||||||
|
*/
|
||||||
type State = "Ready" | "Connecting" | "Online" | "Disconnected" | "Offline";
|
type State = "Ready" | "Connecting" | "Online" | "Disconnected" | "Offline";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible transitions between states
|
||||||
|
*/
|
||||||
type Transition =
|
type Transition =
|
||||||
| {
|
| {
|
||||||
action: "LOGIN";
|
action: "LOGIN";
|
||||||
|
@ -26,11 +32,17 @@ type Transition =
|
||||||
| "OFFLINE";
|
| "OFFLINE";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client lifecycle finite state machine
|
||||||
|
*/
|
||||||
export default class Session {
|
export default class Session {
|
||||||
state: State = window.navigator.onLine ? "Ready" : "Offline";
|
state: State = window.navigator.onLine ? "Ready" : "Offline";
|
||||||
user_id: string | null = null;
|
user_id: string | null = null;
|
||||||
client: Client | null = null;
|
client: Client | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Session
|
||||||
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
|
|
||||||
|
@ -44,7 +56,7 @@ export default class Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate logout and destroy client.
|
* Initiate logout and destroy client
|
||||||
*/
|
*/
|
||||||
@action destroy() {
|
@action destroy() {
|
||||||
if (this.client) {
|
if (this.client) {
|
||||||
|
@ -54,30 +66,46 @@ export default class Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when user's browser signals it is online
|
||||||
|
*/
|
||||||
private onOnline() {
|
private onOnline() {
|
||||||
this.emit({
|
this.emit({
|
||||||
action: "ONLINE",
|
action: "ONLINE",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when user's browser signals it is offline
|
||||||
|
*/
|
||||||
private onOffline() {
|
private onOffline() {
|
||||||
this.emit({
|
this.emit({
|
||||||
action: "OFFLINE",
|
action: "OFFLINE",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the client signals it has disconnected
|
||||||
|
*/
|
||||||
private onDropped() {
|
private onDropped() {
|
||||||
this.emit({
|
this.emit({
|
||||||
action: "DISCONNECT",
|
action: "DISCONNECT",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the client signals it has received the Ready packet
|
||||||
|
*/
|
||||||
private onReady() {
|
private onReady() {
|
||||||
this.emit({
|
this.emit({
|
||||||
action: "SUCCESS",
|
action: "SUCCESS",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Revolt.js Client for this Session
|
||||||
|
* @param apiUrl Optionally specify an API URL
|
||||||
|
*/
|
||||||
private createClient(apiUrl?: string) {
|
private createClient(apiUrl?: string) {
|
||||||
this.client = new Client({
|
this.client = new Client({
|
||||||
unreads: true,
|
unreads: true,
|
||||||
|
@ -90,12 +118,20 @@ export default class Session {
|
||||||
this.client.addListener("ready", this.onReady);
|
this.client.addListener("ready", this.onReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the client including any listeners.
|
||||||
|
*/
|
||||||
private destroyClient() {
|
private destroyClient() {
|
||||||
this.client!.removeAllListeners();
|
this.client!.removeAllListeners();
|
||||||
|
this.client!.logout();
|
||||||
this.user_id = null;
|
this.user_id = null;
|
||||||
this.client = null;
|
this.client = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure we are in one of the given states
|
||||||
|
* @param state Possible states
|
||||||
|
*/
|
||||||
private assert(...state: State[]) {
|
private assert(...state: State[]) {
|
||||||
let found = false;
|
let found = false;
|
||||||
for (const target of state) {
|
for (const target of state) {
|
||||||
|
@ -110,6 +146,10 @@ export default class Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Continue logging in provided onboarding is successful
|
||||||
|
* @param data Transition Data
|
||||||
|
*/
|
||||||
private async continueLogin(data: Transition & { action: "LOGIN" }) {
|
private async continueLogin(data: Transition & { action: "LOGIN" }) {
|
||||||
try {
|
try {
|
||||||
await this.client!.useExistingSession(data.session);
|
await this.client!.useExistingSession(data.session);
|
||||||
|
@ -121,6 +161,10 @@ export default class Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transition to a new state by a certain action
|
||||||
|
* @param data Transition Data
|
||||||
|
*/
|
||||||
@action async emit(data: Transition) {
|
@action async emit(data: Transition) {
|
||||||
console.info(`[FSM ${this.user_id ?? "Anonymous"}]`, data);
|
console.info(`[FSM ${this.user_id ?? "Anonymous"}]`, data);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue