Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adopt bucket priorities in yjs demo #512

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 8 additions & 35 deletions demos/yjs-react-supabase-text-collab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,9 @@ This demo app uses Supabase as its Postgres database and backend:

1. [Create a new project on the Supabase dashboard](https://supabase.com/dashboard/projects).
2. Go to the Supabase SQL Editor for your new project and execute the SQL statements in [`database.sql`](database.sql) to create the database schema, database functions, and publication needed for PowerSync.
3. Enable "anonymous sign-ins" for the project [here](https://supabase.com/dashboard/project/_/auth/providers).

### 3. Auth setup and Supabase edge functions

For ease of demoing, this demo app uses anonymous authentication. The below instructions are derived from the [powersync-jwks-example README](https://github.com/powersync-ja/powersync-jwks-example):

1. Install [Deno](https://deno.com/) and [Supabase CLI](https://supabase.com/docs/guides/cli/getting-started) if you don't have them already.
2. Clone the [powersync-jwks-example](https://github.com/powersync-ja/powersync-jwks-example) repo.
3. In the [powersync-jwks-example](https://github.com/powersync-ja/powersync-jwks-example) repo directory, run the script to generate a keypair:

```bash
deno run generate-keys.ts
```

4. Then use `supabase secrets set` as shown in the terminal output to update the generated keys on Supabase (`POWERSYNC_PUBLIC_KEY` and `POWERSYNC_PRIVATE_KEY`). You will need the project ref of the Supabase project you created previously.
5. Switch back to the `powersync-supabase-yjs-text-collab-demo` repo directory and deploy the `powersync-jwks` and `powersync-auth-anonymous` edge functions to Supabase: (Note that the Supabase CLI requires [Docker Desktop](https://docs.docker.com/desktop/) to be installed for this step)

```bash
supabase functions deploy --no-verify-jwt powersync-jwks
supabase functions deploy powersync-auth-anonymous
```

### 4. Create new project on PowerSync and connect to Supabase/Postgres
### 3. Create new project on PowerSync and connect to Supabase/Postgres

If you don't have a PowerSync account yet, [sign up here](https://accounts.journeyapps.com/portal/free-trial?powersync=true).

Expand All @@ -57,24 +38,16 @@ Then, in the [PowerSync dashboard](https://powersync.journeyapps.com/), create a
5. Input the credentials from the project you created in Supabase. In the Supabase dashboard, under your project you can go to "Project Settings" and then "Database" and choose "URI" under "Connection string", untick the "Use connection pooling" option, and then copy & paste the connection string into the PowerSync dashboard "URI" field, and then enter your database password at the "Password" field.
6. Click the "Test connection" button and you should see "Connection success!"
7. Click on the "Credentials" tab of the "Edit Instance" dialog.
8. Tick the "Use Supabase Auth" checkbox
9. Enter the URL of your `powersync-jwks` edge function at the "JWKS URI" field. It should be in the format `https://<supabase-project-ref>.supabase.co/functions/v1/powersync-jwks`. If needed, you can find the URL in the Supabase dashboard by going to your project and then to "Edge Functions".
10. Click "Save" to save all the changes to your PowerSync instance. The instance will now be deployed — this may take a minute or two.

### 5. Set `POWERSYNC_URL` secret on Supabase

1. Now that your PowerSync instance is created, you should see a URL for it on the right side of the PowerSync dashboard in the "Deploy logs" panel. Click the button next to the URL to copy it.
2. Use the copied value to set the `POWERSYNC_URL` secret to be used by your Supabase edge functions: (make sure there is no trailing slash at the end of the URL)

`supabase secrets set POWERSYNC_URL=https://<powersync-instance-id>.powersync.journeyapps.com`
8. Tick the "Use Supabase Auth" checkbox and configure the JWT secret.
9. Click "Save" to save all the changes to your PowerSync instance. The instance will now be deployed — this may take a minute or two.

### 6. Create Sync Rules on PowerSync
### 4. Create Sync Rules on PowerSync

1. Open the [`sync-rules.yaml`](sync-rules.yaml) in this repo and copy the contents.
2. In the [PowerSync dashboard](https://powersync.journeyapps.com/), paste that into the 'sync-rules.yaml' editor panel.
3. Click the "Deploy sync rules" button and select your PowerSync instance from the drop-down list.

### 7. Set up local environment variables
### 5. Set up local environment variables

To set up the environment variables for the demo app:

Expand All @@ -87,9 +60,9 @@ cp .env.local.template .env.local
2. Edit `.env.local` and populate the relevant values:
- Set `VITE_SUPABASE_URL` to your Supabase project URL. You can find this by going to the main page for the project on the Supabase dashboard and then look for "Project URL" in the "Project API" panel.
- Set `VITE_SUPABASE_ANON_KEY` to your Supabase API key. This can be found right below the Project URL on the Supabase dashboard.
- Set `VITE_POWERSYNC_URL` to your PowerSync instance URL (this is the same URL from step 5)
- Set `VITE_POWERSYNC_URL` to your PowerSync instance URL (this is the same URL from step 3)

### 8. Run the demo app
### 6. Run the demo app

In this directory, run the following to start the development server:

Expand Down
17 changes: 0 additions & 17 deletions demos/yjs-react-supabase-text-collab/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,9 @@
"start": "pnpm build && pnpm preview"
},
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^5.0.12",
"@powersync/react": "workspace:*",
"@powersync/web": "workspace:*",
"@journeyapps/wa-sqlite": "^1.2.0",
"@lexical/react": "^0.11.3",
"@mui/icons-material": "^5.15.12",
"@mui/material": "^5.15.12",
"@mui/x-data-grid": "^6.19.6",
"@supabase/supabase-js": "^2.39.8",
Expand All @@ -27,31 +22,19 @@
"@tiptap/extension-task-list": "2.2.2",
"@tiptap/react": "2.2.2",
"@tiptap/starter-kit": "2.2.2",
"d3": "^7.8.5",
"fast-glob": "^3.3.2",
"formik": "^2.4.5",
"highlight.js": "^11.9.0",
"js-logger": "^1.6.1",
"lato-font": "^3.0.0",
"lexical": "^0.11.3",
"lib0": "^0.2.91",
"lodash": "^4.17.21",
"lowlight": "^2.9.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.22.3",
"react-router-dom": "^6.22.3",
"remixicon": "^2.5.0",
"shiki": "^0.10.1",
"simplify-js": "^1.2.4",
"uuid": "^9.0.1",
"y-prosemirror": "1.0.20",
"y-protocols": "1.0.6",
"yjs": "^13.6.14"
},
"devDependencies": {
"@swc/core": "~1.6.0",
"@types/lodash": "^4.17.0",
"@types/node": "^20.11.26",
"@types/react": "^18.2.65",
"@types/react-dom": "^18.2.21",
Expand Down
12 changes: 11 additions & 1 deletion demos/yjs-react-supabase-text-collab/src/app/editor/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { usePowerSync, useQuery, useStatus } from '@powersync/react';
import { Box, Container, Typography } from '@mui/material';
import { Box, Container, FormControlLabel, Switch, Typography } from '@mui/material';
import { useEffect, useMemo, useState } from 'react';
import MenuBar from '@/components/widgets/MenuBar';
import { PowerSyncYjsProvider } from '@/library/powersync/PowerSyncYjsProvider';
Expand All @@ -12,6 +12,7 @@ import StarterKit from '@tiptap/starter-kit';
import * as Y from 'yjs';
import './tiptap-styles.scss';
import { useParams } from 'react-router-dom';
import { connector } from '@/components/providers/SystemProvider';

export default function EditorPage() {
const powerSync = usePowerSync();
Expand All @@ -24,6 +25,7 @@ export default function EditorPage() {
}

const [totalDocUpdates, setTotalDocUpdates] = useState(0);
const [allowUploads, setAllowUploads] = useState(connector.enableUploads);

const ydoc = useMemo(() => {
return new Y.Doc();
Expand All @@ -36,6 +38,10 @@ export default function EditorPage() {
};
}, [ydoc, powerSync]);

useEffect(() => {
connector.enableUploads = allowUploads;
}, [allowUploads]);

// watch for total number of document updates changing to update the counter
const { data: docUpdatesCount } = useQuery(
'SELECT COUNT(*) as total_updates FROM document_updates WHERE document_id=?',
Expand Down Expand Up @@ -89,6 +95,10 @@ export default function EditorPage() {
<Typography variant="caption" display="block" gutterBottom>
{totalDocUpdates} total edit(s) in this document.
</Typography>
<FormControlLabel
control={<Switch checked={allowUploads} onChange={(e) => setAllowUploads(e.target.checked)} />}
label="Enable uploads"
/>
</Box>
</>
)}
Expand Down
17 changes: 8 additions & 9 deletions demos/yjs-react-supabase-text-collab/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import React from 'react';
import { CircularProgress, Grid, styled } from '@mui/material';
import { useSupabase } from '@/components/providers/SystemProvider';
import { useNavigate } from 'react-router-dom';
import { usePowerSync } from '@powersync/react';

export default function EntryPage() {
const navigate = useNavigate();
const connector = useSupabase();
const powerSync = usePowerSync();

React.useEffect(() => {
if (!connector) {
Expand All @@ -21,17 +23,14 @@ export default function EntryPage() {
return;
}
// otherwise, create a new document
const { data } = await connector.client
.from('documents')
.insert({
title: 'Test Document ' + (1000 + Math.floor(Math.random() * 8999))
})
.select()
.single();
const results = await powerSync.execute('INSERT INTO documents(id, title) VALUES(uuid(), ?) RETURNING id', [
'Test Document ' + (1000 + Math.floor(Math.random() * 8999))
]);
const documentId = results.rows!.item(0).id;

// redirect user to the document
lastDocumentId = data.id;
window.localStorage.setItem('lastDocumentId', lastDocumentId || '');
lastDocumentId = documentId;
window.localStorage.setItem('lastDocumentId', documentId);
navigate('/editor/' + lastDocumentId);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@ import {
UpdateType
} from '@powersync/web';

import {
SupabaseClient,
createClient,
FunctionsHttpError,
FunctionsRelayError,
FunctionsFetchError
} from '@supabase/supabase-js';
import { createClient, SupabaseClient } from '@supabase/supabase-js';

export type SupabaseConfig = {
supabaseUrl: string;
Expand Down Expand Up @@ -40,6 +34,8 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
readonly client: SupabaseClient;
readonly config: SupabaseConfig;

enableUploads: boolean = true;

constructor() {
super();
this.config = {
Expand All @@ -51,29 +47,31 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
}

async fetchCredentials() {
const { data, error } = await this.client.functions.invoke('powersync-auth-anonymous', {
method: 'GET'
});

if (error instanceof FunctionsHttpError) {
const errorMessage = await error.context.json();
console.log('Supabase edge function returned an error', errorMessage);
} else if (error instanceof FunctionsRelayError) {
console.log('Supabase edge function: Relay error:', error.message);
} else if (error instanceof FunctionsFetchError) {
console.log('Supabase edge function: Fetch error:', error.message);
let {
data: { session }
} = await this.client.auth.getSession();
if (session == null) {
const { data, error } = await this.client.auth.signInAnonymously();
if (error) {
throw error;
}
session = data.session;
}
if (error) {
throw error;
if (session == null) {
throw new Error(`Failed to get Supabase session`);
}

return {
endpoint: data.powersync_url,
token: data.token
endpoint: this.config.powersyncUrl,
token: session.access_token
};
}

async uploadData(database: AbstractPowerSyncDatabase): Promise<void> {
if (!this.enableUploads) {
console.log('Skipping uploadData because uploads were disabled manually');
return;
}

const batch = await database.getCrudBatch(200);

if (!batch) {
Expand Down

This file was deleted.

This file was deleted.

6 changes: 5 additions & 1 deletion demos/yjs-react-supabase-text-collab/sync-rules.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Sync-rule docs: https://docs.powersync.com/usage/sync-rules
bucket_definitions:
global:
documents:
data:
- SELECT * FROM documents
updates:
# Allow remote changes to be synchronized even while there are local changes
priority: 0
data:
- SELECT id, document_id, base64(update_data) as update_b64 FROM document_updates
Loading