Compare commits

..

2 Commits

Author SHA1 Message Date
Trisha Lim
95f51ea783 Update gitignore 2025-01-24 14:51:57 +08:00
Trisha Lim
82b0268c4d Fix example readme instructions 2025-01-24 14:49:54 +08:00
668 changed files with 12445 additions and 40727 deletions

View File

@@ -11,7 +11,7 @@
"cojson-storage-sqlite", "cojson-storage-sqlite",
"cojson-transport-ws", "cojson-transport-ws",
"jazz-browser", "jazz-browser",
"jazz-auth-clerk", "jazz-browser-auth-clerk",
"jazz-browser-media-images", "jazz-browser-media-images",
"jazz-nodejs", "jazz-nodejs",
"jazz-react", "jazz-react",

View File

@@ -1,39 +0,0 @@
name: Setup Android Emulator
inputs:
api-level:
description: 'API level to use for the emulator'
required: true
default: '29'
runs:
using: "composite"
steps:
- name: Enable KVM
shell: bash
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Gradle cache
uses: gradle/actions/setup-gradle@v4
- name: AVD cache
uses: useblacksmith/cache@v5
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ inputs.api-level }}
- name: Create AVD and Generate Snapshot for Caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ inputs.api-level }}
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics
disable-animations: false
script: echo "Generated AVD snapshot for caching."

View File

@@ -1,36 +0,0 @@
name: Get and Build Source Code
runs:
using: "composite"
steps:
- name: Enable latestcorepack
shell: bash
run: |
echo "Before: corepack version => $(corepack --version || echo 'not installed')"
npm install -g corepack@latest
echo "After : corepack version => $(corepack --version)"
corepack enable
pnpm --version
- name: Install Node.js
uses: useblacksmith/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: useblacksmith/cache@v5
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
shell: bash
run: pnpm install --frozen-lockfile

View File

@@ -6,7 +6,7 @@ on:
jobs: jobs:
build-examples: build-examples:
runs-on: blacksmith-4vcpu-ubuntu-2204 runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
example: [ example: [
@@ -19,16 +19,40 @@ jobs:
"pets", "pets",
"reactions", "reactions",
"todo", "todo",
"onboarding",
] ]
steps: steps:
- name: Checkout - uses: actions/checkout@v3
uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Setup Source Code - name: Enable corepack
uses: ./.github/actions/source-code/ run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Pnpm Build - name: Pnpm Build
run: | run: |

View File

@@ -2,25 +2,51 @@ name: Build Starters
on: on:
push: push:
branches: ["main"] branches: [ "main" ]
jobs: jobs:
build-starters: build-starters:
runs-on: blacksmith-4vcpu-ubuntu-2204 runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
starter: ["react-passkey-auth"] starter: [
"react-demo-auth-tailwind",
]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Setup Source Code - name: Enable corepack
uses: ./.github/actions/source-code/ run: corepack enable
- name: Pnpm Build - name: Install Node.js
run: | uses: actions/setup-node@v3
pnpm install with:
pnpm turbo build; node-version-file: '.node-version'
working-directory: ./starters/${{ matrix.starter }} cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Pnpm Build
run: |
pnpm install
pnpm turbo build;
working-directory: ./starters/${{ matrix.starter }}

View File

@@ -6,7 +6,7 @@ on:
jobs: jobs:
quality: quality:
runs-on: blacksmith-4vcpu-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@@ -1,85 +0,0 @@
name: End-to-End Tests for React Native
on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- ".github/actions/android-emulator/**"
- ".github/actions/source-code/**"
- ".github/workflows/e2e-rn-test.yml"
- "examples/chat-rn/**"
- "examples/chat-rn-clerk/**"
- "packages/**"
jobs:
e2e-tests:
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Create Output Directory
run: |
mkdir -p ~/output
- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: corretto
java-version: 22
cache: gradle
- name: Pnpm Build
run: pnpm turbo build --filter="./packages/*"
- name: chat-rn App Pre Build
working-directory: ./examples/chat-rn
run: |
pnpm build
pnpm expo prebuild --clean
- name: Install Maestro
run: |
curl -fsSL "https://get.maestro.mobile.dev" | bash
- name: Setup Android Emulator
id: android-emulator
uses: ./.github/actions/android-emulator/
with:
api-level: 29
- name: Test App
uses: reactivecircus/android-emulator-runner@v2
id: e2e_test
continue-on-error: true
with:
api-level: 29
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics
disable-animations: true
working-directory: ./examples/chat-rn/
script: ./test/e2e/run.sh
- name: Copy Maestro Output
if: steps.e2e_test.outcome != 'success'
run: |
cp -r ~/.maestro/tests/* ~/output
- name: Upload Output Files
if: steps.e2e_test.outcome != 'success'
uses: actions/upload-artifact@v4
with:
name: e2e-test-output
path: ~/output/*
retention-days: 5
- name: Exit with Test Result
if: always()
run: |
if [ "${{ steps.e2e_test.outcome }}" != "success" ]; then
exit 1
fi

View File

@@ -8,16 +8,38 @@ on:
jobs: jobs:
test: test:
runs-on: blacksmith-4vcpu-ubuntu-2204 runs-on: ubuntu-latest
timeout-minutes: 5 timeout-minutes: 5
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Setup Source Code - name: Enable corepack
uses: ./.github/actions/source-code/ run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build jazz-run - name: Build jazz-run
run: pnpm exec turbo build && chmod +x dist/index.js; run: pnpm exec turbo build && chmod +x dist/index.js;
@@ -26,3 +48,4 @@ jobs:
- name: Run create account - name: Run create account
run: ./dist/index.js account create --name "Jazz Run CI test" run: ./dist/index.js account create --name "Jazz Run CI test"
working-directory: ./packages/jazz-run working-directory: ./packages/jazz-run

View File

@@ -9,19 +9,41 @@ on:
jobs: jobs:
test: test:
timeout-minutes: 60 timeout-minutes: 60
runs-on: blacksmith-4vcpu-ubuntu-2204 runs-on: ubuntu-latest
continue-on-error: true continue-on-error: true
strategy: strategy:
matrix: matrix:
project: ["tests/e2e", "examples/chat", "examples/file-share-svelte", "examples/form", "examples/music-player", "examples/pets", "starters/react-passkey-auth"] project: ["tests/e2e", "examples/chat", "examples/file-share-svelte", "examples/form", "examples/music-player", "examples/pets", "examples/onboarding", "starters/react-demo-auth-tailwind"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Setup Source Code - name: Enable corepack
uses: ./.github/actions/source-code/ run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Pnpm Build - name: Pnpm Build
run: pnpm turbo build run: pnpm turbo build

View File

@@ -1,102 +0,0 @@
name: Pre-Publish tagged Pull Requests
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
pre-release:
if: contains(github.event.pull_request.labels.*.name, 'pre-release')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Pnpm Build
run: pnpm turbo build --filter="./packages/*"
- name: Pre publish
run: pnpm exec pkg-pr-new publish --json output.json --comment=off "./packages/*"
- name: Post or update comment
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const output = JSON.parse(fs.readFileSync('output.json', 'utf8'));
const packages = output.packages
.map((p) => `- ${p.name}: ${p.url}`)
.join('\n');
const sha =
context.event_name === 'pull_request'
? context.payload.pull_request.head.sha
: context.payload.after;
const resolutions = Object.fromEntries(
output.packages.map((p) => [p.name, p.url])
);
const commitUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${sha}`;
const body = `## Jazz pre-release
### Packages:
\`\`\`json
${JSON.stringify(resolutions, null, 4)}
\`\`\`
[View Commit](${commitUrl})`;
async function logPublishInfo() {
console.log('\n' + '='.repeat(50));
console.log('Publish Information');
console.log('='.repeat(50));
console.log('\nPublished Packages:');
console.log(output.packages);
console.log('\nTemplates:');
console.log(templates);
console.log(`\nCommit URL: ${commitUrl}`);
console.log('\n' + '='.repeat(50));
}
if (context.eventName === 'pull_request') {
if (context.issue.number) {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body,
});
}
} else if (context.eventName === 'push') {
const pullRequests = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
head: `${context.repo.owner}:${context.ref.replace(
'refs/heads/',
''
)}`,
});
if (pullRequests.data.length > 0) {
await github.rest.issues.createComment({
issue_number: pullRequests.data[0].number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body,
});
} else {
console.log(
'No open pull request found for this push. Logging publish information to console:'
);
await logPublishInfo();
}
}

View File

@@ -17,13 +17,35 @@ concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs: jobs:
release: release:
name: Release name: Release
runs-on: blacksmith-4vcpu-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Setup Source Code - name: Enable corepack
uses: ./.github/actions/source-code/ run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Create Release Pull Request or Publish to npm - name: Create Release Pull Request or Publish to npm
id: changesets id: changesets

View File

@@ -9,20 +9,39 @@ on:
jobs: jobs:
unit-tests: unit-tests:
runs-on: blacksmith-4vcpu-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Source Code - name: Enable corepack
uses: ./.github/actions/source-code/ run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Pnpm Build - name: Pnpm Build
run: pnpm turbo build --filter="./packages/*" run: pnpm turbo build --filter="./packages/*"
- name: Install Playwright Browsers
run: pnpm exec playwright install
- name: Unit Tests - name: Unit Tests
run: pnpm test:ci run: pnpm test:ci

6
.gitignore vendored
View File

@@ -7,15 +7,11 @@ docsTmp
coverage coverage
.direnv .direnv
# Typescript
**/*.tsbuildinfo
# Next.js # Next.js
**/.next **/.next
# Vite output # Vite output
**/dist **/dist
__screenshots__
# Playwright # Playwright
test-results test-results
@@ -23,5 +19,3 @@ test-results
.husky .husky
.vscode/settings.json .vscode/settings.json
.svelte-kit

View File

@@ -48,19 +48,7 @@ You'll need Node.js 20.x or 22.x installed (we're working on support for 23.x),
pnpm install pnpm install
``` ```
3. **Install homepage dependencies**: 3. **Run tests** to verify everything is working:
```bash
cd homepage && pnpm install
```
4. **Build the packages**:
```bash
pnpm build
```
5. **Run tests** to verify everything is working:
```bash ```bash
pnpm test pnpm test
``` ```

View File

@@ -1,4 +1,4 @@
Copyright 2025, Garden Computing, Inc. Copyright 2024, Garden Computing, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

View File

@@ -17,4 +17,4 @@ For community and support, please join our [Discord](https://discord.gg/utDMjHYg
- Community & support: [Discord](https://discord.gg/utDMjHYg42) - Community & support: [Discord](https://discord.gg/utDMjHYg42)
- Updates: [X](https://x.com/jazz_tools) & [Email](https://garden.co/news) - Updates: [X](https://x.com/jazz_tools) & [Email](https://garden.co/news)
Copyright 2025 — Garden Computing, Inc. Copyright 2024 — Garden Computing, Inc.

View File

@@ -42,15 +42,6 @@
} }
}, },
"overrides": [ "overrides": [
{
"include": ["**/package.json"],
"linter": {
"enabled": false
},
"formatter": {
"enabled": false
}
},
{ {
"include": ["packages/**/src/**"], "include": ["packages/**/src/**"],
"linter": { "linter": {

View File

@@ -1,186 +1,5 @@
# chat-rn-clerk # chat-rn-clerk
## 1.0.75
### Patch Changes
- Updated dependencies [3405d8f]
- jazz-react-native@0.10.10
- jazz-react-native-auth-clerk@0.10.10
## 1.0.74
### Patch Changes
- jazz-react-native-auth-clerk@0.10.9
## 1.0.73
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react-native@0.10.8
- jazz-react-native-auth-clerk@0.10.8
- jazz-react-native-media-images@0.10.8
## 1.0.72
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react-native@0.10.7
- jazz-tools@0.10.7
- jazz-react-native-auth-clerk@0.10.7
- jazz-react-native-media-images@0.10.7
## 1.0.71
### Patch Changes
- Updated dependencies [ada802b]
- jazz-tools@0.10.6
- jazz-react-native@0.10.6
- jazz-react-native-auth-clerk@0.10.6
- jazz-react-native-media-images@0.10.6
## 1.0.70
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-react-native@0.10.5
- jazz-react-native-auth-clerk@0.10.5
- jazz-react-native-media-images@0.10.5
## 1.0.69
### Patch Changes
- jazz-react-native@0.10.4
- jazz-react-native-auth-clerk@0.10.4
- jazz-tools@0.10.4
- jazz-react-native-media-images@0.10.4
## 1.0.68
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-react-native@0.10.3
- jazz-react-native-auth-clerk@0.10.3
- jazz-react-native-media-images@0.10.3
## 1.0.67
### Patch Changes
- jazz-react-native@0.10.2
- jazz-react-native-auth-clerk@0.10.2
- jazz-tools@0.10.2
- jazz-react-native-media-images@0.10.2
## 1.0.66
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-react-native@0.10.1
- jazz-react-native-auth-clerk@0.10.1
- jazz-react-native-media-images@0.10.1
## 1.0.65
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-react-native-auth-clerk@0.10.0
- jazz-react-native@0.10.0
- jazz-tools@0.10.0
- jazz-react-native-media-images@0.10.0
## 1.0.64
### Patch Changes
- jazz-react-native@0.9.23
- jazz-react-native-auth-clerk@0.9.23
- jazz-tools@0.9.23
- jazz-react-native-media-images@0.9.23
## 1.0.63
### Patch Changes
- jazz-react-native@0.9.22
- jazz-react-native-auth-clerk@0.9.22
## 1.0.62
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react-native@0.9.21
- jazz-react-native-auth-clerk@0.9.21
- jazz-react-native-media-images@0.9.21
## 1.0.61
### Patch Changes
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-react-native@0.9.20
- jazz-react-native-auth-clerk@0.9.20
- jazz-react-native-media-images@0.9.20
## 1.0.60
### Patch Changes
- jazz-react-native@0.9.19
- jazz-react-native-auth-clerk@0.9.19
- jazz-tools@0.9.19
- jazz-react-native-media-images@0.9.19
## 1.0.59
### Patch Changes
- jazz-react-native@0.9.18
- jazz-react-native-auth-clerk@0.9.18
- jazz-tools@0.9.18
- jazz-react-native-media-images@0.9.18
## 1.0.58
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-react-native@0.9.17
- jazz-react-native-auth-clerk@0.9.17
- jazz-react-native-media-images@0.9.17
## 1.0.57
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-react-native-auth-clerk@0.9.16
- jazz-tools@0.9.16
- jazz-react-native@0.9.16
- jazz-react-native-media-images@0.9.16
## 1.0.56 ## 1.0.56
### Patch Changes ### Patch Changes

View File

@@ -1,9 +1,9 @@
import { Redirect, Stack } from "expo-router"; import { Redirect, Stack } from "expo-router";
import { useIsAuthenticated } from "jazz-react-native";
import React from "react"; import React from "react";
import { useAuth } from "../../src/auth-context";
export default function HomeLayout() { export default function HomeLayout() {
const isAuthenticated = useIsAuthenticated(); const { isAuthenticated } = useAuth();
if (isAuthenticated) { if (isAuthenticated) {
return <Redirect href={"/chat"} />; return <Redirect href={"/chat"} />;

View File

@@ -1,8 +1,8 @@
import { Redirect, Stack } from "expo-router"; import { Redirect, Stack } from "expo-router";
import { useIsAuthenticated } from "jazz-react-native"; import { useAuth } from "../../src/auth-context";
export default function UnAuthenticatedLayout() { export default function UnAuthenticatedLayout() {
const isAuthenticated = useIsAuthenticated(); const { isAuthenticated } = useAuth();
if (isAuthenticated) { if (isAuthenticated) {
return <Redirect href={"/chat"} />; return <Redirect href={"/chat"} />;

View File

@@ -20,15 +20,10 @@ export default function ChatScreen() {
const navigation = useNavigation(); const navigation = useNavigation();
const { user } = useUser(); const { user } = useUser();
function handleLogOut() {
logOut();
router.navigate("/");
}
useLayoutEffect(() => { useLayoutEffect(() => {
navigation.setOptions({ navigation.setOptions({
headerTitle: "Chat", headerTitle: "Chat",
headerRight: () => <Button onPress={handleLogOut} title="Logout" />, headerRight: () => <Button onPress={logOut} title="Logout" />,
}); });
}, [navigation]); }, [navigation]);

View File

@@ -19,6 +19,7 @@ config.resolver.nodeModulesPaths = [
path.resolve(workspaceRoot, "node_modules"), path.resolve(workspaceRoot, "node_modules"),
]; ];
config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"]; config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"];
config.resolver.unstable_enablePackageExports = true;
config.resolver.requireCycleIgnorePatterns = [ config.resolver.requireCycleIgnorePatterns = [
/(^|\/|\\)node_modules($|\/|\\)/, /(^|\/|\\)node_modules($|\/|\\)/,
/(^|\/|\\)packages($|\/|\\)/, /(^|\/|\\)packages($|\/|\\)/,

View File

@@ -1,7 +1,7 @@
{ {
"name": "chat-rn-clerk", "name": "chat-rn-clerk",
"main": "index.js", "main": "index.js",
"version": "1.0.75", "version": "1.0.56",
"scripts": { "scripts": {
"build": "expo export -p ios", "build": "expo export -p ios",
"start": "expo start", "start": "expo start",
@@ -9,22 +9,23 @@
"format-and-lint:fix": "biome check . --write", "format-and-lint:fix": "biome check . --write",
"android": "expo run:android", "android": "expo run:android",
"ios": "expo run:ios", "ios": "expo run:ios",
"web": "expo start --web" "web": "expo start --web",
"test": "jest --watchAll"
}, },
"jest": { "jest": {
"preset": "jest-expo" "preset": "jest-expo"
}, },
"dependencies": { "dependencies": {
"@azure/core-asynciterator-polyfill": "^1.0.2", "@azure/core-asynciterator-polyfill": "^1.0.2",
"@bacons/text-decoder": "0.0.0",
"@bam.tech/react-native-image-resizer": "^3.0.11", "@bam.tech/react-native-image-resizer": "^3.0.11",
"@craftzdog/react-native-buffer": "6.0.5",
"@clerk/clerk-expo": "^2.2.21", "@clerk/clerk-expo": "^2.2.21",
"@expo/vector-icons": "^14.0.2", "@expo/vector-icons": "^14.0.2",
"@op-engineering/op-sqlite": "^11.2.12", "@op-engineering/op-sqlite": "^11.2.12",
"@react-native-community/netinfo": "^11.4.1", "@react-native-community/netinfo": "^11.4.1",
"@react-navigation/native": "^7.0.13", "@react-navigation/native": "^7.0.13",
"@react-navigation/native-stack": "^7.1.14", "@react-navigation/native-stack": "^7.1.14",
"base-64": "^1.0.0",
"buffer": "^6.0.3",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"expo": "^52.0.0", "expo": "^52.0.0",
"expo-build-properties": "~0.13.1", "expo-build-properties": "~0.13.1",
@@ -50,14 +51,18 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-native": "~0.76.3", "react-native": "~0.76.3",
"react-native-fetch-api": "^3.0.0",
"react-native-gesture-handler": "~2.20.2", "react-native-gesture-handler": "~2.20.2",
"react-native-get-random-values": "^1.11.0", "react-native-get-random-values": "^1.11.0",
"react-native-polyfill-globals": "^3.1.0",
"react-native-quick-base64": "^2.1.2",
"react-native-reanimated": "~3.16.3", "react-native-reanimated": "~3.16.3",
"react-native-safe-area-context": "4.12.0", "react-native-safe-area-context": "4.12.0",
"react-native-screens": "4.1.0", "react-native-screens": "4.1.0",
"react-native-url-polyfill": "^2.0.0", "react-native-url-polyfill": "^2.0.0",
"react-native-web": "~0.19.13", "react-native-web": "~0.19.13",
"readable-stream": "4.7.0" "text-encoding": "^0.7.0",
"web-streams-polyfill": "^3.2.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
@@ -67,7 +72,7 @@
"jest": "^29.2.1", "jest": "^29.2.1",
"jest-expo": "~52.0.2", "jest-expo": "~52.0.2",
"react-test-renderer": "18.2.0", "react-test-renderer": "18.2.0",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.15",
"typescript": "~5.6.2" "typescript": "~5.6.2"
}, },
"private": true "private": true

View File

@@ -1,17 +1,8 @@
/* eslint-disable import/order */ import "react-native-polyfill-globals/auto";
// @ts-expect-error - @types/react-native doesn't cover this file
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions";
import { Buffer } from "@craftzdog/react-native-buffer";
polyfillGlobal("Buffer", () => Buffer);
// @ts-expect-error - @types/readable-stream doesn't have ReadableStream type
import { ReadableStream } from "readable-stream";
polyfillGlobal("ReadableStream", () => ReadableStream);
import "@azure/core-asynciterator-polyfill"; import "@azure/core-asynciterator-polyfill";
import { Buffer } from "buffer";
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions";
import { ReadableStream } from "web-streams-polyfill/ponyfill/es6";
import "@bacons/text-decoder/install"; polyfillGlobal("Buffer", () => Buffer);
polyfillGlobal("ReadableStream", () => ReadableStream);
import "react-native-get-random-values";

View File

@@ -1 +0,0 @@
export const apiKey = "chat-rn-clerk-example-jazz@garden.co";

View File

@@ -1,21 +1,49 @@
import { useClerk } from "@clerk/clerk-expo"; import { useClerk, useUser } from "@clerk/clerk-expo";
import { JazzProviderWithClerk } from "jazz-react-native-auth-clerk"; import { JazzProvider, setupKvStore } from "jazz-react-native";
import React, { PropsWithChildren } from "react"; import { useJazzClerkAuth } from "jazz-react-native-auth-clerk";
import { apiKey } from "./apiKey"; import React, { createContext, PropsWithChildren, useContext } from "react";
import { Text, View } from "react-native";
const AuthContext = createContext<{
isAuthenticated: boolean;
isLoading: boolean;
}>({
isAuthenticated: false,
isLoading: true,
});
export function useAuth() {
return useContext(AuthContext);
}
const kvStore = setupKvStore();
export function JazzAndAuth({ children }: PropsWithChildren) { export function JazzAndAuth({ children }: PropsWithChildren) {
const { isSignedIn, isLoaded: isClerkLoaded } = useUser();
const clerk = useClerk(); const clerk = useClerk();
const [auth, state] = useJazzClerkAuth(clerk, kvStore);
const isAuthenticated = Boolean(isSignedIn && isClerkLoaded && auth);
return ( return (
<JazzProviderWithClerk <AuthContext.Provider
clerk={clerk} value={{ isAuthenticated, isLoading: !isClerkLoaded || !auth }}
storage="sqlite"
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
when: "signedUp", // This makes the app work in local mode when the user is not authenticated
}}
> >
{children} {state?.errors?.length > 0 &&
</JazzProviderWithClerk> state.errors.map((error) => (
<View key={error}>
<Text style={{ color: "red" }}>{error}</Text>
</View>
))}
{auth && clerk.user ? (
<JazzProvider
auth={auth}
storage="sqlite"
peer="wss://cloud.jazz.tools/?key=chat-rn-clerk-example-jazz@garden.co"
>
{children}
</JazzProvider>
) : (
children
)}
</AuthContext.Provider>
); );
} }

View File

@@ -1,146 +1,5 @@
# chat-rn # chat-rn
## 1.0.71
### Patch Changes
- Updated dependencies [3405d8f]
- jazz-react-native@0.10.10
## 1.0.70
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react-native@0.10.8
## 1.0.69
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react-native@0.10.7
- jazz-tools@0.10.7
## 1.0.68
### Patch Changes
- Updated dependencies [ada802b]
- jazz-tools@0.10.6
- jazz-react-native@0.10.6
## 1.0.67
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-react-native@0.10.5
## 1.0.66
### Patch Changes
- jazz-react-native@0.10.4
- jazz-tools@0.10.4
## 1.0.65
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-react-native@0.10.3
## 1.0.64
### Patch Changes
- jazz-react-native@0.10.2
- jazz-tools@0.10.2
## 1.0.63
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-react-native@0.10.1
## 1.0.62
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-react-native@0.10.0
- jazz-tools@0.10.0
## 1.0.61
### Patch Changes
- jazz-react-native@0.9.23
- jazz-tools@0.9.23
## 1.0.60
### Patch Changes
- jazz-react-native@0.9.22
## 1.0.59
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react-native@0.9.21
## 1.0.58
### Patch Changes
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-react-native@0.9.20
## 1.0.57
### Patch Changes
- jazz-react-native@0.9.19
- jazz-tools@0.9.19
## 1.0.56
### Patch Changes
- jazz-react-native@0.9.18
- jazz-tools@0.9.18
## 1.0.55
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-react-native@0.9.17
## 1.0.54
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-tools@0.9.16
- jazz-react-native@0.9.16
## 1.0.53 ## 1.0.53
### Patch Changes ### Patch Changes

View File

@@ -19,6 +19,7 @@ config.resolver.nodeModulesPaths = [
path.resolve(workspaceRoot, "node_modules"), path.resolve(workspaceRoot, "node_modules"),
]; ];
config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"]; config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"];
config.resolver.unstable_enablePackageExports = true;
config.resolver.requireCycleIgnorePatterns = [ config.resolver.requireCycleIgnorePatterns = [
/(^|\/|\\)node_modules($|\/|\\)/, /(^|\/|\\)node_modules($|\/|\\)/,
/(^|\/|\\)packages($|\/|\\)/, /(^|\/|\\)packages($|\/|\\)/,

View File

@@ -1,6 +1,6 @@
{ {
"name": "chat-rn", "name": "chat-rn",
"version": "1.0.71", "version": "1.0.53",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"build": "expo export -p ios", "build": "expo export -p ios",
@@ -13,17 +13,17 @@
}, },
"dependencies": { "dependencies": {
"@azure/core-asynciterator-polyfill": "^1.0.2", "@azure/core-asynciterator-polyfill": "^1.0.2",
"@bacons/text-decoder": "0.0.0",
"@craftzdog/react-native-buffer": "6.0.5",
"@op-engineering/op-sqlite": "^11.2.12", "@op-engineering/op-sqlite": "^11.2.12",
"@react-native-community/netinfo": "^11.4.1", "@react-native-community/netinfo": "^11.4.1",
"@react-navigation/native": "^7.0.13", "@react-navigation/native": "^7.0.13",
"@react-navigation/native-stack": "^7.1.14", "@react-navigation/native-stack": "^7.1.14",
"base-64": "^1.0.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"expo": "^52.0.0", "expo": "^52.0.0",
"expo-build-properties": "~0.13.1", "expo-build-properties": "~0.13.1",
"expo-clipboard": "~7.0.0", "expo-clipboard": "~7.0.0",
"expo-constants": "~17.0.3", "expo-constants": "~17.0.3",
"expo-crypto": "~14.0.1",
"expo-dev-client": "~5.0.5", "expo-dev-client": "~5.0.5",
"expo-linking": "~7.0.3", "expo-linking": "~7.0.3",
"expo-secure-store": "~14.0.0", "expo-secure-store": "~14.0.0",
@@ -34,18 +34,19 @@
"nativewind": "^4.1.21", "nativewind": "^4.1.21",
"react": "^18.3.1", "react": "^18.3.1",
"react-native": "~0.76.3", "react-native": "~0.76.3",
"react-native-fetch-api": "^3.0.0",
"react-native-get-random-values": "^1.11.0", "react-native-get-random-values": "^1.11.0",
"react-native-nitro-modules": "0.21.0", "react-native-polyfill-globals": "^3.1.0",
"react-native-quick-crypto": "1.0.0-beta.12",
"react-native-safe-area-context": "4.12.0", "react-native-safe-area-context": "4.12.0",
"react-native-screens": "4.1.0", "react-native-screens": "4.1.0",
"react-native-url-polyfill": "^2.0.0", "react-native-url-polyfill": "^2.0.0",
"readable-stream": "4.7.0" "text-encoding": "^0.7.0",
"web-streams-polyfill": "^3.2.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.15",
"typescript": "~5.6.2" "typescript": "~5.6.2"
}, },
"private": true "private": true

View File

@@ -1,17 +1,6 @@
/* eslint-disable import/order */ import "react-native-polyfill-globals/auto";
// @ts-expect-error - @types/react-native doesn't cover this file
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions";
import { Buffer } from "@craftzdog/react-native-buffer";
polyfillGlobal("Buffer", () => Buffer);
// @ts-expect-error - @types/readable-stream doesn't have ReadableStream type
import { ReadableStream } from "readable-stream";
polyfillGlobal("ReadableStream", () => ReadableStream);
import "@azure/core-asynciterator-polyfill"; import "@azure/core-asynciterator-polyfill";
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions";
import { ReadableStream } from "web-streams-polyfill/ponyfill/es6";
import "@bacons/text-decoder/install"; polyfillGlobal("ReadableStream", () => ReadableStream);
import "react-native-get-random-values";

View File

@@ -9,8 +9,7 @@ import * as Linking from "expo-linking";
import React, { StrictMode, useEffect, useState } from "react"; import React, { StrictMode, useEffect, useState } from "react";
import HandleInviteScreen from "./invite"; import HandleInviteScreen from "./invite";
import { JazzProvider, RNQuickCrypto } from "jazz-react-native"; import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react-native";
import { apiKey } from "./apiKey";
import ChatScreen from "./chat"; import ChatScreen from "./chat";
const Stack = createNativeStackNavigator(); const Stack = createNativeStackNavigator();
@@ -29,6 +28,7 @@ const linking = {
}; };
function App() { function App() {
const [auth, state] = useDemoAuth();
const [initialRoute, setInitialRoute] = useState< const [initialRoute, setInitialRoute] = useState<
"ChatScreen" | "HandleInviteScreen" "ChatScreen" | "HandleInviteScreen"
>("ChatScreen"); >("ChatScreen");
@@ -43,14 +43,16 @@ function App() {
}); });
}, []); }, []);
if (!auth) {
return null;
}
return ( return (
<StrictMode> <StrictMode>
<JazzProvider <JazzProvider
auth={auth}
storage="sqlite" storage="sqlite"
sync={{ peer="wss://cloud.jazz.tools/?key=chat-rn-example-jazz@garden.co"
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
CryptoProvider={RNQuickCrypto}
> >
<NavigationContainer linking={linking} ref={navigationRef}> <NavigationContainer linking={linking} ref={navigationRef}>
<Stack.Navigator initialRouteName={initialRoute}> <Stack.Navigator initialRouteName={initialRoute}>
@@ -67,6 +69,9 @@ function App() {
</Stack.Navigator> </Stack.Navigator>
</NavigationContainer> </NavigationContainer>
</JazzProvider> </JazzProvider>
{state.state !== "signedIn" ? (
<DemoAuthBasicUI appName="Jazz Chat" state={state} />
) : null}
</StrictMode> </StrictMode>
); );
} }

View File

@@ -1 +0,0 @@
export const apiKey = "chat-rn-example-jazz@garden.co";

View File

@@ -1,6 +1,6 @@
import clsx from "clsx"; import clsx from "clsx";
import * as Clipboard from "expo-clipboard"; import * as Clipboard from "expo-clipboard";
import { Group, ID, Profile } from "jazz-tools"; import { Group, ID } from "jazz-tools";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import React, { import React, {
Button, Button,
@@ -22,16 +22,10 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
const [chatId, setChatId] = useState<ID<Chat>>(); const [chatId, setChatId] = useState<ID<Chat>>();
const loadedChat = useCoState(Chat, chatId, [{}]); const loadedChat = useCoState(Chat, chatId, [{}]);
const [message, setMessage] = useState(""); const [message, setMessage] = useState("");
const profile = useCoState(Profile, me._refs.profile?.id, {});
function handleLogOut() {
setChatId(undefined);
logOut();
}
useEffect(() => { useEffect(() => {
navigation.setOptions({ navigation.setOptions({
headerRight: () => <Button onPress={handleLogOut} title="Logout" />, headerRight: () => <Button onPress={logOut} title="Logout" />,
headerLeft: () => headerLeft: () =>
loadedChat ? ( loadedChat ? (
<Button <Button
@@ -137,19 +131,6 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
<View className="flex flex-col h-full"> <View className="flex flex-col h-full">
{!loadedChat ? ( {!loadedChat ? (
<View className="flex flex-col h-full items-center justify-center"> <View className="flex flex-col h-full items-center justify-center">
<Text className="text-m font-bold mb-6">Username</Text>
<TextInput
className="rounded h-12 p-2 mb-12 w-40 border border-gray-200 block"
value={profile?.name ?? ""}
onChangeText={(value) => {
if (profile) {
profile.name = value;
}
}}
textAlignVertical="center"
onSubmitEditing={sendMessage}
testID="username-input"
/>
<TouchableOpacity <TouchableOpacity
onPress={createChat} onPress={createChat}
className="bg-blue-500 p-4 rounded-md" className="bg-blue-500 p-4 rounded-md"
@@ -191,12 +172,10 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
placeholder="Type a message..." placeholder="Type a message..."
textAlignVertical="center" textAlignVertical="center"
onSubmitEditing={sendMessage} onSubmitEditing={sendMessage}
testID="message-input"
/> />
<TouchableOpacity <TouchableOpacity
onPress={sendMessage} onPress={sendMessage}
className="bg-gray-300 text-white rounded-full h-8 w-8 items-center justify-center" className="bg-gray-300 text-white rounded-full h-8 w-8 items-center justify-center"
testID="send-button"
> >
<Text></Text> <Text></Text>
</TouchableOpacity> </TouchableOpacity>

View File

@@ -1,16 +0,0 @@
# this sub-flow exists to work around an ios issue where the text field is not
# fully erased. The tap into the input field hits the middle, and clears all
# text to the left. If there's more to the right, it slides left, and thus we
# repeat this step. https://maestro.mobile.dev/api-reference/commands/erasetext
appId: com.jazz.chatrn
---
- copyTextFrom:
id: ${id}
- repeat:
times: 4
commands:
- tapOn:
id: ${id}
- eraseText
- copyTextFrom:
id: ${id}

View File

@@ -1,48 +0,0 @@
appId: com.jazz.chatrn
---
- launchApp
# # handle Expo screens (for local dev)
# - assertVisible: "Continue"
# - tapOn: "Continue"
# - assertVisible: "Reload"
# - tapOn: "Reload"
# login
- assertVisible: "Anonymous user"
- runFlow:
label: "Erase existing username"
file: erase_text.yml
env:
id: "username-input"
- inputText: "boorad"
- assertVisible: "boorad"
# start new chat
- tapOn: "Start new chat"
- assertVisible: "Share"
- assertVisible: "Jazz Chat"
- assertVisible: "Logout"
# send a message
- runFlow:
label: "Erase existing message"
file: erase_text.yml
env:
id: "message-input"
- inputText: "bro, low key, it do be like that tho"
- tapOn:
id: "send-button"
- assertVisible: "bro, low key, it do be like that tho"
# get invite code
- tapOn: "Share"
- assertVisible: "Copied to clipboard"
- tapOn: "OK"
# this assert doesn't work. maestro.copiedText only populates from `copyTextFrom`
# - assertTrue: ${maestro.copiedText.startsWith("co_z")}
# logout
- tapOn: "Logout"
- assertVisible: "boorad"
- assertVisible: "bro, low key, it do be like that tho"

View File

@@ -1,20 +0,0 @@
#!/bin/bash
# This script is necessary, because unlike ios, the android emulator action
# accepts a script, runs it as your tests, then terminates.
set -e
# build and install the app
echo "Building and installing Android app."
echo "If it fails, its output will be in artifact: android-install.log..."
cd ./android/
./gradlew installRelease >> ~/output/android-install.log 2>&1
cd ..
# run the e2e tests
export PATH="$PATH":"$HOME/.maestro/bin"
export MAESTRO_DRIVER_STARTUP_TIMEOUT=300000 # setting to 5 mins 👀
export MAESTRO_CLI_NO_ANALYTICS=1
export MAESTRO_CLI_ANALYSIS_NOTIFICATION_DISABLED=true
maestro test test/e2e/flow.yml

View File

@@ -1,166 +1,5 @@
# chat-vue # chat-vue
## 0.0.58
### Patch Changes
- Updated dependencies [834203f]
- jazz-browser@0.10.9
- jazz-vue@0.10.9
## 0.0.57
### Patch Changes
- Updated dependencies [1e87fc7]
- Updated dependencies [2fb6428]
- jazz-browser@0.10.8
- jazz-tools@0.10.8
- jazz-vue@0.10.8
## 0.0.56
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [bf76d79]
- Updated dependencies [0eed228]
- jazz-browser@0.10.7
- jazz-tools@0.10.7
- jazz-vue@0.10.7
## 0.0.55
### Patch Changes
- Updated dependencies [ada802b]
- jazz-tools@0.10.6
- jazz-browser@0.10.6
- jazz-vue@0.10.6
## 0.0.54
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-browser@0.10.5
- jazz-vue@0.10.5
## 0.0.53
### Patch Changes
- jazz-browser@0.10.4
- jazz-tools@0.10.4
- jazz-vue@0.10.4
## 0.0.52
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-browser@0.10.3
- jazz-vue@0.10.3
## 0.0.51
### Patch Changes
- jazz-browser@0.10.2
- jazz-tools@0.10.2
- jazz-vue@0.10.2
## 0.0.50
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-browser@0.10.1
- jazz-vue@0.10.1
## 0.0.49
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-browser@0.10.0
- jazz-tools@0.10.0
- jazz-vue@0.10.0
## 0.0.48
### Patch Changes
- jazz-browser@0.9.23
- jazz-tools@0.9.23
- jazz-vue@0.9.23
## 0.0.47
### Patch Changes
- jazz-browser@0.9.22
- jazz-vue@0.9.22
## 0.0.46
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser@0.9.21
- jazz-vue@0.9.21
## 0.0.45
### Patch Changes
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-browser@0.9.20
- jazz-vue@0.9.20
## 0.0.44
### Patch Changes
- jazz-browser@0.9.19
- jazz-tools@0.9.19
- jazz-vue@0.9.19
## 0.0.43
### Patch Changes
- jazz-browser@0.9.18
- jazz-tools@0.9.18
- jazz-vue@0.9.18
## 0.0.42
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-browser@0.9.17
- jazz-vue@0.9.17
## 0.0.41
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-tools@0.9.16
- jazz-browser@0.9.16
- jazz-vue@0.9.16
## 0.0.40 ## 0.0.40
### Patch Changes ### Patch Changes

View File

@@ -11,6 +11,10 @@ You can either
Create a new Jazz project, and use this example as a template. Create a new Jazz project, and use this example as a template.
```bash ```bash
npm create jazz-app@latest --example chat-vue --project-name chat-vue
```
or
```bash
npx create-jazz-app@latest --example chat-vue --project-name chat-vue npx create-jazz-app@latest --example chat-vue --project-name chat-vue
``` ```

View File

@@ -1,6 +1,6 @@
{ {
"name": "chat-vue", "name": "chat-vue",
"version": "0.0.58", "version": "0.0.40",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -30,9 +30,9 @@
"eslint-plugin-vue": "^9.28.0", "eslint-plugin-vue": "^9.28.0",
"npm-run-all2": "^6.2.3", "npm-run-all2": "^6.2.3",
"postcss": "^8.4.27", "postcss": "^8.4.27",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.15",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^6.0.11", "vite": "^5.4.10",
"vite-plugin-vue-devtools": "^7.4.6", "vite-plugin-vue-devtools": "^7.4.6",
"vue-tsc": "^2.1.6" "vue-tsc": "^2.1.6"
} }

View File

@@ -1 +0,0 @@
export const apiKey = "chat-example-jazz@garden.co";

View File

@@ -1,31 +1,32 @@
import { DemoAuthBasicUI, JazzProvider } from "jazz-vue"; import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-vue";
import { createApp, defineComponent, h } from "vue"; import { createApp, defineComponent, h } from "vue";
import App from "./App.vue"; import App from "./App.vue";
import "./index.css"; import "./index.css";
import { apiKey } from "@/apiKey";
import router from "./router"; import router from "./router";
const RootComponent = defineComponent({ const RootComponent = defineComponent({
name: "RootComponent", name: "RootComponent",
setup() { setup() {
return () => const { authMethod, state } = useDemoAuth();
return () => [
h( h(
JazzProvider, JazzProvider,
{ {
sync: { auth: authMethod.value,
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: "wss://cloud.jazz.tools/?key=chat-example-jazz@garden.co",
},
}, },
h( {
DemoAuthBasicUI, default: () => h(App),
{ },
appName: "Jazz Vue Chat", ),
},
{ state.state !== "signedIn" &&
default: () => h(App), h(DemoAuthBasicUI, {
}, appName: "Jazz Chat",
), state,
); }),
];
}, },
}); });

View File

@@ -1,165 +1,5 @@
# jazz-example-chat # jazz-example-chat
## 0.0.154
### Patch Changes
- jazz-browser-media-images@0.10.9
- jazz-react@0.10.9
## 0.0.153
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-browser-media-images@0.10.8
## 0.0.152
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-browser-media-images@0.10.7
## 0.0.151
### Patch Changes
- Updated dependencies [1d71ca1]
- Updated dependencies [ada802b]
- hash-slash@0.2.2
- jazz-react@0.10.6
- jazz-tools@0.10.6
- jazz-browser-media-images@0.10.6
## 0.0.150
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-browser-media-images@0.10.5
- jazz-react@0.10.5
## 0.0.149
### Patch Changes
- jazz-react@0.10.4
- jazz-tools@0.10.4
- jazz-browser-media-images@0.10.4
## 0.0.148
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-browser-media-images@0.10.3
- jazz-react@0.10.3
## 0.0.147
### Patch Changes
- jazz-react@0.10.2
- jazz-tools@0.10.2
- jazz-browser-media-images@0.10.2
## 0.0.146
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-browser-media-images@0.10.1
- jazz-react@0.10.1
## 0.0.145
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-react@0.10.0
- jazz-tools@0.10.0
- jazz-browser-media-images@0.10.0
## 0.0.144
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
- jazz-browser-media-images@0.9.23
## 0.0.143
### Patch Changes
- jazz-browser-media-images@0.9.22
- jazz-react@0.9.22
## 0.0.142
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser-media-images@0.9.21
- jazz-react@0.9.21
## 0.0.141
### Patch Changes
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-browser-media-images@0.9.20
- jazz-react@0.9.20
## 0.0.140
### Patch Changes
- jazz-react@0.9.19
- jazz-tools@0.9.19
- jazz-browser-media-images@0.9.19
## 0.0.139
### Patch Changes
- jazz-react@0.9.18
- jazz-tools@0.9.18
- jazz-browser-media-images@0.9.18
## 0.0.138
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-browser-media-images@0.9.17
- jazz-react@0.9.17
## 0.0.137
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-tools@0.9.16
- jazz-browser-media-images@0.9.16
- jazz-react@0.9.16
## 0.0.136 ## 0.0.136
### Patch Changes ### Patch Changes

View File

@@ -13,6 +13,10 @@ You can either
Create a new Jazz project, and use this example as a template. Create a new Jazz project, and use this example as a template.
```bash ```bash
npm create jazz-app@latest --example chat --project-name chat
```
or
```bash
npx create-jazz-app@latest --example chat --project-name chat npx create-jazz-app@latest --example chat --project-name chat
``` ```

View File

@@ -1,7 +1,7 @@
{ {
"name": "jazz-example-chat", "name": "jazz-example-chat",
"private": true, "private": true,
"version": "0.0.154", "version": "0.0.136",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -9,8 +9,8 @@
"format-and-lint": "biome check .", "format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write", "format-and-lint:fix": "biome check . --write",
"preview": "vite preview", "preview": "vite preview",
"test:e2e": "playwright test", "test": "playwright test",
"test:e2e:ui": "playwright test --ui" "test:ui": "playwright test --ui"
}, },
"dependencies": { "dependencies": {
"clsx": "^2.0.0", "clsx": "^2.0.0",
@@ -23,15 +23,15 @@
"react-dom": "^18.3.1" "react-dom": "^18.3.1"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.50.1", "@playwright/test": "^1.46.1",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"@vitejs/plugin-react-swc": "^3.3.2", "@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"is-ci": "^3.0.1", "is-ci": "^3.0.1",
"postcss": "^8.4.27", "postcss": "^8.4.27",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.15",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^6.0.11" "vite": "^5.4.10"
} }
} }

View File

@@ -1 +0,0 @@
export const apiKey = "chat-example-jazz@garden.co";

View File

@@ -1,11 +1,11 @@
import { apiKey } from "@/apiKey.ts"; import { inIframe, onChatLoad } from "@/util.ts";
import { getRandomUsername, inIframe, onChatLoad } from "@/util.ts";
import { useIframeHashRouter } from "hash-slash"; import { useIframeHashRouter } from "hash-slash";
import { JazzProvider, useAccount } from "jazz-react"; import { useAccount } from "jazz-react";
import { Group, ID } from "jazz-tools"; import { Group, ID } from "jazz-tools";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { ChatScreen } from "./chatScreen.tsx"; import { ChatScreen } from "./chatScreen.tsx";
import { JazzAndAuth } from "./jazz.tsx";
import { Chat } from "./schema.ts"; import { Chat } from "./schema.ts";
import { ThemeProvider } from "./themeProvider.tsx"; import { ThemeProvider } from "./themeProvider.tsx";
import { AppContainer, TopBar } from "./ui.tsx"; import { AppContainer, TopBar } from "./ui.tsx";
@@ -28,16 +28,7 @@ export function App() {
return ( return (
<AppContainer> <AppContainer>
<TopBar> <TopBar>
<input <p>{me?.profile?.name}</p>
type="text"
value={me?.profile?.name ?? ""}
className="bg-transparent"
onChange={(e) => {
if (!me?.profile) return;
me.profile.name = e.target.value;
}}
placeholder="Set username"
/>
{!inIframe && <button onClick={logOut}>Log out</button>} {!inIframe && <button onClick={logOut}>Log out</button>}
</TopBar> </TopBar>
{router.route({ {router.route({
@@ -48,20 +39,12 @@ export function App() {
); );
} }
const url = new URL(window.location.href);
const defaultProfileName = url.searchParams.get("user") ?? getRandomUsername();
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<ThemeProvider> <ThemeProvider>
<StrictMode> <StrictMode>
<JazzProvider <JazzAndAuth>
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
defaultProfileName={defaultProfileName}
>
<App /> <App />
</JazzProvider> </JazzAndAuth>
</StrictMode> </StrictMode>
</ThemeProvider>, </ThemeProvider>,
); );

View File

@@ -0,0 +1,19 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
export function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, state] = useDemoAuth();
return (
<>
<JazzProvider
auth={auth}
peer="wss://cloud.jazz.tools/?key=chat-example-jazz@garden.co"
>
{children}
</JazzProvider>
{state.state !== "signedIn" && (
<DemoAuthBasicUI appName="Jazz Chat" state={state} />
)}
</>
);
}

View File

@@ -6,7 +6,7 @@ import { useId, useRef } from "react";
export function AppContainer(props: { children: React.ReactNode }) { export function AppContainer(props: { children: React.ReactNode }) {
return ( return (
<div className="flex flex-col justify-between w-screen h-screen bg-stone-50 dark:bg-stone-925 dark:text-white"> <div className="flex flex-col justify-between w-screen h-screen bg-stone-50 dark:bg-black dark:text-white">
{props.children} {props.children}
</div> </div>
); );
@@ -14,7 +14,7 @@ export function AppContainer(props: { children: React.ReactNode }) {
export function TopBar(props: { children: React.ReactNode }) { export function TopBar(props: { children: React.ReactNode }) {
return ( return (
<div className="p-3 bg-white w-full flex justify-between gap-2 border-b dark:bg-transparent dark:border-stone-900"> <div className="p-3 bg-white w-full flex justify-between gap-2 border-b dark:bg-transparent dark:border-stone-800">
{props.children} {props.children}
</div> </div>
); );
@@ -33,7 +33,7 @@ export function ChatBody(props: { children: React.ReactNode }) {
export function EmptyChatMessage() { export function EmptyChatMessage() {
return ( return (
<div className="h-full text-base text-stone-500 flex items-center justify-center px-3 md:text-2xl"> <div className="h-full text-base text-stone-500 flex items-center justify-center px-3 text-lg md:text-2xl">
Start a conversation below. Start a conversation below.
</div> </div>
); );
@@ -61,7 +61,7 @@ export function BubbleBody(props: {
"line-clamp-10 text-ellipsis whitespace-pre-wrap", "line-clamp-10 text-ellipsis whitespace-pre-wrap",
"rounded-2xl overflow-hidden max-w-[calc(100%-5rem)] shadow-sm p-1", "rounded-2xl overflow-hidden max-w-[calc(100%-5rem)] shadow-sm p-1",
props.fromMe props.fromMe
? "bg-white dark:bg-stone-900 dark:text-white" ? "bg-white dark:bg-stone-700 dark:text-white"
: "bg-blue text-white", : "bg-blue text-white",
)} )}
> >
@@ -97,7 +97,7 @@ export function BubbleInfo(props: { by: string | undefined; madeAt: Date }) {
export function InputBar(props: { children: React.ReactNode }) { export function InputBar(props: { children: React.ReactNode }) {
return ( return (
<div className="p-3 bg-white border-t shadow-2xl mt-auto flex gap-1 dark:bg-transparent dark:border-stone-900"> <div className="p-3 bg-white border-t shadow-2xl mt-auto flex gap-1 dark:bg-transparent dark:border-stone-800">
{props.children} {props.children}
</div> </div>
); );
@@ -147,7 +147,7 @@ export function TextInput(props: { onSubmit: (text: string) => void }) {
</label> </label>
<input <input
id={inputId} id={inputId}
className="rounded-full py-1 px-3 border block w-full placeholder:text-stone-500 dark:bg-stone-925 dark:text-white dark:border-stone-900" className="rounded-full py-1 px-3 border block w-full placeholder:text-stone-500 dark:bg-black dark:text-white dark:border-stone-700"
placeholder="Type a message and press Enter" placeholder="Type a message and press Enter"
maxLength={2048} maxLength={2048}
onKeyDown={({ key, currentTarget: input }) => { onKeyDown={({ key, currentTarget: input }) => {

View File

@@ -15,20 +15,3 @@ export function onChatLoad(chat: Chat) {
} }
export const inIframe = window.self !== window.top; export const inIframe = window.self !== window.top;
const animals = [
"elephant",
"penguin",
"giraffe",
"octopus",
"kangaroo",
"dolphin",
"cheetah",
"koala",
"platypus",
"pangolin",
];
export function getRandomUsername() {
return `Anonymous ${animals[Math.floor(Math.random() * animals.length)]}`;
}

View File

@@ -1,35 +1,48 @@
import { test } from "@playwright/test"; import { test } from "@playwright/test";
import { ChatPage } from "./pages/ChatPage"; import { ChatPage } from "./pages/ChatPage";
import { LoginPage } from "./pages/LoginPage";
test("chat between two users", async ({ page: marioPage, browser }) => { test("chat between two users", async ({ page }) => {
const context = await browser.newContext(); const loginPage = new LoginPage(page);
const luigiPage = await context.newPage();
await marioPage.goto("/"); const mario = "S. Mario";
const luigi = "Luigi";
const marioChat = new ChatPage(marioPage); await loginPage.goto();
const luigiChat = new ChatPage(luigiPage); await loginPage.fillUsername(mario);
await loginPage.signup();
await marioChat.setUsername("Mario"); const chatPage = new ChatPage(page);
const message1ByMario = "Hello Luigi, are you ready to save the princess?"; const message1ByMario = "Hello Luigi, are you ready to save the princess?";
await marioChat.sendMessage(message1ByMario); await chatPage.sendMessage(message1ByMario);
await marioChat.expectMessageRow(message1ByMario); await chatPage.expectMessageRow(message1ByMario);
const roomURL = marioPage.url(); const roomURL = page.url();
await luigiPage.goto(roomURL);
await luigiChat.setUsername("Luigi"); await chatPage.logout();
await luigiChat.expectMessageRow(message1ByMario); await loginPage.expectLoaded();
await loginPage.fillUsername(luigi);
await loginPage.signup();
await page.goto(roomURL);
await chatPage.expectMessageRow(message1ByMario);
const message2ByLuigi = const message2ByLuigi =
"No, I'm not ready yet. I'm still trying to find the key to the castle."; "No, I'm not ready yet. I'm still trying to find the key to the castle.";
await luigiChat.sendMessage(message2ByLuigi); await chatPage.sendMessage(message2ByLuigi);
await luigiChat.expectMessageRow(message2ByLuigi); await chatPage.expectMessageRow(message2ByLuigi);
await marioChat.expectMessageRow(message1ByMario); await chatPage.logout();
await luigiChat.expectMessageRow(message2ByLuigi); await loginPage.loginAs(mario);
await page.goto(roomURL);
await chatPage.expectMessageRow(message1ByMario);
await chatPage.expectMessageRow(message2ByLuigi);
}); });

View File

@@ -4,7 +4,7 @@ export class ChatPage {
readonly page: Page; readonly page: Page;
readonly messageInput: Locator; readonly messageInput: Locator;
readonly logoutButton: Locator; readonly logoutButton: Locator;
readonly usernameInput: Locator;
constructor(page: Page) { constructor(page: Page) {
this.page = page; this.page = page;
this.messageInput = page.getByRole("textbox", { this.messageInput = page.getByRole("textbox", {
@@ -13,11 +13,6 @@ export class ChatPage {
this.logoutButton = page.getByRole("button", { this.logoutButton = page.getByRole("button", {
name: "Log out", name: "Log out",
}); });
this.usernameInput = page.getByPlaceholder("Set username");
}
async setUsername(username: string) {
await this.usernameInput.fill(username);
} }
async sendMessage(message: string) { async sendMessage(message: string) {

View File

@@ -0,0 +1,40 @@
import { Locator, Page, expect } from "@playwright/test";
export class LoginPage {
readonly page: Page;
readonly usernameInput: Locator;
readonly signupButton: Locator;
constructor(page: Page) {
this.page = page;
this.usernameInput = page.getByRole("textbox");
this.signupButton = page.getByRole("button", {
name: "Sign up",
});
}
async goto() {
this.page.goto("/");
}
async fillUsername(value: string) {
await this.usernameInput.clear();
await this.usernameInput.fill(value);
}
async loginAs(value: string) {
await this.page
.getByRole("button", {
name: value,
})
.click();
}
async signup() {
await this.signupButton.click();
}
async expectLoaded() {
await expect(this.signupButton).toBeVisible();
}
}

View File

@@ -1,164 +1,5 @@
# minimal-auth-clerk # minimal-auth-clerk
## 0.0.53
### Patch Changes
- jazz-react@0.10.9
- jazz-react-auth-clerk@0.10.9
## 0.0.52
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-react-auth-clerk@0.10.8
## 0.0.51
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react-auth-clerk@0.10.7
- jazz-react@0.10.7
- jazz-tools@0.10.7
## 0.0.50
### Patch Changes
- Updated dependencies [1d71ca1]
- Updated dependencies [ada802b]
- jazz-react-auth-clerk@0.10.6
- jazz-react@0.10.6
- jazz-tools@0.10.6
## 0.0.49
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-react@0.10.5
- jazz-react-auth-clerk@0.10.5
## 0.0.48
### Patch Changes
- jazz-react@0.10.4
- jazz-react-auth-clerk@0.10.4
- jazz-tools@0.10.4
## 0.0.47
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-react@0.10.3
- jazz-react-auth-clerk@0.10.3
## 0.0.46
### Patch Changes
- jazz-react@0.10.2
- jazz-react-auth-clerk@0.10.2
- jazz-tools@0.10.2
## 0.0.45
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-react@0.10.1
- jazz-react-auth-clerk@0.10.1
## 0.0.44
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-react-auth-clerk@0.10.0
- jazz-react@0.10.0
- jazz-tools@0.10.0
## 0.0.43
### Patch Changes
- jazz-react@0.9.23
- jazz-react-auth-clerk@0.9.23
- jazz-tools@0.9.23
## 0.0.42
### Patch Changes
- jazz-react@0.9.22
- jazz-react-auth-clerk@0.9.22
## 0.0.41
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react@0.9.21
- jazz-react-auth-clerk@0.9.21
## 0.0.40
### Patch Changes
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-react@0.9.20
- jazz-react-auth-clerk@0.9.20
## 0.0.39
### Patch Changes
- jazz-react@0.9.19
- jazz-react-auth-clerk@0.9.19
- jazz-tools@0.9.19
## 0.0.38
### Patch Changes
- jazz-react@0.9.18
- jazz-react-auth-clerk@0.9.18
- jazz-tools@0.9.18
## 0.0.37
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-react@0.9.17
- jazz-react-auth-clerk@0.9.17
## 0.0.36
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-react-auth-clerk@0.9.16
- jazz-tools@0.9.16
- jazz-react@0.9.16
## 0.0.35 ## 0.0.35
### Patch Changes ### Patch Changes

View File

@@ -15,6 +15,10 @@ You can either
Create a new Jazz project, and use this example as a template. Create a new Jazz project, and use this example as a template.
```bash ```bash
npm create jazz-app@latest --example clerk --project-name clerk
```
or
```bash
npx create-jazz-app@latest --example clerk --project-name clerk npx create-jazz-app@latest --example clerk --project-name clerk
``` ```

View File

@@ -1,7 +1,7 @@
{ {
"name": "clerk", "name": "clerk",
"private": true, "private": true,
"version": "0.0.53", "version": "0.0.35",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -13,7 +13,7 @@
"dependencies": { "dependencies": {
"@clerk/clerk-react": "^5.4.1", "@clerk/clerk-react": "^5.4.1",
"jazz-react": "workspace:*", "jazz-react": "workspace:*",
"jazz-react-auth-clerk": "workspace:0.10.9", "jazz-react-auth-clerk": "workspace:0.9.15",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1" "react-dom": "^18.3.1"
@@ -25,6 +25,6 @@
"@vitejs/plugin-react": "^4.3.3", "@vitejs/plugin-react": "^4.3.3",
"globals": "^15.11.0", "globals": "^15.11.0",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^6.0.11" "vite": "^5.4.10"
} }
} }

View File

@@ -1,25 +1,13 @@
import { SignInButton } from "@clerk/clerk-react"; import { useAccount } from "jazz-react";
import { useAccount, useIsAuthenticated } from "jazz-react";
function App() { function App() {
const { me, logOut } = useAccount(); const { me, logOut } = useAccount();
const isAuthenticated = useIsAuthenticated();
if (isAuthenticated) {
return (
<div className="container">
<h1>You're logged in</h1>
<p>Welcome back, {me?.profile?.name}</p>
<button onClick={() => logOut()}>Logout</button>
</div>
);
}
return ( return (
<div className="container"> <div className="container">
<h1>You're not logged in</h1> <h1>You're logged in</h1>
<SignInButton /> <p>Welcome back, {me?.profile?.name}</p>
<button onClick={() => logOut()}>Logout</button>
</div> </div>
); );
} }

View File

@@ -1 +0,0 @@
export const apiKey = "minimal-auth-clerk-example@garden.co";

View File

@@ -62,7 +62,7 @@ button {
} }
.container { .container {
max-width: 400px; max-width: 200px;
margin: 0 auto; margin: 0 auto;
padding: 0 1rem; padding: 0 1rem;
display: flex; display: flex;

View File

@@ -1,10 +1,10 @@
import { ClerkProvider, useClerk } from "@clerk/clerk-react"; import { ClerkProvider, SignInButton, useClerk } from "@clerk/clerk-react";
import { useJazzClerkAuth } from "jazz-react-auth-clerk";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import App from "./App.tsx"; import App from "./App.tsx";
import "./index.css"; import "./index.css";
import { JazzProviderWithClerk } from "jazz-react-auth-clerk"; import { JazzProvider } from "jazz-react";
import { apiKey } from "./apiKey";
// Import your publishable key // Import your publishable key
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY; const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
@@ -13,28 +13,35 @@ if (!PUBLISHABLE_KEY) {
throw new Error("Add your Clerk publishable key to the .env.local file"); throw new Error("Add your Clerk publishable key to the .env.local file");
} }
function JazzProvider({ children }: { children: React.ReactNode }) { function JazzAndAuth({ children }: { children: React.ReactNode }) {
const clerk = useClerk(); const clerk = useClerk();
const [auth, state] = useJazzClerkAuth(clerk);
return ( return (
<JazzProviderWithClerk <main className="container">
clerk={clerk} {state?.errors?.map((error) => (
sync={{ <div key={error}>{error}</div>
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, ))}
when: "signedUp", // This makes the app work in local mode when the user is not authenticated {clerk.user && auth ? (
}} <JazzProvider
> auth={auth}
{children} peer="wss://cloud.jazz.tools/?key=minimal-auth-clerk-example@garden.co"
</JazzProviderWithClerk> >
{children}
</JazzProvider>
) : (
<SignInButton />
)}
</main>
); );
} }
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl="/"> <ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl="/">
<JazzProvider> <JazzAndAuth>
<App /> <App />
</JazzProvider> </JazzAndAuth>
</ClerkProvider> </ClerkProvider>
</StrictMode>, </StrictMode>,
); );

View File

@@ -1,145 +1,5 @@
# file-share-svelte # file-share-svelte
## 0.0.38
### Patch Changes
- jazz-svelte@0.10.9
## 0.0.37
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-svelte@0.10.8
## 0.0.36
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-svelte@0.10.7
- jazz-tools@0.10.7
## 0.0.35
### Patch Changes
- Updated dependencies [ada802b]
- jazz-tools@0.10.6
- jazz-svelte@0.10.6
## 0.0.34
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-svelte@0.10.5
## 0.0.33
### Patch Changes
- jazz-svelte@0.10.4
- jazz-tools@0.10.4
## 0.0.32
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-svelte@0.10.3
## 0.0.31
### Patch Changes
- jazz-svelte@0.10.2
- jazz-tools@0.10.2
## 0.0.30
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-svelte@0.10.1
## 0.0.29
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-tools@0.10.0
- jazz-svelte@0.10.0
## 0.0.28
### Patch Changes
- jazz-svelte@0.9.23
- jazz-tools@0.9.23
## 0.0.27
### Patch Changes
- jazz-svelte@0.9.22
## 0.0.26
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-svelte@0.9.21
## 0.0.25
### Patch Changes
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-svelte@0.9.20
## 0.0.24
### Patch Changes
- jazz-svelte@0.9.19
- jazz-tools@0.9.19
## 0.0.23
### Patch Changes
- jazz-svelte@0.9.18
- jazz-tools@0.9.18
## 0.0.22
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-svelte@0.9.17
## 0.0.21
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-tools@0.9.16
- jazz-svelte@0.9.16
## 0.0.20 ## 0.0.20
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "file-share-svelte", "name": "file-share-svelte",
"version": "0.0.38", "version": "0.0.20",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -13,13 +13,13 @@
"lint": "prettier --check . && eslint .", "lint": "prettier --check . && eslint .",
"format-and-lint": "pnpm run format && pnpm run lint", "format-and-lint": "pnpm run format && pnpm run lint",
"format-and-lint:fix": "pnpm run format --write && pnpm run lint --fix", "format-and-lint:fix": "pnpm run format --write && pnpm run lint --fix",
"test:e2e": "playwright test", "test": "playwright test",
"test:e2e:ui": "playwright test --ui" "test:ui": "playwright test --ui"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-vercel": "^5.5.0", "@sveltejs/adapter-vercel": "^5.5.0",
"@sveltejs/kit": "^2.16.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.1",
"@types/is-ci": "^3.0.4", "@types/is-ci": "^3.0.4",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^9.7.0", "eslint": "^9.7.0",
@@ -32,10 +32,10 @@
"prettier-plugin-tailwindcss": "^0.6.5", "prettier-plugin-tailwindcss": "^0.6.5",
"svelte": "^5.0.0", "svelte": "^5.0.0",
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.15",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"typescript-eslint": "^8.0.0", "typescript-eslint": "^8.0.0",
"vite": "^6.0.11" "vite": "^5.4.10"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/typography": "^0.5.15", "@tailwindcss/typography": "^0.5.15",

View File

@@ -1 +0,0 @@
export const apiKey = "file-share-svelte@garden.co"

View File

@@ -17,6 +17,6 @@ export function formatFileSize(bytes: number): string {
* @param createdAt The creation date * @param createdAt The creation date
* @returns A unique file ID string * @returns A unique file ID string
*/ */
export function generateTempFileId(fileName: string | undefined, createdAt: Date | undefined): string { export function generateTempFileId(fileName: string, createdAt: Date): string {
return `file-${fileName ?? 'unknown'}-${createdAt?.getTime() ?? 0}`; return `file-${fileName}-${createdAt.getTime()}`;
} }

View File

@@ -12,9 +12,11 @@
import { Toaster } from 'svelte-sonner'; import { Toaster } from 'svelte-sonner';
import '../app.css'; import '../app.css';
import { FileShareAccount } from '$lib/schema'; import { FileShareAccount } from '$lib/schema';
import {apiKey} from '../apiKey';
let { children } = $props(); let { children } = $props();
const auth = usePasskeyAuth({
appName: 'File Share'
});
</script> </script>
<svelte:head> <svelte:head>
@@ -23,16 +25,21 @@
<Toaster richColors /> <Toaster richColors />
<JazzProvider {#if auth.state.state === 'ready'}
AccountSchema={FileShareAccount} <div class="fixed inset-0 flex items-center justify-center bg-gray-50/80">
sync={{ <div class="rounded-lg bg-white p-8 shadow-lg">
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, <PasskeyAuthBasicUI state={auth.state} />
when: "signedUp", </div>
}} </div>
> {/if}
<PasskeyAuthBasicUI appName="File Share"> {#if auth.current}
<JazzProvider
AccountSchema={FileShareAccount}
auth={auth.current}
peer="wss://cloud.jazz.tools/?key=file-share-svelte@garden.co"
>
<div class="min-h-screen bg-gray-100"> <div class="min-h-screen bg-gray-100">
{@render children()} {@render children()}
</div> </div>
</PasskeyAuthBasicUI> </JazzProvider>
</JazzProvider> {/if}

View File

@@ -28,7 +28,7 @@
const input = event.target as HTMLInputElement; const input = event.target as HTMLInputElement;
const files = input.files; const files = input.files;
if (!files || !files.length || !me.root?.sharedFiles || !me.root.publicGroup) return; if (!files || !files.length || !me?.root?.sharedFiles || !me.root.publicGroup) return;
const file = files[0]; const file = files[0];
const fileName = file.name; const fileName = file.name;
@@ -129,14 +129,12 @@
{#if sharedFiles.current} {#if sharedFiles.current}
{#if !(sharedFiles.current.length === 0 && uploadingFiles.size === 0)} {#if !(sharedFiles.current.length === 0 && uploadingFiles.size === 0)}
{#each [...sharedFiles.current, ...uploadingFiles.values()] as file (generateTempFileId(file?.name, file?.createdAt))} {#each [...sharedFiles.current, ...uploadingFiles.values()] as file (generateTempFileId(file?.name, file?.createdAt))}
{#if file} <FileItem
<FileItem {file}
{file} loading={uploadingFiles.has(generateTempFileId(file?.name, file?.createdAt))}
loading={uploadingFiles.has(generateTempFileId(file?.name, file?.createdAt))} onShare={shareFile}
onShare={shareFile} onDelete={deleteFile}
onDelete={deleteFile} />
/>
{/if}
{/each} {/each}
{:else} {:else}
<p class="text-center text-gray-500">No files yet</p> <p class="text-center text-gray-500">No files yet</p>

View File

@@ -1,165 +1,5 @@
# form # form
## 0.0.49
### Patch Changes
- jazz-browser-media-images@0.10.9
- jazz-react@0.10.9
## 0.0.48
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-browser-media-images@0.10.8
## 0.0.47
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-browser-media-images@0.10.7
## 0.0.46
### Patch Changes
- Updated dependencies [1d71ca1]
- Updated dependencies [ada802b]
- hash-slash@0.2.2
- jazz-react@0.10.6
- jazz-tools@0.10.6
- jazz-browser-media-images@0.10.6
## 0.0.45
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-browser-media-images@0.10.5
- jazz-react@0.10.5
## 0.0.44
### Patch Changes
- jazz-react@0.10.4
- jazz-tools@0.10.4
- jazz-browser-media-images@0.10.4
## 0.0.43
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-browser-media-images@0.10.3
- jazz-react@0.10.3
## 0.0.42
### Patch Changes
- jazz-react@0.10.2
- jazz-tools@0.10.2
- jazz-browser-media-images@0.10.2
## 0.0.41
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-browser-media-images@0.10.1
- jazz-react@0.10.1
## 0.0.40
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-react@0.10.0
- jazz-tools@0.10.0
- jazz-browser-media-images@0.10.0
## 0.0.39
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
- jazz-browser-media-images@0.9.23
## 0.0.38
### Patch Changes
- jazz-browser-media-images@0.9.22
- jazz-react@0.9.22
## 0.0.37
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser-media-images@0.9.21
- jazz-react@0.9.21
## 0.0.36
### Patch Changes
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-browser-media-images@0.9.20
- jazz-react@0.9.20
## 0.0.35
### Patch Changes
- jazz-react@0.9.19
- jazz-tools@0.9.19
- jazz-browser-media-images@0.9.19
## 0.0.34
### Patch Changes
- jazz-react@0.9.18
- jazz-tools@0.9.18
- jazz-browser-media-images@0.9.18
## 0.0.33
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-browser-media-images@0.9.17
- jazz-react@0.9.17
## 0.0.32
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-tools@0.9.16
- jazz-browser-media-images@0.9.16
- jazz-react@0.9.16
## 0.0.31 ## 0.0.31
### Patch Changes ### Patch Changes

View File

@@ -28,6 +28,10 @@ You can either
Create a new Jazz project, and use this example as a template. Create a new Jazz project, and use this example as a template.
```bash ```bash
npm create jazz-app@latest --example form --project-name form
```
or
```bash
npx create-jazz-app@latest --example form --project-name form npx create-jazz-app@latest --example form --project-name form
``` ```

View File

@@ -1,7 +1,7 @@
{ {
"name": "form", "name": "form",
"private": true, "private": true,
"version": "0.0.49", "version": "0.0.31",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -20,7 +20,7 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.9.4", "@biomejs/biome": "1.9.4",
"@playwright/test": "^1.50.1", "@playwright/test": "^1.46.1",
"@tailwindcss/forms": "^0.5.9", "@tailwindcss/forms": "^0.5.9",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
@@ -29,8 +29,8 @@
"globals": "^15.11.0", "globals": "^15.11.0",
"is-ci": "^3.0.1", "is-ci": "^3.0.1",
"postcss": "^8.4.27", "postcss": "^8.4.27",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.15",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^6.0.11" "vite": "^5.4.10"
} }
} }

View File

@@ -1,4 +1,5 @@
import { useIframeHashRouter } from "hash-slash"; import { useIframeHashRouter } from "hash-slash";
import { useAccount } from "jazz-react";
import { ID } from "jazz-tools"; import { ID } from "jazz-tools";
import { CreateOrder } from "./CreateOrder.tsx"; import { CreateOrder } from "./CreateOrder.tsx";
import { EditOrder } from "./EditOrder.tsx"; import { EditOrder } from "./EditOrder.tsx";
@@ -6,10 +7,25 @@ import { Orders } from "./Orders.tsx";
import { BubbleTeaOrder } from "./schema.ts"; import { BubbleTeaOrder } from "./schema.ts";
function App() { function App() {
const { me, logOut } = useAccount();
const router = useIframeHashRouter(); const router = useIframeHashRouter();
return ( return (
<> <>
<header>
<nav className="container py-2 border-b flex items-center justify-between">
<span>
You're logged in as <strong>{me?.profile?.name}</strong>
</span>
<button
className="bg-stone-100 py-1.5 px-3 text-sm rounded-md dark:bg-stone-900 dark:text-white"
onClick={() => logOut()}
>
Log out
</button>
</nav>
</header>
<main className="container py-8 space-y-8"> <main className="container py-8 space-y-8">
{router.route({ {router.route({
"/": () => <Orders />, "/": () => <Orders />,

View File

@@ -1 +0,0 @@
export const apiKey = "form-example@garden.co";

View File

@@ -1,11 +1,30 @@
import { JazzProvider } from "jazz-react"; import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import App from "./App.tsx"; import App from "./App.tsx";
import "./index.css"; import "./index.css";
import { apiKey } from "./apiKey";
import { JazzAccount } from "./schema.ts"; import { JazzAccount } from "./schema.ts";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
auth={auth}
peer="wss://cloud.jazz.tools/?key=form-example@garden.co"
AccountSchema={JazzAccount}
>
{children}
</JazzProvider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Form" state={authState} />
)}
</>
);
}
declare module "jazz-react" { declare module "jazz-react" {
interface Register { interface Register {
Account: JazzAccount; Account: JazzAccount;
@@ -14,13 +33,8 @@ declare module "jazz-react" {
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<JazzProvider <JazzAndAuth>
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
AccountSchema={JazzAccount}
>
<App /> <App />
</JazzProvider> </JazzAndAuth>
</StrictMode>, </StrictMode>,
); );

View File

@@ -1,7 +1,13 @@
import { expect, test } from "@playwright/test"; import { expect, test } from "@playwright/test";
import { LoginPage } from "./pages/LoginPage";
test("create and edit an order", async ({ page }) => { test("create and edit an order", async ({ page }) => {
await page.goto("/"); const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.fillUsername("Alice");
await loginPage.signup();
// start an order // start an order
await page.getByRole("link", { name: "Add new order" }).click(); await page.getByRole("link", { name: "Add new order" }).click();
await page.getByLabel("Base tea").selectOption("Oolong"); await page.getByLabel("Base tea").selectOption("Oolong");

View File

@@ -0,0 +1,40 @@
import { Locator, Page, expect } from "@playwright/test";
export class LoginPage {
readonly page: Page;
readonly usernameInput: Locator;
readonly signupButton: Locator;
constructor(page: Page) {
this.page = page;
this.usernameInput = page.getByRole("textbox");
this.signupButton = page.getByRole("button", {
name: "Sign up",
});
}
async goto() {
this.page.goto("/");
}
async fillUsername(value: string) {
await this.usernameInput.clear();
await this.usernameInput.fill(value);
}
async loginAs(value: string) {
await this.page
.getByRole("button", {
name: value,
})
.click();
}
async signup() {
await this.signupButton.click();
}
async expectLoaded() {
await expect(this.signupButton).toBeVisible();
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"root":["./src/app.tsx","./src/createorder.tsx","./src/draftindicator.tsx","./src/editorder.tsx","./src/errors.tsx","./src/linktohome.tsx","./src/orderform.tsx","./src/orderthumbnail.tsx","./src/orders.tsx","./src/main.tsx","./src/schema.ts","./src/vite-env.d.ts"],"version":"5.6.3"}

View File

@@ -1,164 +1,5 @@
# image-upload # image-upload
## 0.0.51
### Patch Changes
- jazz-browser-media-images@0.10.9
- jazz-react@0.10.9
## 0.0.50
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
- jazz-browser-media-images@0.10.8
## 0.0.49
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-browser-media-images@0.10.7
## 0.0.48
### Patch Changes
- Updated dependencies [1d71ca1]
- Updated dependencies [ada802b]
- jazz-react@0.10.6
- jazz-tools@0.10.6
- jazz-browser-media-images@0.10.6
## 0.0.47
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-browser-media-images@0.10.5
- jazz-react@0.10.5
## 0.0.46
### Patch Changes
- jazz-react@0.10.4
- jazz-tools@0.10.4
- jazz-browser-media-images@0.10.4
## 0.0.45
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-browser-media-images@0.10.3
- jazz-react@0.10.3
## 0.0.44
### Patch Changes
- jazz-react@0.10.2
- jazz-tools@0.10.2
- jazz-browser-media-images@0.10.2
## 0.0.43
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-browser-media-images@0.10.1
- jazz-react@0.10.1
## 0.0.42
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-react@0.10.0
- jazz-tools@0.10.0
- jazz-browser-media-images@0.10.0
## 0.0.41
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
- jazz-browser-media-images@0.9.23
## 0.0.40
### Patch Changes
- jazz-browser-media-images@0.9.22
- jazz-react@0.9.22
## 0.0.39
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser-media-images@0.9.21
- jazz-react@0.9.21
## 0.0.38
### Patch Changes
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-browser-media-images@0.9.20
- jazz-react@0.9.20
## 0.0.37
### Patch Changes
- jazz-react@0.9.19
- jazz-tools@0.9.19
- jazz-browser-media-images@0.9.19
## 0.0.36
### Patch Changes
- jazz-react@0.9.18
- jazz-tools@0.9.18
- jazz-browser-media-images@0.9.18
## 0.0.35
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-browser-media-images@0.9.17
- jazz-react@0.9.17
## 0.0.34
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-tools@0.9.16
- jazz-browser-media-images@0.9.16
- jazz-react@0.9.16
## 0.0.33 ## 0.0.33
### Patch Changes ### Patch Changes

View File

@@ -15,6 +15,10 @@ You can either
Create a new Jazz project, and use this example as a template. Create a new Jazz project, and use this example as a template.
```bash ```bash
npm create jazz-app@latest --example image-upload --project-name image-upload
```
or
```bash
npx create-jazz-app@latest --example image-upload --project-name image-upload npx create-jazz-app@latest --example image-upload --project-name image-upload
``` ```

View File

@@ -1,7 +1,7 @@
{ {
"name": "image-upload", "name": "image-upload",
"private": true, "private": true,
"version": "0.0.51", "version": "0.0.33",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -24,6 +24,6 @@
"@vitejs/plugin-react": "^4.3.3", "@vitejs/plugin-react": "^4.3.3",
"globals": "^15.11.0", "globals": "^15.11.0",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^6.0.11" "vite": "^5.4.10"
} }
} }

View File

@@ -1,8 +1,19 @@
import { useAccount } from "jazz-react";
import ImageUpload from "./ImageUpload.tsx"; import ImageUpload from "./ImageUpload.tsx";
function App() { function App() {
const { me, logOut } = useAccount();
return ( return (
<> <>
<header>
<nav className="container">
<span>
You're logged in as <strong>{me?.profile?.name}</strong>
</span>
<button onClick={() => logOut()}>Log out</button>
</nav>
</header>
<main className="container"> <main className="container">
<ImageUpload /> <ImageUpload />
</main> </main>

View File

@@ -46,12 +46,7 @@ export default function ImageUpload() {
) : ( ) : (
<div> <div>
<label>Upload image</label> <label>Upload image</label>
<input <input ref={inputRef} type="file" onChange={onImageChange} />
ref={inputRef}
type="file"
accept="image/png, image/jpeg, image/gif"
onChange={onImageChange}
/>
</div> </div>
)} )}
</div> </div>

View File

@@ -1 +0,0 @@
export const apiKey = "image-upload-example@garden.co";

View File

@@ -72,7 +72,8 @@ nav {
.container { .container {
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
padding: 2rem 0.75rem; padding-right: 0.75rem;
padding-left: 0.75rem;
max-width: 800px; max-width: 800px;
} }

View File

@@ -1,11 +1,30 @@
import { JazzProvider } from "jazz-react"; import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import App from "./App.tsx"; import App from "./App.tsx";
import "./index.css"; import "./index.css";
import { apiKey } from "./apiKey.ts";
import { JazzAccount } from "./schema.ts"; import { JazzAccount } from "./schema.ts";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
auth={auth}
peer="wss://cloud.jazz.tools/?key=image-upload-example@garden.co"
AccountSchema={JazzAccount}
>
{children}
</JazzProvider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Image upload" state={authState} />
)}
</>
);
}
declare module "jazz-react" { declare module "jazz-react" {
interface Register { interface Register {
Account: JazzAccount; Account: JazzAccount;
@@ -14,13 +33,8 @@ declare module "jazz-react" {
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<JazzProvider <JazzAndAuth>
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
AccountSchema={JazzAccount}
>
<App /> <App />
</JazzProvider> </JazzAndAuth>
</StrictMode>, </StrictMode>,
); );

View File

@@ -1,99 +1,5 @@
# jazz-example-inspector # jazz-example-inspector
## 0.0.109
### Patch Changes
- Updated dependencies [153dc99]
- cojson@0.10.8
- cojson-transport-ws@0.10.8
## 0.0.108
### Patch Changes
- Updated dependencies [0f83320]
- Updated dependencies [012022d]
- cojson@0.10.7
- cojson-transport-ws@0.10.7
## 0.0.107
### Patch Changes
- Updated dependencies [1d71ca1]
- Updated dependencies [5c76e37]
- hash-slash@0.2.2
- cojson@0.10.6
- cojson-transport-ws@0.10.6
## 0.0.106
### Patch Changes
- Updated dependencies [1af6072]
- cojson@0.10.4
- cojson-transport-ws@0.10.4
## 0.0.105
### Patch Changes
- Updated dependencies [cae3a9e]
- cojson@0.10.2
- cojson-transport-ws@0.10.2
## 0.0.104
### Patch Changes
- Updated dependencies [5a63cba]
- cojson@0.10.1
- cojson-transport-ws@0.10.1
## 0.0.103
### Patch Changes
- Updated dependencies [b426342]
- Updated dependencies [498954f]
- Updated dependencies [8217981]
- Updated dependencies [ac3d9fa]
- Updated dependencies [610543c]
- cojson@0.10.0
- cojson-transport-ws@0.10.0
## 0.0.102
### Patch Changes
- Updated dependencies [70c9a5d]
- cojson@0.9.23
- cojson-transport-ws@0.9.23
## 0.0.101
### Patch Changes
- Updated dependencies [14b6149]
- cojson-transport-ws@0.9.22
## 0.0.100
### Patch Changes
- Updated dependencies [6ad0a9f]
- cojson@0.9.19
- cojson-transport-ws@0.9.19
## 0.0.99
### Patch Changes
- Updated dependencies [8898b10]
- cojson@0.9.18
- cojson-transport-ws@0.9.18
## 0.0.98 ## 0.0.98
### Patch Changes ### Patch Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "jazz-inspector-app", "name": "jazz-inspector-app",
"private": true, "private": true,
"version": "0.0.109", "version": "0.0.98",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -12,13 +12,13 @@
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.4", "@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"cojson": "workspace:0.10.8", "cojson": "workspace:0.9.13",
"cojson-transport-ws": "workspace:0.10.8", "cojson-transport-ws": "workspace:0.9.13",
"hash-slash": "workspace:0.2.2", "hash-slash": "workspace:0.2.1",
"lucide-react": "^0.274.0", "lucide-react": "^0.274.0",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"react": "^18.3.1", "react": "^18.3.1",
@@ -37,8 +37,8 @@
"@vitejs/plugin-react-swc": "^3.3.2", "@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"postcss": "^8.4.27", "postcss": "^8.4.27",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.15",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^6.0.11" "vite": "^5.4.10"
} }
} }

View File

@@ -1 +0,0 @@
export const apiKey = "";

View File

@@ -5,9 +5,13 @@ import {
RawCoStream, RawCoStream,
RawCoValue, RawCoValue,
} from "cojson"; } from "cojson";
import { base64URLtoBytes } from "cojson"; import { base64URLtoBytes } from "cojson/src/base64url.ts";
import { BinaryStreamItem, BinaryStreamStart, CoStreamItem } from "cojson"; import {
import { JsonObject, JsonValue } from "cojson"; BinaryStreamItem,
BinaryStreamStart,
CoStreamItem,
} from "cojson/src/coValues/coStream.ts";
import { JsonObject, JsonValue } from "cojson/src/jsonValue.ts";
import { ArrowDownToLine } from "lucide-react"; import { ArrowDownToLine } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { PageInfo } from "./types"; import { PageInfo } from "./types";

View File

@@ -1,6 +1,6 @@
import clsx from "clsx"; import clsx from "clsx";
import { CoID, LocalNode, RawCoValue } from "cojson"; import { CoID, LocalNode, RawCoValue } from "cojson";
import { JsonObject } from "cojson"; import { JsonObject } from "cojson/src/jsonValue.ts";
import { ResolveIcon } from "./type-icon"; import { ResolveIcon } from "./type-icon";
import { PageInfo, isCoId } from "./types"; import { PageInfo, isCoId } from "./types";
import { CoMapPreview, ValueRenderer } from "./value-renderer"; import { CoMapPreview, ValueRenderer } from "./value-renderer";

View File

@@ -6,9 +6,9 @@ import {
RawAccount, RawAccount,
RawAccountID, RawAccountID,
RawCoValue, RawCoValue,
WasmCrypto,
} from "cojson"; } from "cojson";
import { createWebSocketPeer } from "cojson-transport-ws"; import { createWebSocketPeer } from "cojson-transport-ws";
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
import { Trash2 } from "lucide-react"; import { Trash2 } from "lucide-react";
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Breadcrumbs } from "./breadcrumbs"; import { Breadcrumbs } from "./breadcrumbs";
@@ -102,7 +102,6 @@ export default function CoJsonViewerApp() {
if (coValueId) { if (coValueId) {
setPage(coValueId); setPage(coValueId);
} }
setCoValueId("");
}; };
if ( if (
@@ -119,22 +118,8 @@ export default function CoJsonViewerApp() {
return ( return (
<div className="w-full h-screen bg-gray-100 p-4 overflow-hidden flex flex-col"> <div className="w-full h-screen bg-gray-100 p-4 overflow-hidden flex flex-col">
<div className="flex items-center mb-4 gap-4"> <div className="flex justify-between items-center mb-4">
<Breadcrumbs path={path} onBreadcrumbClick={goToIndex} /> <Breadcrumbs path={path} onBreadcrumbClick={goToIndex} />
<div className="flex-1">
<form onSubmit={handleCoValueIdSubmit}>
{path.length !== 0 && (
<input
className="border p-2 rounded-lg min-w-[21rem] font-mono"
placeholder="co_z1234567890abcdef123456789"
value={coValueId}
onChange={(e) =>
setCoValueId(e.target.value as CoID<RawCoValue>)
}
/>
)}
</form>
</div>
<AccountSwitcher <AccountSwitcher
accounts={accounts} accounts={accounts}
currentAccount={currentAccount} currentAccount={currentAccount}
@@ -187,6 +172,7 @@ export default function CoJsonViewerApp() {
type="button" type="button"
className="border inline-block px-2 py-1.5 text-black rounded" className="border inline-block px-2 py-1.5 text-black rounded"
onClick={() => { onClick={() => {
setCoValueId(currentAccount.id);
setPage(currentAccount.id); setPage(currentAccount.id);
}} }}
> >

View File

@@ -1,5 +1,5 @@
import { CoID, LocalNode, RawCoValue } from "cojson"; import { CoID, LocalNode, RawCoValue } from "cojson";
import { JsonObject } from "cojson"; import { JsonObject } from "cojson/src/jsonValue.ts";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { LinkIcon } from "../link-icon"; import { LinkIcon } from "../link-icon";
import { PageInfo } from "./types"; import { PageInfo } from "./types";

View File

@@ -18,8 +18,6 @@ export function ValueRenderer({
compact?: boolean; compact?: boolean;
onCoIDClick?: (childNode: CoID<RawCoValue>) => void; onCoIDClick?: (childNode: CoID<RawCoValue>) => void;
}) { }) {
const [isExpanded, setIsExpanded] = useState(false);
if (typeof json === "undefined" || json === undefined) { if (typeof json === "undefined" || json === undefined) {
return <span className="text-gray-400">undefined</span>; return <span className="text-gray-400">undefined</span>;
} }
@@ -87,31 +85,15 @@ export function ValueRenderer({
return ( return (
<span <span
title={JSON.stringify(json, null, 2)} title={JSON.stringify(json, null, 2)}
className="inline-block max-w-64" className="inline-block max-w-64 truncate"
> >
{compact ? ( {compact ? (
<span> <span>
Object{" "} Object{" "}
<span className="text-gray-500">({Object.keys(json).length})</span> <span className="text-gray-500">({Object.keys(json).length})</span>
<pre className="mt-1 text-sm whitespace-pre-wrap">
{isExpanded
? JSON.stringify(json, null, 2)
: JSON.stringify(json, null, 2)
.split("\n")
.slice(0, 3)
.join("\n") + (Object.keys(json).length > 2 ? "\n..." : "")}
</pre>
<button
onClick={() => setIsExpanded(!isExpanded)}
className="text-xs text-gray-500 hover:text-gray-700"
>
{isExpanded ? "Show less" : "Show more"}
</button>
</span> </span>
) : ( ) : (
<pre className="whitespace-pre-wrap"> JSON.stringify(json, null, 2)
{JSON.stringify(json, null, 2)}
</pre>
)} )}
</span> </span>
); );

View File

@@ -1,169 +1,5 @@
# jazz-example-musicplayer # jazz-example-musicplayer
## 0.0.75
### Patch Changes
- jazz-react@0.10.9
## 0.0.74
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-inspector@0.10.8
- jazz-react@0.10.8
## 0.0.73
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
- jazz-inspector@0.10.7
## 0.0.72
### Patch Changes
- Updated dependencies [1d71ca1]
- Updated dependencies [ada802b]
- jazz-react@0.10.6
- jazz-tools@0.10.6
- jazz-inspector@0.10.6
## 0.0.71
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-inspector@0.10.5
- jazz-react@0.10.5
## 0.0.70
### Patch Changes
- jazz-inspector@0.10.4
- jazz-react@0.10.4
- jazz-tools@0.10.4
## 0.0.69
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-inspector@0.10.3
- jazz-react@0.10.3
## 0.0.68
### Patch Changes
- jazz-inspector@0.10.2
- jazz-react@0.10.2
- jazz-tools@0.10.2
## 0.0.67
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-inspector@0.10.1
- jazz-react@0.10.1
## 0.0.66
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-inspector@0.10.0
- jazz-react@0.10.0
- jazz-tools@0.10.0
## 0.0.65
### Patch Changes
- jazz-inspector@0.9.23
- jazz-react@0.9.23
- jazz-tools@0.9.23
## 0.0.64
### Patch Changes
- jazz-react@0.9.22
## 0.0.63
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-inspector@0.9.22
- jazz-react@0.9.21
## 0.0.62
### Patch Changes
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-inspector@0.9.21
- jazz-react@0.9.20
## 0.0.61
### Patch Changes
- jazz-inspector@0.9.20
- jazz-react@0.9.19
- jazz-tools@0.9.19
## 0.0.60
### Patch Changes
- Updated dependencies [2faf22f]
- jazz-inspector@0.9.19
## 0.0.59
### Patch Changes
- jazz-inspector@0.9.18
- jazz-react@0.9.18
- jazz-tools@0.9.18
## 0.0.58
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-inspector@0.9.17
- jazz-react@0.9.17
## 0.0.57
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-tools@0.9.16
- jazz-inspector@0.9.16
- jazz-react@0.9.16
## 0.0.56 ## 0.0.56
### Patch Changes ### Patch Changes

View File

@@ -13,6 +13,10 @@ You can either
Create a new Jazz project, and use this example as a template. Create a new Jazz project, and use this example as a template.
```bash ```bash
npm create jazz-app@latest --example music-player --project-name music-player
```
or
```bash
npx create-jazz-app@latest --example music-player --project-name music-player npx create-jazz-app@latest --example music-player --project-name music-player
``` ```

View File

@@ -1,7 +1,7 @@
{ {
"name": "jazz-example-music-player", "name": "jazz-example-music-player",
"private": true, "private": true,
"version": "0.0.75", "version": "0.0.56",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -9,21 +9,18 @@
"format-and-lint": "biome check .", "format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write", "format-and-lint:fix": "biome check . --write",
"preview": "vite preview", "preview": "vite preview",
"test:e2e": "playwright test", "test": "playwright test",
"test:e2e:ui": "playwright test --ui" "test:ui": "playwright test --ui"
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.1", "@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-toast": "^1.1.4", "@radix-ui/react-toast": "^1.1.4",
"@radix-ui/react-tooltip": "^1.1.6",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"jazz-react": "workspace:0.9.15",
"jazz-tools": "workspace:0.9.15",
"jazz-inspector": "workspace:*", "jazz-inspector": "workspace:*",
"jazz-react": "workspace:0.10.9",
"jazz-tools": "workspace:0.10.8",
"lucide-react": "^0.274.0", "lucide-react": "^0.274.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
@@ -33,14 +30,14 @@
"tailwindcss-animate": "^1.0.7" "tailwindcss-animate": "^1.0.7"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.50.1", "@playwright/test": "^1.46.1",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"@vitejs/plugin-react-swc": "^3.3.2", "@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"postcss": "^8.4.27", "postcss": "^8.4.27",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.15",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^6.0.11" "vite": "^5.4.10"
} }
} }

View File

@@ -36,8 +36,6 @@ export class MusicTrack extends CoMap {
*/ */
file = co.ref(FileStream); file = co.ref(FileStream);
waveform = co.ref(MusicTrackWaveform); waveform = co.ref(MusicTrackWaveform);
isExampleTrack = co.optional.boolean;
} }
export class MusicTrackWaveform extends CoMap { export class MusicTrackWaveform extends CoMap {
@@ -88,20 +86,28 @@ export class MusicaAccount extends Account {
* You can use it to set up the account root and any other initial CoValues you need. * You can use it to set up the account root and any other initial CoValues you need.
*/ */
migrate() { migrate() {
if (this.root === undefined) { if (!this._refs.root) {
const tracks = ListOfTracks.create([]); const ownership = { owner: this };
const rootPlaylist = Playlist.create({
tracks,
title: "",
});
this.root = MusicaAccountRoot.create({ const tracks = ListOfTracks.create([], ownership);
rootPlaylist, const rootPlaylist = Playlist.create(
playlists: ListOfPlaylists.create([]), {
activeTrack: null, tracks,
activePlaylist: rootPlaylist, title: "",
exampleDataLoaded: false, },
}); ownership,
);
this.root = MusicaAccountRoot.create(
{
rootPlaylist,
playlists: ListOfPlaylists.create([], ownership),
activeTrack: null,
activePlaylist: rootPlaylist,
exampleDataLoaded: false,
},
ownership,
);
} }
} }
} }

View File

@@ -11,9 +11,7 @@ import { PlayerControls } from "./components/PlayerControls";
import "./index.css"; import "./index.css";
import { MusicaAccount } from "@/1_schema"; import { MusicaAccount } from "@/1_schema";
import { apiKey } from "@/apiKey.ts"; import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { JazzProvider } from "jazz-react";
import { onAnonymousAccountDiscarded } from "./4_actions";
import { useUploadExampleData } from "./lib/useUploadExampleData"; import { useUploadExampleData } from "./lib/useUploadExampleData";
/** /**
@@ -56,10 +54,30 @@ function Main() {
); );
} }
const peer = function JazzAndAuth({ children }: { children: React.ReactNode }) {
(new URL(window.location.href).searchParams.get( const [auth, state] = useDemoAuth();
"peer",
) as `ws://${string}`) ?? `wss://cloud.jazz.tools/?key=${apiKey}`; const peer =
(new URL(window.location.href).searchParams.get(
"peer",
) as `ws://${string}`) ??
"wss://cloud.jazz.tools/?key=music-player-example-jazz@garden.co";
return (
<>
<JazzProvider
storage="indexedDB"
auth={auth}
peer={peer}
AccountSchema={MusicaAccount}
>
{children}
<JazzInspector />
</JazzProvider>
<DemoAuthBasicUI appName="Jazz Music Player" state={state} />
</>
);
}
declare module "jazz-react" { declare module "jazz-react" {
interface Register { interface Register {
@@ -69,18 +87,8 @@ declare module "jazz-react" {
ReactDOM.createRoot(document.getElementById("root")!).render( ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode> <React.StrictMode>
<JazzProvider <JazzAndAuth>
sync={{
peer,
when: "signedUp", // This makes the app work in local mode when the user is anonymous
}}
storage="indexedDB"
AccountSchema={MusicaAccount}
defaultProfileName="Anonymous unicorn"
onAnonymousAccountDiscarded={onAnonymousAccountDiscarded}
>
<Main /> <Main />
<JazzInspector /> </JazzAndAuth>
</JazzProvider>
</React.StrictMode>, </React.StrictMode>,
); );

View File

@@ -1,17 +1,12 @@
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { import { createInviteLink, useAccount, useCoState } from "jazz-react";
createInviteLink,
useAccount,
useCoState,
useIsAuthenticated,
} from "jazz-react";
import { ID } from "jazz-tools"; import { ID } from "jazz-tools";
import { useNavigate, useParams } from "react-router"; import { useNavigate, useParams } from "react-router";
import { Playlist } from "./1_schema"; import { Playlist } from "./1_schema";
import { createNewPlaylist, uploadMusicTracks } from "./4_actions"; import { createNewPlaylist, uploadMusicTracks } from "./4_actions";
import { MediaPlayer } from "./5_useMediaPlayer"; import { MediaPlayer } from "./5_useMediaPlayer";
import { AuthButton } from "./components/AuthButton";
import { FileUploadButton } from "./components/FileUploadButton"; import { FileUploadButton } from "./components/FileUploadButton";
import { LogoutButton } from "./components/LogoutButton";
import { MusicTrackRow } from "./components/MusicTrackRow"; import { MusicTrackRow } from "./components/MusicTrackRow";
import { PlaylistTitleInput } from "./components/PlaylistTitleInput"; import { PlaylistTitleInput } from "./components/PlaylistTitleInput";
import { SidePanel } from "./components/SidePanel"; import { SidePanel } from "./components/SidePanel";
@@ -71,8 +66,6 @@ export function HomePage({ mediaPlayer }: { mediaPlayer: MediaPlayer }) {
}); });
}; };
const isAuthenticated = useIsAuthenticated();
return ( return (
<div className="flex flex-col h-screen text-gray-800 bg-blue-50"> <div className="flex flex-col h-screen text-gray-800 bg-blue-50">
<div className="flex flex-1 overflow-hidden"> <div className="flex flex-1 overflow-hidden">
@@ -93,12 +86,12 @@ export function HomePage({ mediaPlayer }: { mediaPlayer: MediaPlayer }) {
<Button onClick={handleCreatePlaylist}>New playlist</Button> <Button onClick={handleCreatePlaylist}>New playlist</Button>
</> </>
)} )}
{!isRootPlaylist && isAuthenticated && ( {!isRootPlaylist && (
<Button onClick={handlePlaylistShareClick}> <Button onClick={handlePlaylistShareClick}>
Share playlist Share playlist
</Button> </Button>
)} )}
<AuthButton /> <LogoutButton />
</div> </div>
</div> </div>
<ul className="flex flex-col"> <ul className="flex flex-col">

View File

@@ -22,18 +22,18 @@ import {
* pattern that best fits your app. * pattern that best fits your app.
*/ */
export async function uploadMusicTracks( export async function uploadMusicTracks(files: Iterable<File>) {
files: Iterable<File>, const me = await MusicaAccount.getMe().ensureLoaded({
isExampleTrack: boolean = false,
) {
const { root } = await MusicaAccount.getMe().ensureLoaded({
root: { root: {
rootPlaylist: { rootPlaylist: {
tracks: [], tracks: [],
}, },
playlists: [],
}, },
}); });
if (!me) return;
for (const file of files) { for (const file of files) {
// The ownership object defines the user that owns the created coValues // The ownership object defines the user that owns the created coValues
// We are creating a group for each CoValue in order to be able to share them via Playlist // We are creating a group for each CoValue in order to be able to share them via Playlist
@@ -52,24 +52,25 @@ export async function uploadMusicTracks(
duration: data.duration, duration: data.duration,
waveform: MusicTrackWaveform.create({ data: data.waveform }, group), waveform: MusicTrackWaveform.create({ data: data.waveform }, group),
title: file.name, title: file.name,
isExampleTrack,
}, },
group, group,
); );
// The newly created musicTrack can be associated to the // The newly created musicTrack can be associated to the
// user track list using a simple push call // user track list using a simple push call
root.rootPlaylist.tracks.push(musicTrack); me.root.rootPlaylist.tracks.push(musicTrack);
} }
} }
export async function createNewPlaylist() { export async function createNewPlaylist() {
const { root } = await MusicaAccount.getMe().ensureLoaded({ const me = await MusicaAccount.getMe().ensureLoaded({
root: { root: {
playlists: [], playlists: [],
}, },
}); });
if (!me) throw new Error("Current playlist not resolved");
// Since playlists are meant to be shared we associate them // Since playlists are meant to be shared we associate them
// to a group which will contain the keys required to get // to a group which will contain the keys required to get
// access to the "owned" values // access to the "owned" values
@@ -85,7 +86,7 @@ export async function createNewPlaylist() {
// Again, we associate the new playlist to the // Again, we associate the new playlist to the
// user by pushing it into the playlists CoList // user by pushing it into the playlists CoList
root.playlists.push(playlist); me.root.playlists.push(playlist);
return playlist; return playlist;
} }
@@ -151,49 +152,24 @@ export async function updateMusicTrackTitle(track: MusicTrack, title: string) {
} }
export async function updateActivePlaylist(playlist?: Playlist) { export async function updateActivePlaylist(playlist?: Playlist) {
const { root } = await MusicaAccount.getMe().ensureLoaded({ const me = await MusicaAccount.getMe().ensureLoaded({
root: { root: {
activePlaylist: {}, activePlaylist: {},
rootPlaylist: {}, rootPlaylist: {},
}, },
}); });
root.activePlaylist = playlist ?? root.rootPlaylist; if (!me) return;
me.root.activePlaylist = playlist ?? me.root.rootPlaylist;
} }
export async function updateActiveTrack(track: MusicTrack) { export async function updateActiveTrack(track: MusicTrack) {
const { root } = await MusicaAccount.getMe().ensureLoaded({ const me = await MusicaAccount.getMe().ensureLoaded({
root: {}, root: {},
}); });
root.activeTrack = track; if (!me) return;
}
me.root.activeTrack = track;
export async function onAnonymousAccountDiscarded(
anonymousAccount: MusicaAccount,
) {
const { root: anonymousAccountRoot } = await anonymousAccount.ensureLoaded({
root: {
rootPlaylist: {
tracks: [{}],
},
},
});
const me = await MusicaAccount.getMe().ensureLoaded({
root: {
rootPlaylist: {
tracks: [],
},
},
});
for (const track of anonymousAccountRoot.rootPlaylist.tracks) {
if (track.isExampleTrack) continue;
const trackGroup = track._owner.castAs(Group);
trackGroup.addMember(me, "admin");
me.root.rootPlaylist.tracks.push(track);
}
} }

View File

@@ -19,6 +19,8 @@ export function InvitePage() {
}, },
}); });
if (!me) return;
if ( if (
playlist && playlist &&
!me.root.playlists.some((item) => playlist.id === item?.id) !me.root.playlists.some((item) => playlist.id === item?.id)

View File

@@ -1 +0,0 @@
export const apiKey = "music-player-example-jazz@garden.co";

Some files were not shown because too many files have changed in this diff Show More