diff --git a/doc/FAQ.md b/doc/FAQ.md index c6445c72..4784062d 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -32,10 +32,4 @@ There are no published builds at this point. You need to checkout the version yo ## I want to embed Hydrogen in my website, how should I do that? -There are no npm modules yet published for Hydrogen. The easiest is probably to setup your website project, do yarn/npm init if you haven't yet, then add the hydrogen repo as a git http dependency, and import the files/classes you want to use from Hydrogen. - -For example, for a single room chat, you could create an instance of `Platform`, you create a new `SessionContainer` with it, call `startWithLogin` on it, observe `sessionContainer.loadStatus` to know when initial sync is done, then do `sessionContainer.session.rooms.get('roomid')` and you create a `RoomViewModel` with it and pass that to a `RoomView`. Then you call `document.appendChild(roomView.mount())` and you should see a syncing room. - -Feel free to ask for pointers in #hydrogen:matrix.org as the documentation is still lacking considerably. Note that at this early, pre 1.0 stage of the project, there is no promise of API stability yet. - -Also, to make end-to-end encryption work, you'll likely need some tweaks to your build system, see [this issue](https://github.com/vector-im/hydrogen-web/issues/467). +Hydrogen aims to be usable as an SDK, and while it is still early days, you can find some documentation how to do that in [SDK.md](SDK.md). diff --git a/doc/SDK.md b/doc/SDK.md new file mode 100644 index 00000000..7d34a49b --- /dev/null +++ b/doc/SDK.md @@ -0,0 +1,77 @@ +# How to use Hydrogen as an SDK + +If you want to use end-to-end encryption, it is recommended to use a [supported build system](../src/sdk/paths/) (currently only vite) to be able to locate the olm library files. + +You can create a project using the following commands + +```sh +# you can pick "vanilla-ts" here for project type if you're not using react or vue +yarn create vite +cd +yarn +yarn add https://github.com/vector-im/hydrogen-web.git +``` + +If you go into the `src` directory, you should see a `main.ts` file. If you put this code in there, you should see a basic timeline after login and initial sync have finished. + +```ts +import { + Platform, + SessionContainer, + LoadStatus, + createNavigation, + createRouter, + RoomViewModel, + TimelineView +} from "hydrogen-web"; +import {olmPaths, downloadSandboxPath} from "hydrogen-web/src/sdk/paths/vite"; + +const app = document.querySelector('#app')! + +// bootstrap a session container +const platform = new Platform(app, { + downloadSandbox: downloadSandboxPath, + olm: olmPaths, +}, null, { development: true }); +const navigation = createNavigation(); +platform.setNavigation(navigation); +const urlRouter = createRouter({ + navigation: navigation, + history: platform.history +}); +urlRouter.attach(); +const sessionContainer = new SessionContainer({ + platform, + olmPromise: platform.loadOlm(), + workerPromise: platform.loadOlmWorker() +}); + +// wait for login and first sync to finish +const loginOptions = await sessionContainer.queryLogin("matrix.org").result; +sessionContainer.startWithLogin(loginOptions.password("bruno4", "testtest")); +await sessionContainer.loadStatus.waitFor((status: string) => { + return status === LoadStatus.Ready || + status === LoadStatus.Error || + status === LoadStatus.LoginFailed; +}).promise; +// check the result +if (sessionContainer.loginFailure) { + alert("login failed: " + sessionContainer.loginFailure); +} else if (sessionContainer.loadError) { + alert("load failed: " + sessionContainer.loadError.message); +} else { + // we're logged in, we can access the room now + const {session} = sessionContainer; + const room = session.rooms.get("!bEWtlqtDwCLFIAKAcv:matrix.org"); + const vm = new RoomViewModel({ + room, + ownUserId: session.userId, + platform, + urlCreator: urlRouter, + navigation, + }); + await vm.load(); + const view = new TimelineView(vm.timelineViewModel); + app.appendChild(view.mount()); +} +``` diff --git a/documentation/UI/index.md b/doc/UI/index.md similarity index 100% rename from documentation/UI/index.md rename to doc/UI/index.md diff --git a/documentation/UI/render-dom-elements.md b/doc/UI/render-dom-elements.md similarity index 100% rename from documentation/UI/render-dom-elements.md rename to doc/UI/render-dom-elements.md diff --git a/package.json b/package.json index 52eb38af..65e72801 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "hydrogen-web", "version": "0.2.16", "description": "A javascript matrix client prototype, trying to minize RAM usage by offloading as much as possible to IndexedDB", - "main": "index.js", + "main": "src/lib.ts", "directories": { "doc": "doc" }, diff --git a/src/lib.ts b/src/lib.ts new file mode 100644 index 00000000..27290fb5 --- /dev/null +++ b/src/lib.ts @@ -0,0 +1,33 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +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 main view & view models +export {RootViewModel} from "./domain/RootViewModel.js"; +export {RootView} from "./platform/web/ui/RootView.js"; +export {SessionViewModel} from "./domain/session/SessionViewModel.js"; +export {SessionView} from "./platform/web/ui/session/SessionView.js"; +export {RoomViewModel} from "./domain/session/room/RoomViewModel.js"; +export {RoomView} from "./platform/web/ui/session/room/RoomView.js"; +export {TimelineViewModel} from "./domain/session/room/timeline/TimelineViewModel.js"; +export {TimelineView} from "./platform/web/ui/session/room/TimelineView"; diff --git a/src/sdk.ts b/src/sdk.ts deleted file mode 100644 index 0754b2ab..00000000 --- a/src/sdk.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2020 Bruno Windels -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// import {RecordRequester, ReplayRequester} from "./matrix/net/request/replay.js"; -import {SessionContainer} from "./matrix/SessionContainer.js"; -import {RootViewModel} from "./domain/RootViewModel.js"; -import {createNavigation, createRouter} from "./domain/navigation/index.js"; -// Don't use a default export here, as we use multiple entries during legacy build, -// which does not support default exports, -// see https://github.com/rollup/plugins/tree/master/packages/multi-entry -export async function main(platform) { - try { - // to replay: - // const fetchLog = await (await fetch("/fetchlogs/constrainterror.json")).json(); - // const replay = new ReplayRequester(fetchLog, {delay: false}); - // const request = replay.request; - - // to record: - // const recorder = new RecordRequester(createFetchRequest(clock.createTimeout)); - // const request = recorder.request; - // window.getBrawlFetchLog = () => recorder.log(); - const navigation = createNavigation(); - platform.setNavigation(navigation); - const urlRouter = createRouter({navigation, history: platform.history}); - urlRouter.attach(); - const olmPromise = platform.loadOlm(); - const workerPromise = platform.loadOlmWorker(); - - const vm = new RootViewModel({ - createSessionContainer: () => { - return new SessionContainer({platform, olmPromise, workerPromise}); - }, - platform, - // the only public interface of the router is to create urls, - // so we call it that in the view models - urlCreator: urlRouter, - navigation, - }); - await vm.load(); - platform.createAndMountRootView(vm); - } catch(err) { - console.error(`${err.message}:\n${err.stack}`); - } -} diff --git a/src/sdk/paths/vite.ts b/src/sdk/paths/vite.ts new file mode 100644 index 00000000..e38648eb --- /dev/null +++ b/src/sdk/paths/vite.ts @@ -0,0 +1,12 @@ +import _downloadSandboxPath from "../../../assets/download-sandbox.html?url"; +import olmWasmPath from "../../../lib/olm/olm.wasm?url"; +import olmJsPath from "../../../lib/olm/olm.js?url"; +import olmLegacyJsPath from "../../../lib/olm/olm_legacy.js?url"; + +export const olmPaths = { + wasm: olmWasmPath, + legacyBundle: olmLegacyJsPath, + wasmBundle: olmJsPath, +}; + +export const downloadSandboxPath = _downloadSandboxPath; diff --git a/tsconfig.json b/tsconfig.json index 0179e267..e09e7cc5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,5 +4,8 @@ "noEmit": true, "target": "ES2020" }, + "exclude": [ + "src/sdk/paths/*" + ], "include": ["src/**/*"], }