forked from mystiq/hydrogen-web
Merge pull request #622 from vector-im/bwindels/sdk-refactoring
Some API cleanup ahead of first SDK release
This commit is contained in:
commit
203a5fd88c
17 changed files with 126 additions and 133 deletions
|
@ -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({
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
Loading…
Reference in a new issue