Merge pull request #622 from vector-im/bwindels/sdk-refactoring

Some API cleanup ahead of first SDK release
This commit is contained in:
Bruno Windels 2021-12-22 17:58:20 +01:00 committed by GitHub
commit 203a5fd88c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 126 additions and 133 deletions

View file

@ -38,7 +38,8 @@ import "hydrogen-view-sdk/style.css";
async function main() { async function main() {
const app = document.querySelector<HTMLDivElement>('#app')! const app = document.querySelector<HTMLDivElement>('#app')!
const platform = new Platform(app, assetPaths, { development: import.meta.env.DEV }); const config = {};
const platform = new Platform(app, assetPaths, config, { development: import.meta.env.DEV });
const navigation = createNavigation(); const navigation = createNavigation();
platform.setNavigation(navigation); platform.setNavigation(navigation);
const urlRouter = createRouter({ const urlRouter = createRouter({

View file

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {Client} from "../matrix/Client.js";
import {SessionViewModel} from "./session/SessionViewModel.js"; import {SessionViewModel} from "./session/SessionViewModel.js";
import {SessionLoadViewModel} from "./SessionLoadViewModel.js"; import {SessionLoadViewModel} from "./SessionLoadViewModel.js";
import {LoginViewModel} from "./login/LoginViewModel.js"; import {LoginViewModel} from "./login/LoginViewModel.js";
@ -23,13 +24,12 @@ import {ViewModel} from "./ViewModel.js";
export class RootViewModel extends ViewModel { export class RootViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
this._createSessionContainer = options.createSessionContainer;
this._error = null; this._error = null;
this._sessionPickerViewModel = null; this._sessionPickerViewModel = null;
this._sessionLoadViewModel = null; this._sessionLoadViewModel = null;
this._loginViewModel = null; this._loginViewModel = null;
this._sessionViewModel = null; this._sessionViewModel = null;
this._pendingSessionContainer = null; this._pendingClient = null;
} }
async load() { async load() {
@ -53,16 +53,16 @@ export class RootViewModel extends ViewModel {
} }
} else if (sessionId) { } else if (sessionId) {
if (!this._sessionViewModel || this._sessionViewModel.id !== sessionId) { if (!this._sessionViewModel || this._sessionViewModel.id !== sessionId) {
// see _showLogin for where _pendingSessionContainer comes from // see _showLogin for where _pendingClient comes from
if (this._pendingSessionContainer && this._pendingSessionContainer.sessionId === sessionId) { if (this._pendingClient && this._pendingClient.sessionId === sessionId) {
const sessionContainer = this._pendingSessionContainer; const client = this._pendingClient;
this._pendingSessionContainer = null; this._pendingClient = null;
this._showSession(sessionContainer); this._showSession(client);
} else { } else {
// this should never happen, but we want to be sure not to leak it // this should never happen, but we want to be sure not to leak it
if (this._pendingSessionContainer) { if (this._pendingClient) {
this._pendingSessionContainer.dispose(); this._pendingClient.dispose();
this._pendingSessionContainer = null; this._pendingClient = null;
} }
this._showSessionLoader(sessionId); this._showSessionLoader(sessionId);
} }
@ -106,8 +106,7 @@ export class RootViewModel extends ViewModel {
this._setSection(() => { this._setSection(() => {
this._loginViewModel = new LoginViewModel(this.childOptions({ this._loginViewModel = new LoginViewModel(this.childOptions({
defaultHomeserver: this.platform.config["defaultHomeServer"], defaultHomeserver: this.platform.config["defaultHomeServer"],
createSessionContainer: this._createSessionContainer, ready: client => {
ready: sessionContainer => {
// we don't want to load the session container again, // we don't want to load the session container again,
// but we also want the change of screen to go through the navigation // but we also want the change of screen to go through the navigation
// so we store the session container in a temporary variable that will be // so we store the session container in a temporary variable that will be
@ -116,28 +115,28 @@ export class RootViewModel extends ViewModel {
// Also, we should not call _setSection before the navigation is in the correct state, // Also, we should not call _setSection before the navigation is in the correct state,
// as url creation (e.g. in RoomTileViewModel) // as url creation (e.g. in RoomTileViewModel)
// won't be using the correct navigation base path. // won't be using the correct navigation base path.
this._pendingSessionContainer = sessionContainer; this._pendingClient = client;
this.navigation.push("session", sessionContainer.sessionId); this.navigation.push("session", client.sessionId);
}, },
loginToken loginToken
})); }));
}); });
} }
_showSession(sessionContainer) { _showSession(client) {
this._setSection(() => { this._setSection(() => {
this._sessionViewModel = new SessionViewModel(this.childOptions({sessionContainer})); this._sessionViewModel = new SessionViewModel(this.childOptions({client}));
this._sessionViewModel.start(); this._sessionViewModel.start();
}); });
} }
_showSessionLoader(sessionId) { _showSessionLoader(sessionId) {
const sessionContainer = this._createSessionContainer(); const client = new Client(this.platform);
sessionContainer.startWithExistingSession(sessionId); client.startWithExistingSession(sessionId);
this._setSection(() => { this._setSection(() => {
this._sessionLoadViewModel = new SessionLoadViewModel(this.childOptions({ this._sessionLoadViewModel = new SessionLoadViewModel(this.childOptions({
sessionContainer, client,
ready: sessionContainer => this._showSession(sessionContainer) ready: client => this._showSession(client)
})); }));
this._sessionLoadViewModel.start(); this._sessionLoadViewModel.start();
}); });

View file

@ -15,15 +15,15 @@ limitations under the License.
*/ */
import {AccountSetupViewModel} from "./AccountSetupViewModel.js"; import {AccountSetupViewModel} from "./AccountSetupViewModel.js";
import {LoadStatus} from "../matrix/SessionContainer.js"; import {LoadStatus} from "../matrix/Client.js";
import {SyncStatus} from "../matrix/Sync.js"; import {SyncStatus} from "../matrix/Sync.js";
import {ViewModel} from "./ViewModel.js"; import {ViewModel} from "./ViewModel.js";
export class SessionLoadViewModel extends ViewModel { export class SessionLoadViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
const {sessionContainer, ready, homeserver, deleteSessionOnCancel} = options; const {client, ready, homeserver, deleteSessionOnCancel} = options;
this._sessionContainer = sessionContainer; this._client = client;
this._ready = ready; this._ready = ready;
this._homeserver = homeserver; this._homeserver = homeserver;
this._deleteSessionOnCancel = deleteSessionOnCancel; this._deleteSessionOnCancel = deleteSessionOnCancel;
@ -41,16 +41,16 @@ export class SessionLoadViewModel extends ViewModel {
try { try {
this._loading = true; this._loading = true;
this.emitChange("loading"); this.emitChange("loading");
this._waitHandle = this._sessionContainer.loadStatus.waitFor(s => { this._waitHandle = this._client.loadStatus.waitFor(s => {
if (s === LoadStatus.AccountSetup) { if (s === LoadStatus.AccountSetup) {
this._accountSetupViewModel = new AccountSetupViewModel(this._sessionContainer.accountSetup); this._accountSetupViewModel = new AccountSetupViewModel(this._client.accountSetup);
} else { } else {
this._accountSetupViewModel = undefined; this._accountSetupViewModel = undefined;
} }
this.emitChange("loadLabel"); this.emitChange("loadLabel");
// wait for initial sync, but not catchup sync // wait for initial sync, but not catchup sync
const isCatchupSync = s === LoadStatus.FirstSync && const isCatchupSync = s === LoadStatus.FirstSync &&
this._sessionContainer.sync.status.get() === SyncStatus.CatchupSync; this._client.sync.status.get() === SyncStatus.CatchupSync;
return isCatchupSync || return isCatchupSync ||
s === LoadStatus.LoginFailed || s === LoadStatus.LoginFailed ||
s === LoadStatus.Error || s === LoadStatus.Error ||
@ -67,15 +67,15 @@ export class SessionLoadViewModel extends ViewModel {
// much like we will once you are in the app. Probably a good idea // much like we will once you are in the app. Probably a good idea
// did it finish or get stuck at LoginFailed or Error? // did it finish or get stuck at LoginFailed or Error?
const loadStatus = this._sessionContainer.loadStatus.get(); const loadStatus = this._client.loadStatus.get();
const loadError = this._sessionContainer.loadError; const loadError = this._client.loadError;
if (loadStatus === LoadStatus.FirstSync || loadStatus === LoadStatus.Ready) { if (loadStatus === LoadStatus.FirstSync || loadStatus === LoadStatus.Ready) {
const sessionContainer = this._sessionContainer; const client = this._client;
// session container is ready, // session container is ready,
// don't dispose it anymore when // don't dispose it anymore when
// we get disposed // we get disposed
this._sessionContainer = null; this._client = null;
this._ready(sessionContainer); this._ready(client);
} }
if (loadError) { if (loadError) {
console.error("session load error", loadError); console.error("session load error", loadError);
@ -85,16 +85,16 @@ export class SessionLoadViewModel extends ViewModel {
console.error("error thrown during session load", err.stack); console.error("error thrown during session load", err.stack);
} finally { } finally {
this._loading = false; this._loading = false;
// loadLabel in case of sc.loadError also gets updated through this // loadLabel in case of client.loadError also gets updated through this
this.emitChange("loading"); this.emitChange("loading");
} }
} }
dispose() { dispose() {
if (this._sessionContainer) { if (this._client) {
this._sessionContainer.dispose(); this._client.dispose();
this._sessionContainer = null; this._client = null;
} }
if (this._waitHandle) { if (this._waitHandle) {
// rejects with AbortError // rejects with AbortError
@ -105,23 +105,23 @@ export class SessionLoadViewModel extends ViewModel {
// to show a spinner or not // to show a spinner or not
get loading() { get loading() {
const sc = this._sessionContainer; const client = this._client;
if (sc && sc.loadStatus.get() === LoadStatus.AccountSetup) { if (client && client.loadStatus.get() === LoadStatus.AccountSetup) {
return false; return false;
} }
return this._loading; return this._loading;
} }
get loadLabel() { get loadLabel() {
const sc = this._sessionContainer; const client = this._client;
const error = this._getError(); const error = this._getError();
if (error || (sc && sc.loadStatus.get() === LoadStatus.Error)) { if (error || (client && client.loadStatus.get() === LoadStatus.Error)) {
return `Something went wrong: ${error && error.message}.`; return `Something went wrong: ${error && error.message}.`;
} }
// Statuses related to login are handled by respective login view models // Statuses related to login are handled by respective login view models
if (sc) { if (client) {
switch (sc.loadStatus.get()) { switch (client.loadStatus.get()) {
case LoadStatus.QueryAccount: case LoadStatus.QueryAccount:
return `Querying account encryption setup…`; return `Querying account encryption setup…`;
case LoadStatus.AccountSetup: case LoadStatus.AccountSetup:
@ -133,7 +133,7 @@ export class SessionLoadViewModel extends ViewModel {
case LoadStatus.FirstSync: case LoadStatus.FirstSync:
return `Getting your conversations from the server…`; return `Getting your conversations from the server…`;
default: default:
return this._sessionContainer.loadStatus.get(); return this._client.loadStatus.get();
} }
} }
@ -141,7 +141,7 @@ export class SessionLoadViewModel extends ViewModel {
} }
_getError() { _getError() {
return this._error || this._sessionContainer?.loadError; return this._error || this._client?.loadError;
} }
get hasError() { get hasError() {
@ -154,7 +154,7 @@ export class SessionLoadViewModel extends ViewModel {
} }
async logout() { async logout() {
await this._sessionContainer.logout(); await this._client.logout();
this.navigation.push("session", true); this.navigation.push("session", true);
} }

View file

@ -15,18 +15,18 @@ limitations under the License.
*/ */
import {ViewModel} from "../ViewModel.js"; import {ViewModel} from "../ViewModel.js";
import {LoginFailure} from "../../matrix/SessionContainer.js"; import {LoginFailure} from "../../matrix/Client.js";
export class CompleteSSOLoginViewModel extends ViewModel { export class CompleteSSOLoginViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
const { const {
loginToken, loginToken,
sessionContainer, client,
attemptLogin, attemptLogin,
} = options; } = options;
this._loginToken = loginToken; this._loginToken = loginToken;
this._sessionContainer = sessionContainer; this._client = client;
this._attemptLogin = attemptLogin; this._attemptLogin = attemptLogin;
this._errorMessage = ""; this._errorMessage = "";
this.performSSOLoginCompletion(); this.performSSOLoginCompletion();
@ -46,7 +46,7 @@ export class CompleteSSOLoginViewModel extends ViewModel {
const homeserver = await this.platform.settingsStorage.getString("sso_ongoing_login_homeserver"); const homeserver = await this.platform.settingsStorage.getString("sso_ongoing_login_homeserver");
let loginOptions; let loginOptions;
try { try {
loginOptions = await this._sessionContainer.queryLogin(homeserver).result; loginOptions = await this._client.queryLogin(homeserver).result;
} }
catch (err) { catch (err) {
this._showError(err.message); this._showError(err.message);

View file

@ -14,21 +14,21 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {Client} from "../../matrix/Client.js";
import {ViewModel} from "../ViewModel.js"; import {ViewModel} from "../ViewModel.js";
import {PasswordLoginViewModel} from "./PasswordLoginViewModel.js"; import {PasswordLoginViewModel} from "./PasswordLoginViewModel.js";
import {StartSSOLoginViewModel} from "./StartSSOLoginViewModel.js"; import {StartSSOLoginViewModel} from "./StartSSOLoginViewModel.js";
import {CompleteSSOLoginViewModel} from "./CompleteSSOLoginViewModel.js"; import {CompleteSSOLoginViewModel} from "./CompleteSSOLoginViewModel.js";
import {LoadStatus} from "../../matrix/SessionContainer.js"; import {LoadStatus} from "../../matrix/Client.js";
import {SessionLoadViewModel} from "../SessionLoadViewModel.js"; import {SessionLoadViewModel} from "../SessionLoadViewModel.js";
export class LoginViewModel extends ViewModel { export class LoginViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
const {ready, defaultHomeserver, createSessionContainer, loginToken} = options; const {ready, defaultHomeserver, loginToken} = options;
this._createSessionContainer = createSessionContainer;
this._ready = ready; this._ready = ready;
this._loginToken = loginToken; this._loginToken = loginToken;
this._sessionContainer = this._createSessionContainer(); this._client = new Client(this.platform);
this._loginOptions = null; this._loginOptions = null;
this._passwordLoginViewModel = null; this._passwordLoginViewModel = null;
this._startSSOLoginViewModel = null; this._startSSOLoginViewModel = null;
@ -66,7 +66,7 @@ export class LoginViewModel extends ViewModel {
this._completeSSOLoginViewModel = this.track(new CompleteSSOLoginViewModel( this._completeSSOLoginViewModel = this.track(new CompleteSSOLoginViewModel(
this.childOptions( this.childOptions(
{ {
sessionContainer: this._sessionContainer, client: this._client,
attemptLogin: loginMethod => this.attemptLogin(loginMethod), attemptLogin: loginMethod => this.attemptLogin(loginMethod),
loginToken: this._loginToken loginToken: this._loginToken
}))); })));
@ -107,14 +107,14 @@ export class LoginViewModel extends ViewModel {
async attemptLogin(loginMethod) { async attemptLogin(loginMethod) {
this._setBusy(true); this._setBusy(true);
this._sessionContainer.startWithLogin(loginMethod, {inspectAccountSetup: true}); this._client.startWithLogin(loginMethod, {inspectAccountSetup: true});
const loadStatus = this._sessionContainer.loadStatus; const loadStatus = this._client.loadStatus;
const handle = loadStatus.waitFor(status => status !== LoadStatus.Login); const handle = loadStatus.waitFor(status => status !== LoadStatus.Login);
await handle.promise; await handle.promise;
this._setBusy(false); this._setBusy(false);
const status = loadStatus.get(); const status = loadStatus.get();
if (status === LoadStatus.LoginFailed) { if (status === LoadStatus.LoginFailed) {
return this._sessionContainer.loginFailure; return this._client.loginFailure;
} }
this._hideHomeserver = true; this._hideHomeserver = true;
this.emitChange("hideHomeserver"); this.emitChange("hideHomeserver");
@ -129,12 +129,12 @@ export class LoginViewModel extends ViewModel {
this._loadViewModel = this.track( this._loadViewModel = this.track(
new SessionLoadViewModel( new SessionLoadViewModel(
this.childOptions({ this.childOptions({
ready: (sessionContainer) => { ready: (client) => {
// make sure we don't delete the session in dispose when navigating away // make sure we don't delete the session in dispose when navigating away
this._sessionContainer = null; this._client = null;
this._ready(sessionContainer); this._ready(client);
}, },
sessionContainer: this._sessionContainer, client: this._client,
homeserver: this._homeserver homeserver: this._homeserver
}) })
) )
@ -200,7 +200,7 @@ export class LoginViewModel extends ViewModel {
// cancel ongoing query operation, if any // cancel ongoing query operation, if any
this._abortQueryOperation = this.disposeTracked(this._abortQueryOperation); this._abortQueryOperation = this.disposeTracked(this._abortQueryOperation);
try { try {
const queryOperation = this._sessionContainer.queryLogin(this._homeserver); const queryOperation = this._client.queryLogin(this._homeserver);
this._abortQueryOperation = this.track(() => queryOperation.abort()); this._abortQueryOperation = this.track(() => queryOperation.abort());
this.emitChange("isFetchingLoginOptions"); this.emitChange("isFetchingLoginOptions");
this._loginOptions = await queryOperation.result; this._loginOptions = await queryOperation.result;
@ -230,10 +230,10 @@ export class LoginViewModel extends ViewModel {
dispose() { dispose() {
super.dispose(); super.dispose();
if (this._sessionContainer) { if (this._client) {
// if we move away before we're done with initial sync // if we move away before we're done with initial sync
// delete the session // delete the session
this._sessionContainer.deleteSession(); this._client.deleteSession();
} }
} }
} }

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import {ViewModel} from "../ViewModel.js"; import {ViewModel} from "../ViewModel.js";
import {LoginFailure} from "../../matrix/SessionContainer.js"; import {LoginFailure} from "../../matrix/Client.js";
export class PasswordLoginViewModel extends ViewModel { export class PasswordLoginViewModel extends ViewModel {
constructor(options) { constructor(options) {

View file

@ -48,7 +48,7 @@ export class RoomViewModelObservable extends ObservableValue {
are called in that case. are called in that case.
*/ */
async initialize() { async initialize() {
const {session} = this._sessionViewModel._sessionContainer; const {session} = this._sessionViewModel._client;
const statusObservable = await session.observeRoomStatus(this.id); const statusObservable = await session.observeRoomStatus(this.id);
this.set(await this._statusToViewModel(statusObservable.get())); this.set(await this._statusToViewModel(statusObservable.get()));
this._statusSubscription = statusObservable.subscribe(async status => { this._statusSubscription = statusObservable.subscribe(async status => {

View file

@ -30,16 +30,16 @@ import {RightPanelViewModel} from "./rightpanel/RightPanelViewModel.js";
export class SessionViewModel extends ViewModel { export class SessionViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
const {sessionContainer} = options; const {client} = options;
this._sessionContainer = this.track(sessionContainer); this._client = this.track(client);
this._sessionStatusViewModel = this.track(new SessionStatusViewModel(this.childOptions({ this._sessionStatusViewModel = this.track(new SessionStatusViewModel(this.childOptions({
sync: sessionContainer.sync, sync: client.sync,
reconnector: sessionContainer.reconnector, reconnector: client.reconnector,
session: sessionContainer.session, session: client.session,
}))); })));
this._leftPanelViewModel = this.track(new LeftPanelViewModel(this.childOptions({ this._leftPanelViewModel = this.track(new LeftPanelViewModel(this.childOptions({
invites: this._sessionContainer.session.invites, invites: this._client.session.invites,
rooms: this._sessionContainer.session.rooms rooms: this._client.session.rooms
}))); })));
this._settingsViewModel = null; this._settingsViewModel = null;
this._roomViewModelObservable = null; this._roomViewModelObservable = null;
@ -88,7 +88,7 @@ export class SessionViewModel extends ViewModel {
} }
get id() { get id() {
return this._sessionContainer.sessionId; return this._client.sessionId;
} }
start() { start() {
@ -163,7 +163,7 @@ export class SessionViewModel extends ViewModel {
} }
_createRoomViewModel(roomId) { _createRoomViewModel(roomId) {
const room = this._sessionContainer.session.rooms.get(roomId); const room = this._client.session.rooms.get(roomId);
if (room) { if (room) {
const roomVM = new RoomViewModel(this.childOptions({room})); const roomVM = new RoomViewModel(this.childOptions({room}));
roomVM.load(); roomVM.load();
@ -175,12 +175,12 @@ export class SessionViewModel extends ViewModel {
_createUnknownRoomViewModel(roomIdOrAlias) { _createUnknownRoomViewModel(roomIdOrAlias) {
return new UnknownRoomViewModel(this.childOptions({ return new UnknownRoomViewModel(this.childOptions({
roomIdOrAlias, roomIdOrAlias,
session: this._sessionContainer.session, session: this._client.session,
})); }));
} }
async _createArchivedRoomViewModel(roomId) { async _createArchivedRoomViewModel(roomId) {
const room = await this._sessionContainer.session.loadArchivedRoom(roomId); const room = await this._client.session.loadArchivedRoom(roomId);
if (room) { if (room) {
const roomVM = new RoomViewModel(this.childOptions({room})); const roomVM = new RoomViewModel(this.childOptions({room}));
roomVM.load(); roomVM.load();
@ -190,11 +190,11 @@ export class SessionViewModel extends ViewModel {
} }
_createInviteViewModel(roomId) { _createInviteViewModel(roomId) {
const invite = this._sessionContainer.session.invites.get(roomId); const invite = this._client.session.invites.get(roomId);
if (invite) { if (invite) {
return new InviteViewModel(this.childOptions({ return new InviteViewModel(this.childOptions({
invite, invite,
mediaRepository: this._sessionContainer.session.mediaRepository, mediaRepository: this._client.session.mediaRepository,
})); }));
} }
return null; return null;
@ -230,7 +230,7 @@ export class SessionViewModel extends ViewModel {
} }
if (settingsOpen) { if (settingsOpen) {
this._settingsViewModel = this.track(new SettingsViewModel(this.childOptions({ this._settingsViewModel = this.track(new SettingsViewModel(this.childOptions({
sessionContainer: this._sessionContainer, client: this._client,
}))); })));
this._settingsViewModel.load(); this._settingsViewModel.load();
} }
@ -254,7 +254,7 @@ export class SessionViewModel extends ViewModel {
_roomFromNavigation() { _roomFromNavigation() {
const roomId = this.navigation.path.get("room")?.value; const roomId = this.navigation.path.get("room")?.value;
const room = this._sessionContainer.session.rooms.get(roomId); const room = this._client.session.rooms.get(roomId);
return room; return room;
} }

View file

@ -41,8 +41,8 @@ export class SettingsViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
this._updateService = options.updateService; this._updateService = options.updateService;
const {sessionContainer} = options; const {client} = options;
this._sessionContainer = sessionContainer; this._client = client;
this._sessionBackupViewModel = this.track(new SessionBackupViewModel(this.childOptions({session: this._session}))); this._sessionBackupViewModel = this.track(new SessionBackupViewModel(this.childOptions({session: this._session})));
this._closeUrl = this.urlCreator.urlUntilSegment("session"); this._closeUrl = this.urlCreator.urlUntilSegment("session");
this._estimate = null; this._estimate = null;
@ -54,12 +54,12 @@ export class SettingsViewModel extends ViewModel {
} }
get _session() { get _session() {
return this._sessionContainer.session; return this._client.session;
} }
async logout() { async logout() {
this._isLoggingOut = true; this._isLoggingOut = true;
await this._sessionContainer.logout(); await this._client.logout();
this.emitChange("isLoggingOut"); this.emitChange("isLoggingOut");
this.navigation.push("session", true); this.navigation.push("session", true);
} }

View file

@ -14,15 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// types need to bootstrap a SessionContainer
export {SessionContainer, LoadStatus} from "./matrix/SessionContainer.js";
export {Session} from "./matrix/Session.js";
export {Sync} from "./matrix/Sync.js";
export {Room} from "./matrix/room/Room.js";
export {Timeline} from "./matrix/room/timeline/Timeline.js";
export {createNavigation, createRouter} from "./domain/navigation/index.js";
export {Platform} from "./platform/web/Platform.js"; export {Platform} from "./platform/web/Platform.js";
export {Client, LoadStatus} from "./matrix/Client.js";
// export main view & view models // export main view & view models
export {createNavigation, createRouter} from "./domain/navigation/index.js";
export {RootViewModel} from "./domain/RootViewModel.js"; export {RootViewModel} from "./domain/RootViewModel.js";
export {RootView} from "./platform/web/ui/RootView.js"; export {RootView} from "./platform/web/ui/RootView.js";
export {SessionViewModel} from "./domain/session/SessionViewModel.js"; export {SessionViewModel} from "./domain/session/SessionViewModel.js";

View file

@ -51,8 +51,8 @@ export const LoginFailure = createEnum(
"Unknown", "Unknown",
); );
export class SessionContainer { export class Client {
constructor({platform, olmPromise, workerPromise}) { constructor(platform) {
this._platform = platform; this._platform = platform;
this._sessionStartedByReconnector = false; this._sessionStartedByReconnector = false;
this._status = new ObservableValue(LoadStatus.NotLoading); this._status = new ObservableValue(LoadStatus.NotLoading);
@ -64,8 +64,8 @@ export class SessionContainer {
this._sessionId = null; this._sessionId = null;
this._storage = null; this._storage = null;
this._requestScheduler = null; this._requestScheduler = null;
this._olmPromise = olmPromise; this._olmPromise = platform.loadOlm();
this._workerPromise = workerPromise; this._workerPromise = platform.loadOlmWorker();
this._accountSetup = undefined; this._accountSetup = undefined;
} }

View file

@ -109,7 +109,7 @@ export class Session {
return this._sessionInfo.userId; return this._sessionInfo.userId;
} }
/** @internal call SessionContainer.logout instead */ /** @internal call Client.logout instead */
async logout(log = undefined) { async logout(log = undefined) {
await this._hsApi.logout({log}).response(); await this._hsApi.logout({log}).response();
} }

View file

@ -19,6 +19,6 @@ import {hkdf} from "../../utils/crypto/hkdf";
import {Platform as ModernPlatform} from "./Platform.js"; import {Platform as ModernPlatform} from "./Platform.js";
export function Platform(container, paths) { export function Platform(container, assetPaths, config, options = null) {
return new ModernPlatform(container, paths, {aesjs, hkdf}); return new ModernPlatform(container, assetPaths, config, options, {aesjs, hkdf});
} }

View file

@ -76,12 +76,12 @@ function assetAbsPath(assetPath) {
return assetPath; return assetPath;
} }
async function loadOlmWorker(config) { async function loadOlmWorker(assetPaths) {
const workerPool = new WorkerPool(config.worker, 4); const workerPool = new WorkerPool(assetPaths.worker, 4);
await workerPool.init(); await workerPool.init();
await workerPool.sendAll({ await workerPool.sendAll({
type: "load_olm", type: "load_olm",
path: assetAbsPath(config.olm.legacyBundle) path: assetAbsPath(assetPaths.olm.legacyBundle)
}); });
const olmWorker = new OlmWorker(workerPool); const olmWorker = new OlmWorker(workerPool);
return olmWorker; return olmWorker;
@ -126,9 +126,10 @@ function adaptUIOnVisualViewportResize(container) {
} }
export class Platform { export class Platform {
constructor(container, config, cryptoExtras = null, options = null) { constructor(container, assetPaths, config, options = null, cryptoExtras = null) {
this._config = config;
this._container = container; this._container = container;
this._assetPaths = assetPaths;
this._config = config;
this.settingsStorage = new SettingsStorage("hydrogen_setting_v1_"); this.settingsStorage = new SettingsStorage("hydrogen_setting_v1_");
this.clock = new Clock(); this.clock = new Clock();
this.encoding = new Encoding(); this.encoding = new Encoding();
@ -137,9 +138,9 @@ export class Platform {
this.history = new History(); this.history = new History();
this.onlineStatus = new OnlineStatus(); this.onlineStatus = new OnlineStatus();
this._serviceWorkerHandler = null; this._serviceWorkerHandler = null;
if (config.serviceWorker && "serviceWorker" in navigator) { if (assetPaths.serviceWorker && "serviceWorker" in navigator) {
this._serviceWorkerHandler = new ServiceWorkerHandler(); this._serviceWorkerHandler = new ServiceWorkerHandler();
this._serviceWorkerHandler.registerAndStart(config.serviceWorker); this._serviceWorkerHandler.registerAndStart(assetPaths.serviceWorker);
} }
this.notificationService = new NotificationService(this._serviceWorkerHandler, config.push); this.notificationService = new NotificationService(this._serviceWorkerHandler, config.push);
this.crypto = new Crypto(cryptoExtras); this.crypto = new Crypto(cryptoExtras);
@ -157,6 +158,8 @@ export class Platform {
const isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) && !window.MSStream; const isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) && !window.MSStream;
this.isIOS = isIOS; this.isIOS = isIOS;
this._disposables = new Disposables(); this._disposables = new Disposables();
this._olmPromise = undefined;
this._workerPromise = undefined;
} }
_createLogger(isDevelopment) { _createLogger(isDevelopment) {
@ -179,7 +182,10 @@ export class Platform {
} }
loadOlm() { loadOlm() {
return loadOlm(this._config.olm); if (!this._olmPromise) {
this._olmPromise = loadOlm(this._assetPaths.olm);
}
return this._olmPromise;
} }
get config() { get config() {
@ -188,7 +194,10 @@ export class Platform {
async loadOlmWorker() { async loadOlmWorker() {
if (!window.WebAssembly) { if (!window.WebAssembly) {
return await loadOlmWorker(this._config); if (!this._workerPromise) {
this._workerPromise = loadOlmWorker(this._assetPaths);
}
return this._workerPromise;
} }
} }
@ -222,7 +231,7 @@ export class Platform {
if (navigator.msSaveBlob) { if (navigator.msSaveBlob) {
navigator.msSaveBlob(blobHandle.nativeBlob, filename); navigator.msSaveBlob(blobHandle.nativeBlob, filename);
} else { } else {
downloadInIframe(this._container, this._config.downloadSandbox, blobHandle, filename, this.isIOS); downloadInIframe(this._container, this._assetPaths.downloadSandbox, blobHandle, filename, this.isIOS);
} }
} }

View file

@ -19,20 +19,14 @@
import {main} from "./main"; import {main} from "./main";
import {Platform} from "./Platform"; import {Platform} from "./Platform";
import configJSON from "./assets/config.json?raw"; import configJSON from "./assets/config.json?raw";
import {olmPaths, downloadSandboxPath, workerPath} from "./sdk/paths/vite"; import assetPaths from "./sdk/paths/vite";
const paths = {
olm: olmPaths,
downloadSandbox: downloadSandboxPath,
worker: workerPath,
...JSON.parse(configJSON)
};
if (import.meta.env.PROD) { if (import.meta.env.PROD) {
paths.serviceWorker = "sw.js"; assetPaths.serviceWorker = "sw.js";
} }
const platform = new Platform( const platform = new Platform(
document.body, document.body,
paths, assetPaths,
null, JSON.parse(configJSON),
{development: import.meta.env.DEV} {development: import.meta.env.DEV}
); );
main(platform); main(platform);

View file

@ -16,7 +16,7 @@ limitations under the License.
*/ */
// import {RecordRequester, ReplayRequester} from "./matrix/net/request/replay"; // import {RecordRequester, ReplayRequester} from "./matrix/net/request/replay";
import {SessionContainer} from "../../matrix/SessionContainer.js"; import {Client} from "../../matrix/Client.js";
import {RootViewModel} from "../../domain/RootViewModel.js"; import {RootViewModel} from "../../domain/RootViewModel.js";
import {createNavigation, createRouter} from "../../domain/navigation/index.js"; import {createNavigation, createRouter} from "../../domain/navigation/index.js";
// Don't use a default export here, as we use multiple entries during legacy build, // Don't use a default export here, as we use multiple entries during legacy build,
@ -37,13 +37,7 @@ export async function main(platform) {
platform.setNavigation(navigation); platform.setNavigation(navigation);
const urlRouter = createRouter({navigation, history: platform.history}); const urlRouter = createRouter({navigation, history: platform.history});
urlRouter.attach(); urlRouter.attach();
const olmPromise = platform.loadOlm();
const workerPromise = platform.loadOlmWorker();
const vm = new RootViewModel({ const vm = new RootViewModel({
createSessionContainer: () => {
return new SessionContainer({platform, olmPromise, workerPromise});
},
platform, platform,
// the only public interface of the router is to create urls, // the only public interface of the router is to create urls,
// so we call it that in the view models // so we call it that in the view models

View file

@ -9,11 +9,12 @@ import olmJsPath from "@matrix-org/olm/olm.js?url";
// @ts-ignore // @ts-ignore
import olmLegacyJsPath from "@matrix-org/olm/olm_legacy.js?url"; import olmLegacyJsPath from "@matrix-org/olm/olm_legacy.js?url";
export const olmPaths = { export default {
downloadSandbox: _downloadSandboxPath,
worker: _workerPath,
olm: {
wasm: olmWasmPath, wasm: olmWasmPath,
legacyBundle: olmLegacyJsPath, legacyBundle: olmLegacyJsPath,
wasmBundle: olmJsPath, wasmBundle: olmJsPath,
}
}; };
export const downloadSandboxPath = _downloadSandboxPath;
export const workerPath = _workerPath;