-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
221 additions
and
34,552 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
A simple example using SyncedStore and Matrix-CRDT. | ||
A simple TODO app example using SyncedStore and Matrix-CRDT. | ||
TODOs are saved in a Matrix Room. | ||
|
||
- [Open live demo](https://lvclo.csb.app/) (via CodeSandbox) | ||
- [Edit code](https://codesandbox.io/s/matrix-crdt-todo-simple-example-lvclo?file=/src/App.tsx) (CodeSandbox) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { Box, ChoiceInputField, Label, Radio } from "@primer/react"; | ||
import { MatrixProvider } from "matrix-crdt"; | ||
import { MatrixClient } from "matrix-js-sdk"; | ||
import React, { useState } from "react"; | ||
import { LoginButton } from "./login/LoginButton"; | ||
import * as Y from "yjs"; | ||
|
||
/** | ||
* The Top Bar of the app that contains the sign in button and status of the MatrixProvider (connection to the Matrix Room) | ||
*/ | ||
export default function MatrixStatusBar({ doc }: { doc: Y.Doc }) { | ||
const [isOpen, setIsOpen] = useState(false); | ||
const [matrixProvider, setMatrixProvider] = useState<MatrixProvider>(); | ||
const [status, setStatus] = useState< | ||
"loading" | "failed" | "ok" | "disconnected" | ||
>(); | ||
|
||
const [matrixClient, setMatrixClient] = useState<MatrixClient>(); | ||
const [roomAlias, setRoomAlias] = useState<string>(); | ||
|
||
const connect = React.useCallback( | ||
(matrixClient: MatrixClient, roomAlias: string) => { | ||
if (!matrixClient || !roomAlias) { | ||
throw new Error("can't connect without matrixClient or roomAlias"); | ||
} | ||
|
||
// This is the main code that sets up the connection between | ||
// yjs and Matrix. It creates a new MatrixProvider and | ||
// registers it to the `doc`. | ||
const newMatrixProvider = new MatrixProvider( | ||
doc, | ||
matrixClient, | ||
{ type: "alias", alias: roomAlias }, | ||
undefined, | ||
{ | ||
translator: { updatesAsRegularMessages: true }, | ||
reader: { snapshotInterval: 10 }, | ||
writer: { flushInterval: 500 }, | ||
} | ||
); | ||
setStatus("loading"); | ||
newMatrixProvider.initialize(); | ||
setMatrixProvider(newMatrixProvider); | ||
|
||
// (optional): capture events from MatrixProvider to reflect the status in the UI | ||
newMatrixProvider.onDocumentAvailable((e) => { | ||
setStatus("ok"); | ||
}); | ||
|
||
newMatrixProvider.onCanWriteChanged((e) => { | ||
if (!newMatrixProvider.canWrite) { | ||
setStatus("failed"); | ||
} else { | ||
setStatus("ok"); | ||
} | ||
}); | ||
|
||
newMatrixProvider.onDocumentUnavailable((e) => { | ||
setStatus("failed"); | ||
}); | ||
}, | ||
[doc] | ||
); | ||
|
||
const onLogin = React.useCallback( | ||
(matrixClient: MatrixClient, roomAlias: string) => { | ||
if (matrixProvider) { | ||
matrixProvider.dispose(); | ||
setStatus("disconnected"); | ||
setMatrixProvider(undefined); | ||
} | ||
|
||
// (optional) stored on state for easy disconnect + connect toggle | ||
setMatrixClient(matrixClient); | ||
setRoomAlias(roomAlias); | ||
|
||
// actually connect | ||
connect(matrixClient, roomAlias); | ||
}, | ||
[matrixProvider, connect] | ||
); | ||
|
||
const onConnectChange = React.useCallback( | ||
(e: React.ChangeEvent<HTMLInputElement>) => { | ||
if (!matrixClient || !roomAlias) { | ||
throw new Error("matrixClient and roomAlias should be set"); | ||
} | ||
|
||
if (matrixProvider) { | ||
matrixProvider.dispose(); | ||
setStatus("disconnected"); | ||
setMatrixProvider(undefined); | ||
} | ||
|
||
if (e.target.value === "true") { | ||
connect(matrixClient, roomAlias); | ||
} | ||
}, | ||
[connect, matrixClient, roomAlias, matrixProvider] | ||
); | ||
|
||
return ( | ||
<Box textAlign={"right"}> | ||
{/* TODO: add options to go offline / webrtc, snapshots etc */} | ||
{status === undefined && ( | ||
<LoginButton onLogin={onLogin} isOpen={isOpen} setIsOpen={setIsOpen} /> | ||
)} | ||
{matrixClient && ( | ||
<fieldset style={{ margin: 0, padding: 0, border: 0 }}> | ||
<ChoiceInputField> | ||
<ChoiceInputField.Label>Online</ChoiceInputField.Label> | ||
<Radio | ||
name="online" | ||
value="true" | ||
defaultChecked={true} | ||
onChange={onConnectChange} | ||
/> | ||
</ChoiceInputField> | ||
<ChoiceInputField> | ||
<ChoiceInputField.Label> | ||
Offline (disable sync) | ||
</ChoiceInputField.Label> | ||
<Radio | ||
name="online" | ||
value="false" | ||
defaultChecked={false} | ||
onChange={onConnectChange} | ||
/> | ||
</ChoiceInputField> | ||
</fieldset> | ||
)} | ||
{status === "loading" && ( | ||
<Label variant="small" outline> | ||
Connecting with Matrix room… | ||
</Label> | ||
)} | ||
{status === "disconnected" && ( | ||
<Label variant="small" outline> | ||
Disconnected | ||
</Label> | ||
)} | ||
{status === "ok" && ( | ||
<Label | ||
variant="small" | ||
outline | ||
sx={{ borderColor: "success.emphasis", color: "success.fg" }}> | ||
Connected with Matrix room | ||
</Label> | ||
)} | ||
{status === "failed" && ( | ||
<Label | ||
variant="small" | ||
outline | ||
sx={{ borderColor: "danger.emphasis", color: "danger.fg" }}> | ||
Failed. Make sure the user has access to the Matrix room | ||
</Label> | ||
)} | ||
</Box> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,8 @@ | ||
import { getYjsValue, syncedStore } from "@syncedstore/core"; | ||
import { MatrixProvider } from "matrix-crdt"; | ||
import sdk from "matrix-js-sdk"; | ||
import { syncedStore } from "@syncedstore/core"; | ||
|
||
export type Todo = { | ||
title: string; | ||
completed: boolean; | ||
}; | ||
|
||
export const globalStore = syncedStore({ todos: [] as Todo[] }); | ||
// new MatrixProvider(getYjsValue(globalStore) as any, client, {} | ||
let matrixProvider: MatrixProvider | undefined; | ||
export function setMatrixCredentials( | ||
server: string, | ||
userId: string, | ||
token: string, | ||
room: string | ||
) { | ||
if (matrixProvider) { | ||
matrixProvider.dispose(); | ||
} | ||
const matrixClient = sdk.createClient({ | ||
baseUrl: server, | ||
accessToken: token, | ||
userId, | ||
}); | ||
// overwrites because we don't call .start(); | ||
(matrixClient as any).canSupportVoip = false; | ||
(matrixClient as any).clientOpts = { | ||
lazyLoadMembers: true, | ||
}; | ||
|
||
matrixClient.loginWithToken(token); | ||
|
||
matrixProvider = new MatrixProvider( | ||
getYjsValue(globalStore) as any, | ||
matrixClient, | ||
{ type: "alias", alias: room }, | ||
undefined, | ||
{ | ||
translator: { updatesAsRegularMessages: true }, | ||
reader: { snapshotInterval: 10 }, | ||
writer: { flushInterval: 500 }, | ||
} | ||
); | ||
matrixProvider.initialize(); | ||
} |
Oops, something went wrong.