Compare commits

..

24 Commits

Author SHA1 Message Date
Emil Sayahi
c8eb75dd1f test: missing service
todo:
- tests for worker accounts (`packages/jazz-nodejs/src/test/startWorker.test.ts`)
  - should include test ensuring a new worker account has service
- tests for `useServiceSender` (`packages/jazz-react-core/src/tests/useServiceSender.test.ts`)
- migration guide
- documentation
2025-06-09 16:34:27 -07:00
Emil Sayahi
61170900bd fix: Service E2E test 2025-06-06 15:24:07 -07:00
Emil Sayahi
4f2f889542 fix: service not created for all accounts
todo: test example apps
2025-06-06 14:48:14 -07:00
Emil Sayahi
719974c46c start making service optional
bug: not including it still makes the service. need to detect if it's present or not
2025-06-06 10:49:58 -07:00
Emil Sayahi
8845ee8601 chore: changeset 2025-06-05 20:47:04 -07:00
Emil Sayahi
42cc41d0cb feat: rename inbox to service
todo:
- review docs
- changeset
- mark ready for review
2025-06-05 20:39:09 -07:00
Emil Sayahi
7a25c49480 fix: group inheritance tests
todo:
- rename inbox
- update docs
- changeset
- mark ready for review
2025-06-05 19:50:26 -07:00
Emil Sayahi
aa202ad175 fix: 'authenticate to an existing account after immediately close the creation node' test 2025-06-05 19:43:21 -07:00
Emil Sayahi
1d3e5c688d fix: 'create a new account' test 2025-06-05 19:40:04 -07:00
Emil Sayahi
cbf7c2bbd8 fix: some sync tests
todo:
- fix more tests
- rename inbox
- update docs
- changeset
- mark ready for review
2025-06-05 19:14:22 -07:00
Emil Sayahi
e5ae4ffb18 remove console.logs
todo:
- fix test failures
- rename inbox
- update docs
- changeset
- mark ready for review
2025-06-05 18:30:11 -07:00
Emil Sayahi
b393c81ce5 fix: inbox visibility to senders
todo:
- remove console.logs
- rename inbox
- update docs
- changeset
- mark ready for review
2025-06-05 18:14:26 -07:00
Emil Sayahi
3c5d9d6f73 inbox.inbox not visible to others
the `inbox` key on raw account *is* set for other users, but the `inbox` property inside of it is missing … still investigating
2025-06-05 17:28:22 -07:00
Emil Sayahi
23115af9c3 fix: inbox key set for self with trusting
problem remains that `inbox` key on raw account is unset for other users

not sure why. maybe not loaded?
2025-06-05 17:09:03 -07:00
Emil Sayahi
cd55fa9fe2 Merge branch 'main' into emil/inbox-changes 2025-06-05 16:16:43 -07:00
Emil Sayahi
9336eaf123 Update account.ts 2025-06-05 16:16:35 -07:00
Emil Sayahi
a6caeea7ac trying to debug
problem is `inbox` key on raw account is unset for everyone (if `trusting`) or is unset for other users (if `private`)

still not certain why
2025-06-05 16:16:18 -07:00
Emil Sayahi
a8f1e6507a Merge branch 'main' into emil/inbox-changes 2025-06-05 11:16:18 -07:00
Emil Sayahi
e2b6ca6f69 fix: merge issue 2025-06-03 14:23:10 -07:00
Emil Sayahi
79dd8bf805 Merge branch 'main' into emil/inbox-changes 2025-06-03 14:16:37 -07:00
Emil Sayahi
da0adbbe9f inbox in RawAccount 2025-06-03 14:16:31 -07:00
Emil Sayahi
2164e6eb36 wip: remove inbox invitations 2025-06-03 13:17:17 -07:00
Emil Sayahi
a976c16b7c Merge branch 'main' into emil/inbox-changes 2025-06-03 09:36:53 -07:00
Emil Sayahi
6f2b79e79a feat: move inbox refs from Profile to Account
see #1297
2025-06-03 07:44:34 -07:00
1218 changed files with 63871 additions and 20189 deletions

View File

@@ -10,15 +10,36 @@
"cojson-storage-indexeddb",
"cojson-storage-sqlite",
"cojson-transport-ws",
"jazz-browser",
"jazz-auth-clerk",
"jazz-auth-betterauth",
"jazz-betterauth-client-plugin",
"jazz-betterauth-server-plugin",
"jazz-react-auth-betterauth",
"jazz-browser-media-images",
"jazz-expo",
"jazz-inspector",
"jazz-inspector-element",
"jazz-nodejs",
"jazz-react",
"jazz-react-core",
"jazz-react-auth-clerk",
"jazz-react-native-core",
"jazz-react-native",
"jazz-react-native-media-images",
"jazz-richtext-prosemirror",
"jazz-richtext-tiptap",
"jazz-run",
"jazz-tools"
"jazz-svelte",
"jazz-tools",
"jazz-vue"
]
],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "minor"
"updateInternalDependencies": "patch",
"ignore": [],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
}
}

View File

@@ -0,0 +1,5 @@
---
"cojson": patch
---
fix: clarify `Group.addMember` error message when attempting to set roles with insufficient permissions

37
.github/workflows/build-examples.yaml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Build Examples
on:
push:
branches: [ "main" ]
jobs:
build-examples:
runs-on: blacksmith-4vcpu-ubuntu-2204
strategy:
matrix:
example: [
"chat",
"clerk",
"passkey",
"inspector",
"music-player",
"password-manager",
"pets",
"reactions",
"todo",
]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Pnpm Build
run: |
pnpm install
pnpm turbo build;
working-directory: ./examples/${{ matrix.example }}

26
.github/workflows/build-starters.yaml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Build Starters
on:
push:
branches: ["main"]
jobs:
build-starters:
runs-on: blacksmith-4vcpu-ubuntu-2204
strategy:
matrix:
starter: ["react-passkey-auth"]
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Pnpm Build
run: |
pnpm install
pnpm turbo build;
working-directory: ./starters/${{ matrix.starter }}

View File

@@ -2,21 +2,18 @@ name: Code quality
on:
push:
branches:
- "main"
pull_request:
jobs:
quality:
runs-on: blacksmith-2vcpu-ubuntu-2404-arm
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Biome
uses: biomejs/setup-biome@v2
with:
version: 1.9.4
version: latest
- name: Run Biome
run: biome ci .

View File

@@ -60,8 +60,7 @@ jobs:
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-expo/
# killall due to this issue: https://github.com/ReactiveCircus/android-emulator-runner/issues/385
script: ./test/e2e/run.sh && killall -INT crashpad_handler || true
script: ./test/e2e/run.sh
- name: Copy Maestro Output
if: steps.e2e_test.outcome != 'success'

View File

@@ -8,7 +8,7 @@ on:
jobs:
test:
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: blacksmith-4vcpu-ubuntu-2204
timeout-minutes: 5
steps:

View File

@@ -9,7 +9,10 @@ on:
jobs:
test:
timeout-minutes: 60
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: blacksmith-4vcpu-ubuntu-2204
strategy:
matrix:
project: ["homepage/homepage"]
steps:
- uses: actions/checkout@v4
@@ -19,28 +22,25 @@ jobs:
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Install root dependencies
run: pnpm install && pnpm exec turbo build --filter="./packages/*"
- name: Install project dependencies
run: pnpm install
working-directory: ./homepage/homepage
working-directory: ./${{ matrix.project }}
- name: Pnpm Build
run: pnpm exec turbo build
working-directory: ./homepage/homepage
run: pnpm turbo build
working-directory: ./${{ matrix.project }}
- name: Install Playwright Browsers
run: pnpm exec playwright install
working-directory: ./homepage/homepage
working-directory: ./${{ matrix.project }}
- name: Run Playwright tests
run: pnpm exec playwright test
working-directory: ./homepage/homepage
working-directory: ./${{ matrix.project }}
- uses: actions/upload-artifact@v4
if: failure()
with:
name: homepage-playwright-report
path: ./homepage/homepage/playwright-report/
name: ${{ hashFiles(format('{0}/package.json', matrix.project)) }}-playwright-report
path: ./${{ matrix.project }}/playwright-report/
retention-days: 30

View File

@@ -9,25 +9,11 @@ on:
jobs:
test:
timeout-minutes: 60
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: blacksmith-4vcpu-ubuntu-2204
continue-on-error: true
strategy:
matrix:
project: [
"tests/e2e",
"examples/chat",
"examples/chat-svelte",
"examples/clerk",
"examples/betterauth",
"examples/file-share-svelte",
"examples/form",
"examples/inspector",
"examples/music-player",
"examples/organization",
"starters/react-passkey-auth",
"starters/svelte-passkey-auth",
"tests/jazz-svelte"
]
project: ["tests/e2e", "examples/chat", "examples/clerk", "examples/betterauth", "examples/file-share-svelte", "examples/form", "examples/music-player", "examples/organization", "examples/pets", "starters/react-passkey-auth"]
steps:
- uses: actions/checkout@v4
@@ -38,11 +24,7 @@ jobs:
uses: ./.github/actions/source-code/
- name: Pnpm Build
run: |
if [ -f .env.test ]; then
cp .env.test .env
fi
pnpm turbo build
run: pnpm turbo build
working-directory: ./${{ matrix.project }}
- name: Install Playwright Browsers

View File

@@ -17,7 +17,7 @@ concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- name: Checkout Repo
uses: actions/checkout@v4

View File

@@ -9,7 +9,7 @@ on:
jobs:
unit-tests:
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- name: Checkout

View File

@@ -11,9 +11,8 @@
"jazz-tools.json",
"**/ios/**",
"**/android/**",
"tests/jazz-svelte/src/**",
"packages/jazz-svelte/**",
"examples/*svelte*/**",
"starters/*svelte*/**",
"examples/jazz-paper-scissors/src/routeTree.gen.ts",
"homepage/homepage/**",
"**/package.json"

View File

@@ -36,10 +36,8 @@ yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env
.env.*
!.env.example
!.env.test
.env*
!.env
# vercel
.vercel

View File

@@ -1,17 +1,5 @@
# betterauth
## 0.1.25
### Patch Changes
- Updated dependencies [048ac1d]
- jazz-tools@0.14.22
- jazz-betterauth-server-plugin@0.14.22
- jazz-inspector@0.14.22
- jazz-react@0.14.22
- jazz-react-auth-betterauth@0.14.22
- jazz-betterauth-client-plugin@0.14.22
## 0.1.24
### Patch Changes

View File

@@ -5,12 +5,10 @@ This example demonstrates how to integrate [Better Auth](https://www.better-auth
## Getting started
To run this example, you may either:
- Clone the Jazz monorepo and run this example from within.
- Create a new Jazz project using this example as a template, and run that new project.
* Clone the Jazz monorepo and run this example from within.
* Create a new Jazz project using this example as a template, and run that new project.
### Setting environment variables
- `NEXT_PUBLIC_AUTH_BASE_URL`: A URL to a Better Auth server. If undefined, the example will self-host a Better Auth server.
- `BETTER_AUTH_SECRET`: The encryption secret used by the self-hosted Better Auth server (required only if `NEXT_PUBLIC_AUTH_BASE_URL` is undefined)
- `GITHUB_CLIENT_ID`: The client ID for the GitHub OAuth provider used by the self-hosted Better Auth server (required only if `NEXT_PUBLIC_AUTH_BASE_URL` is undefined)
@@ -19,64 +17,38 @@ To run this example, you may either:
### Using this example as a template
1. Create a new Jazz project, and use this example as a template.
```sh
npx create-jazz-app@latest betterauth-app --example betterauth
```
2. Navigate to the new project and install dependencies.
2. Navigate to the new project and start the development server.
```sh
cd betterauth-app
pnpm install
```
3. Create a .env file (don't forget to set your [BETTER_AUTH_SECRET](https://www.better-auth.com/docs/installation#set-environment-variables)!)
```sh
mv .env.example .env
```
4. Start the development server
```sh
pnpm dev
```
https://www.better-auth.com/docs/installation#set-environment-variables
### Using the monorepo
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/betterauth/
```
Create a .env file (don't forget to set your [BETTER_AUTH_SECRET](https://www.better-auth.com/docs/installation#set-environment-variables)!)
```sh
mv .env.example .env
```
Start the dev server.
```bash
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

View File

@@ -1,5 +1,6 @@
{
"name": "betterauth",
"version": "0.1.24",
"private": true,
"type": "module",
"scripts": {
@@ -21,9 +22,11 @@
"better-sqlite3": "^11.9.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"jazz-react-auth-betterauth": "workspace:*",
"jazz-betterauth-client-plugin": "workspace:*",
"jazz-betterauth-server-plugin": "workspace:*",
"jazz-inspector": "workspace:*",
"jazz-react": "workspace:*",
"jazz-react-auth-betterauth": "workspace:*",
"jazz-tools": "workspace:*",
"lucide-react": "^0.510.0",
"next": "15.3.2",

View File

@@ -1,8 +1,8 @@
"use client";
import { Button } from "@/components/ui/button";
import { useAccount } from "jazz-react";
import { Account } from "jazz-tools";
import { useAccount } from "jazz-tools/react";
import {
AppWindowMacIcon,
FileTextIcon,

View File

@@ -1,21 +1,21 @@
"use client";
import { JazzProvider } from "jazz-react";
import { AuthProvider } from "jazz-react-auth-betterauth";
import { JazzReactProvider } from "jazz-tools/react";
import { type ReactNode, lazy } from "react";
const JazzDevTools =
process.env.NODE_ENV === "production"
? () => null
: lazy(() =>
import("jazz-tools/inspector").then((res) => ({
import("jazz-inspector").then((res) => ({
default: res.JazzInspector,
})),
);
export function JazzAndAuth({ children }: { children: ReactNode }) {
return (
<JazzReactProvider
<JazzProvider
sync={{
peer: "wss://cloud.jazz.tools/?key=betterauth-example@garden.co",
}}
@@ -28,6 +28,6 @@ export function JazzAndAuth({ children }: { children: ReactNode }) {
{children}
</AuthProvider>
<JazzDevTools />
</JazzReactProvider>
</JazzProvider>
);
}

View File

@@ -1,9 +1,9 @@
"use client";
import { Button } from "@/components/ui/button";
import { useAccount, useIsAuthenticated } from "jazz-react";
import { useAuth } from "jazz-react-auth-betterauth";
import { Account } from "jazz-tools";
import { useAccount, useIsAuthenticated } from "jazz-tools/react";
import Image from "next/image";
import Link from "next/link";
import { useCallback } from "react";

View File

@@ -1,8 +1,8 @@
"use client";
import { Button } from "@/components/ui/button";
import { useAccount, useIsAuthenticated } from "jazz-react";
import { useAuth } from "jazz-react-auth-betterauth";
import { useAccount, useIsAuthenticated } from "jazz-tools/react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { toast } from "sonner";

View File

@@ -0,0 +1 @@
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_ZXZpZGVudC1kYW5lLTg5LmNsZXJrLmFjY291bnRzLmRldiQ

View File

@@ -1 +0,0 @@
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=

View File

@@ -15,9 +15,3 @@ web-build/
ios
android
# Env
.env
.env.*
!.env.example
!.env.test

View File

@@ -1,14 +1,5 @@
# chat-rn-expo-clerk
## 1.0.145
### Patch Changes
- Updated dependencies [048ac1d]
- jazz-tools@0.14.22
- jazz-expo@0.14.22
- jazz-react-native-media-images@0.14.22
## 1.0.144
### Patch Changes

View File

@@ -8,12 +8,9 @@ First, install dependencies and build the project:
```bash
pnpm i
mv .env.example .env
pnpm run build
```
Don't forget to update `VITE_CLERK_PUBLISHABLE_KEY` in `.env` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
### 2. Inside the `examples/chat-rn-expo-clerk` Directory
Next, navigate to the specific example project and run the following commands:

View File

@@ -1,5 +1,5 @@
import { Redirect, Stack } from "expo-router";
import { useIsAuthenticated } from "jazz-tools/expo";
import { useIsAuthenticated } from "jazz-expo";
import React from "react";
export default function HomeLayout() {

View File

@@ -1,5 +1,5 @@
import { Redirect, Stack } from "expo-router";
import { useIsAuthenticated } from "jazz-tools/expo";
import { useIsAuthenticated } from "jazz-expo";
export default function UnAuthenticatedLayout() {
const isAuthenticated = useIsAuthenticated();

View File

@@ -4,7 +4,7 @@ import { secureStore } from "@clerk/clerk-expo/secure-store";
import { useFonts } from "expo-font";
import { Slot, useRouter, useSegments } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
import { useIsAuthenticated, useJazzContext } from "jazz-tools/expo";
import { useIsAuthenticated, useJazzContext } from "jazz-expo";
import React, { useEffect } from "react";
import { tokenCache } from "../cache";
import { JazzAndAuth } from "../src/auth-context";

View File

@@ -4,10 +4,10 @@ import clsx from "clsx";
import * as Clipboard from "expo-clipboard";
import * as ImagePicker from "expo-image-picker";
import { useLocalSearchParams } from "expo-router";
import { useAccount, useCoState } from "jazz-expo";
import { ProgressiveImg } from "jazz-expo";
import { createImage } from "jazz-react-native-media-images";
import { CoPlainText, Group, Loaded } from "jazz-tools";
import { useAccount, useCoState } from "jazz-tools/expo";
import { ProgressiveImgNative } from "jazz-tools/expo";
import { createImageNative } from "jazz-tools/react-native-media-images";
import { useEffect, useLayoutEffect, useState } from "react";
import React, {
SafeAreaView,
@@ -104,7 +104,7 @@ export default function Conversation() {
setIsUploading(true);
const base64Uri = `data:image/jpeg;base64,${result.assets[0].base64}`;
const image = await createImageNative(base64Uri, {
const image = await createImage(base64Uri, {
owner: chat._owner,
maxSize: 2048,
});
@@ -149,17 +149,15 @@ export default function Conversation() {
)}
>
{item.image && (
<ProgressiveImgNative
image={item.image}
maxWidth={1024}
children={({ src }) => (
<ProgressiveImg image={item.image} maxWidth={1024}>
{({ src, res, originalSize }) => (
<Image
source={{ uri: src }}
className="w-48 h-48 rounded-lg mb-2"
resizeMode="cover"
/>
)}
/>
</ProgressiveImg>
)}
{item.text && (
<Text

View File

@@ -11,7 +11,7 @@ import React, {
} from "react-native";
import { useUser } from "@clerk/clerk-expo";
import { useAccount } from "jazz-tools/expo";
import { useAccount } from "jazz-expo";
import { Chat } from "../../src/schema";
export default function ChatScreen() {

View File

@@ -1,6 +1,7 @@
{
"name": "chat-rn-expo-clerk",
"main": "index.js",
"version": "1.0.144",
"scripts": {
"build": "expo export -p ios",
"start": "expo start",
@@ -16,6 +17,7 @@
"@bacons/text-decoder": "0.0.0",
"@bam.tech/react-native-image-resizer": "^3.0.11",
"@clerk/clerk-expo": "^2.2.21",
"@craftzdog/react-native-buffer": "6.0.5",
"@expo/vector-icons": "^14.1.0",
"@react-native-community/netinfo": "11.4.1",
"@react-navigation/native": "7.0.19",
@@ -37,6 +39,8 @@
"expo-sqlite": "15.2.9",
"expo-status-bar": "~2.2.3",
"expo-web-browser": "~14.1.6",
"jazz-expo": "workspace:*",
"jazz-react-native-media-images": "workspace:*",
"jazz-tools": "workspace:*",
"nativewind": "^4.1.21",
"react": "19.0.0",
@@ -54,6 +58,8 @@
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/react": "~19.0.14",
"@types/react-test-renderer": "^19.0.0",
"react-test-renderer": "18.3.1",
"tailwindcss": "^3.4.17",
"typescript": "5.8.3"
},

View File

@@ -3,6 +3,9 @@
// @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);

View File

@@ -1,5 +1,5 @@
import { useClerk } from "@clerk/clerk-expo";
import { JazzExpoProviderWithClerk } from "jazz-tools/expo";
import { JazzProviderWithClerk } from "jazz-expo/auth/clerk";
import React, { PropsWithChildren } from "react";
import { apiKey } from "./apiKey";
@@ -7,13 +7,13 @@ export function JazzAndAuth({ children }: PropsWithChildren) {
const clerk = useClerk();
return (
<JazzExpoProviderWithClerk
<JazzProviderWithClerk
clerk={clerk}
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
>
{children}
</JazzExpoProviderWithClerk>
</JazzProviderWithClerk>
);
}

View File

@@ -36,9 +36,4 @@ yarn-error.*
# typescript
*.tsbuildinfo
android/
ios/
# env files
.env
.env.*
!.env.example
!.env.test
ios/

View File

@@ -1,13 +1,5 @@
# chat-rn-expo
## 1.0.13
### Patch Changes
- Updated dependencies [048ac1d]
- jazz-tools@0.14.22
- jazz-expo@0.14.22
## 1.0.12
### Patch Changes

View File

@@ -1,5 +1,6 @@
{
"name": "chat-rn-expo",
"version": "1.0.12",
"main": "index.ts",
"scripts": {
"build": "expo prebuild",
@@ -11,13 +12,14 @@
"dependencies": {
"@azure/core-asynciterator-polyfill": "^1.0.2",
"@bacons/text-decoder": "^0.0.0",
"@bam.tech/react-native-image-resizer": "^3.0.11",
"@craftzdog/react-native-buffer": "^6.0.5",
"@react-native-community/netinfo": "11.4.1",
"expo": "~53.0.9",
"expo-clipboard": "^7.1.4",
"expo-secure-store": "~14.2.3",
"expo-sqlite": "~15.2.10",
"expo-status-bar": "~2.2.3",
"jazz-expo": "workspace:*",
"jazz-tools": "workspace:*",
"react": "19.0.0",
"react-native": "0.79.2",

View File

@@ -3,6 +3,9 @@
// @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);

View File

@@ -1,4 +1,4 @@
import { JazzExpoProvider } from "jazz-tools/expo";
import { JazzProvider } from "jazz-expo";
import React, { StrictMode } from "react";
import { apiKey } from "./apiKey";
import ChatScreen from "./chat";
@@ -6,13 +6,13 @@ import ChatScreen from "./chat";
export default function App() {
return (
<StrictMode>
<JazzExpoProvider
<JazzProvider
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
>
<ChatScreen />
</JazzExpoProvider>
</JazzProvider>
</StrictMode>
);
}

View File

@@ -14,7 +14,7 @@ import React, {
StyleSheet,
} from "react-native";
import { useAccount, useCoState } from "jazz-tools/expo";
import { useAccount, useCoState } from "jazz-expo";
import { Chat, Message } from "./schema";
export default function ChatScreen() {
@@ -128,11 +128,7 @@ export default function ChatScreen() {
}}
testID="chat-id-input"
/>
<TouchableOpacity
testID="join-chat-button"
onPress={joinChat}
style={styles.joinChatButton}
>
<TouchableOpacity onPress={joinChat} style={styles.joinChatButton}>
<Text style={styles.newChatButtonText}>Join chat</Text>
</TouchableOpacity>
</View>

View File

@@ -41,17 +41,12 @@ appId: tools.jazz.chatrnexpo
# logout
- tapOn: "Logout"
- assertVisible: "Username"
- assertVisible: "Anonymous user"
- extendedWaitUntil:
visible: "Anonymous user"
timeout: 10000
# join chat
## Commented because it fails on CI
# join chat
# - tapOn:
# id: "chat-id-input"
# - inputText: "co_zFs6KFyhxPw4xtw83tcEMzeHUNv" # Use a static id because maestro doesn't have access to the system clipboard
# - tapOn:
# id: "join-chat-button"
# - tapOn: "Join chat"
# - assertVisible: "boorad"
# - assertVisible: "bro, low key, it do be like that tho"

View File

@@ -1,13 +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
# 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

@@ -73,9 +73,3 @@ yarn-error.log
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# env files
.env
.env.*
!.env.example
!.env.test

View File

@@ -1,16 +1,5 @@
# chat-rn
## 1.0.140
### Patch Changes
- Updated dependencies [57fb69f]
- Updated dependencies [048ac1d]
- cojson@0.14.22
- jazz-tools@0.14.22
- cojson-transport-ws@0.14.22
- jazz-react-native@0.14.22
## 1.0.139
### Patch Changes

View File

@@ -63,14 +63,14 @@ def enableProguardInReleaseBuilds = false
* The preferred build flavor of JavaScriptCore (JSC)
*
* For example, to use the international variant, you can use:
* `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
def jscFlavor = 'org.webkit:android-jsc:+'
android {
ndkVersion rootProject.ext.ndkVersion

View File

@@ -4,11 +4,13 @@ import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader
class MainApplication : Application(), ReactApplication {
@@ -33,6 +35,10 @@ class MainApplication : Application(), ReactApplication {
override fun onCreate() {
super.onCreate()
loadReactNative(this)
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
}
}
}

View File

@@ -3,9 +3,9 @@ buildscript {
buildToolsVersion = "35.0.0"
minSdkVersion = 24
compileSdkVersion = 35
targetSdkVersion = 35
ndkVersion = "27.1.12297006"
kotlinVersion = "2.1.20"
targetSdkVersion = 34
ndkVersion = "26.1.10909125"
kotlinVersion = "1.9.25"
}
repositories {
google()

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -86,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -114,7 +115,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -205,7 +206,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
@@ -213,7 +214,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.

View File

@@ -1,8 +1,3 @@
@REM Copyright (c) Meta Platforms, Inc. and affiliates.
@REM
@REM This source code is licensed under the MIT license found in the
@REM LICENSE file in the root directory of this source tree.
@rem
@rem Copyright 2015 the original author or authors.
@rem
@@ -75,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell

View File

@@ -1,4 +1,3 @@
module.exports = {
presets: ["module:@react-native/babel-preset"],
plugins: ["@babel/plugin-transform-export-namespace-from"],
};

View File

@@ -7,27 +7,56 @@
objects = {
/* Begin PBXBuildFile section */
00E356F31AD99517003FC87E /* ChatRNTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ChatRNTests.m */; };
0C80B921A6F3F58F76C31292 /* libPods-ChatRN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-ChatRN.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
7699B88040F8A987B510C191 /* libPods-ChatRN-ChatRNTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-ChatRN-ChatRNTests.a */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
927557B966FCFCEE5896C589 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
FDBE82F8CEBB429FCE591919 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
remoteInfo = ChatRN;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
00E356EE1AD99517003FC87E /* ChatRNTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChatRNTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* ChatRNTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChatRNTests.m; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* ChatRN.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChatRN.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ChatRN/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = ChatRN/AppDelegate.mm; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ChatRN/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ChatRN/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ChatRN/main.m; sourceTree = "<group>"; };
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = ChatRN/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
19F6CBCC0A4E27FBF8BF4A61 /* libPods-ChatRN-ChatRNTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ChatRN-ChatRNTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
3B4392A12AC88292D35C810B /* Pods-ChatRN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatRN.debug.xcconfig"; path = "Target Support Files/Pods-ChatRN/Pods-ChatRN.debug.xcconfig"; sourceTree = "<group>"; };
5709B34CF0A7D63546082F79 /* Pods-ChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatRN.release.xcconfig"; path = "Target Support Files/Pods-ChatRN/Pods-ChatRN.release.xcconfig"; sourceTree = "<group>"; };
5B7EB9410499542E8C5724F5 /* Pods-ChatRN-ChatRNTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatRN-ChatRNTests.debug.xcconfig"; path = "Target Support Files/Pods-ChatRN-ChatRNTests/Pods-ChatRN-ChatRNTests.debug.xcconfig"; sourceTree = "<group>"; };
5DCACB8F33CDC322A6C60F78 /* libPods-ChatRN.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ChatRN.a"; sourceTree = BUILT_PRODUCTS_DIR; };
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = ChatRN/AppDelegate.swift; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ChatRN/LaunchScreen.storyboard; sourceTree = "<group>"; };
89C6BE57DB24E9ADA2F236DE /* Pods-ChatRN-ChatRNTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatRN-ChatRNTests.release.xcconfig"; path = "Target Support Files/Pods-ChatRN-ChatRNTests/Pods-ChatRN-ChatRNTests.release.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
00E356EB1AD99517003FC87E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7699B88040F8A987B510C191 /* libPods-ChatRN-ChatRNTests.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -39,13 +68,32 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
00E356EF1AD99517003FC87E /* ChatRNTests */ = {
isa = PBXGroup;
children = (
00E356F21AD99517003FC87E /* ChatRNTests.m */,
00E356F01AD99517003FC87E /* Supporting Files */,
);
path = ChatRNTests;
sourceTree = "<group>";
};
00E356F01AD99517003FC87E /* Supporting Files */ = {
isa = PBXGroup;
children = (
00E356F11AD99517003FC87E /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* ChatRN */ = {
isa = PBXGroup;
children = (
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
761780EC2CA45674006654EE /* AppDelegate.swift */,
13B07FB61A68108700A75B9A /* Info.plist */,
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
13B07FB71A68108700A75B9A /* main.m */,
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
);
name = ChatRN;
@@ -56,6 +104,7 @@
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
5DCACB8F33CDC322A6C60F78 /* libPods-ChatRN.a */,
19F6CBCC0A4E27FBF8BF4A61 /* libPods-ChatRN-ChatRNTests.a */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -72,6 +121,7 @@
children = (
13B07FAE1A68108700A75B9A /* ChatRN */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* ChatRNTests */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
BBD78D7AC51CEA395F1C20DB /* Pods */,
@@ -85,6 +135,7 @@
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* ChatRN.app */,
00E356EE1AD99517003FC87E /* ChatRNTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -94,6 +145,8 @@
children = (
3B4392A12AC88292D35C810B /* Pods-ChatRN.debug.xcconfig */,
5709B34CF0A7D63546082F79 /* Pods-ChatRN.release.xcconfig */,
5B7EB9410499542E8C5724F5 /* Pods-ChatRN-ChatRNTests.debug.xcconfig */,
89C6BE57DB24E9ADA2F236DE /* Pods-ChatRN-ChatRNTests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@@ -101,6 +154,27 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
00E356ED1AD99517003FC87E /* ChatRNTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ChatRNTests" */;
buildPhases = (
A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */,
00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */,
C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */,
F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
00E356F51AD99517003FC87E /* PBXTargetDependency */,
);
name = ChatRNTests;
productName = ChatRNTests;
productReference = 00E356EE1AD99517003FC87E /* ChatRNTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
13B07F861A680F5B00A75B9A /* ChatRN */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ChatRN" */;
@@ -130,6 +204,10 @@
attributes = {
LastUpgradeCheck = 1210;
TargetAttributes = {
00E356ED1AD99517003FC87E = {
CreatedOnToolsVersion = 6.2;
TestTargetID = 13B07F861A680F5B00A75B9A;
};
13B07F861A680F5B00A75B9A = {
LastSwiftMigration = 1120;
};
@@ -149,18 +227,26 @@
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* ChatRN */,
00E356ED1AD99517003FC87E /* ChatRNTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
00E356EC1AD99517003FC87E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8E1A680F5B00A75B9A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
927557B966FCFCEE5896C589 /* PrivacyInfo.xcprivacy in Resources */,
FDBE82F8CEBB429FCE591919 /* PrivacyInfo.xcprivacy in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -200,6 +286,28 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ChatRN/Pods-ChatRN-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-ChatRN-ChatRNTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -222,6 +330,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ChatRN-ChatRNTests/Pods-ChatRN-ChatRNTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ChatRN-ChatRNTests/Pods-ChatRN-ChatRNTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ChatRN-ChatRNTests/Pods-ChatRN-ChatRNTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -239,20 +364,105 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ChatRN/Pods-ChatRN-resources.sh\"\n";
showEnvVarsInLog = 0;
};
F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ChatRN-ChatRNTests/Pods-ChatRN-ChatRNTests-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ChatRN-ChatRNTests/Pods-ChatRN-ChatRNTests-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ChatRN-ChatRNTests/Pods-ChatRN-ChatRNTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
00E356EA1AD99517003FC87E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
00E356F31AD99517003FC87E /* ChatRNTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F871A680F5B00A75B9A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */,
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 13B07F861A680F5B00A75B9A /* ChatRN */;
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
00E356F61AD99517003FC87E /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-ChatRN-ChatRNTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = ChatRNTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
"$(inherited)",
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChatRN.app/ChatRN";
};
name = Debug;
};
00E356F71AD99517003FC87E /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-ChatRN-ChatRNTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO;
INFOPLIST_FILE = ChatRNTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
"$(inherited)",
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChatRN.app/ChatRN";
};
name = Release;
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-ChatRN.debug.xcconfig */;
@@ -380,7 +590,7 @@
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true;
@@ -452,7 +662,7 @@
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
VALIDATE_PRODUCT = YES;
@@ -462,6 +672,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ChatRNTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
00E356F61AD99517003FC87E /* Debug */,
00E356F71AD99517003FC87E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ChatRN" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,6 @@
#import <RCTAppDelegate.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : RCTAppDelegate
@end

View File

@@ -0,0 +1,31 @@
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"ChatRN";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
return [self bundleURL];
}
- (NSURL *)bundleURL
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
@end

View File

@@ -1,48 +0,0 @@
import UIKit
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var reactNativeDelegate: ReactNativeDelegate?
var reactNativeFactory: RCTReactNativeFactory?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
let delegate = ReactNativeDelegate()
let factory = RCTReactNativeFactory(delegate: delegate)
delegate.dependencyProvider = RCTAppDependencyProvider()
reactNativeDelegate = delegate
reactNativeFactory = factory
window = UIWindow(frame: UIScreen.main.bounds)
factory.startReactNative(
withModuleName: "ChatRN",
in: window,
launchOptions: launchOptions
)
return true
}
}
class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
override func sourceURL(for bridge: RCTBridge) -> URL? {
self.bundleURL()
}
override func bundleURL() -> URL? {
#if DEBUG
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
}
}

View File

@@ -34,8 +34,6 @@
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>RCTNewArchEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>

View File

@@ -0,0 +1,10 @@
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@@ -23,6 +23,11 @@ target 'ChatRN' do
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
target 'ChatRNTests' do
inherit! :complete
# Pods for testing
end
post_install do |installer|
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
react_native_post_install(

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn",
"private": true,
"version": "1.0.139",
"main": "index.js",
"scripts": {
"android": "react-native run-android",
@@ -12,38 +12,46 @@
"dependencies": {
"@azure/core-asynciterator-polyfill": "^1.0.2",
"@bacons/text-decoder": "0.0.0",
"@op-engineering/op-sqlite": "14.1.0",
"@react-native-clipboard/clipboard": "1.16.2",
"@craftzdog/react-native-buffer": "6.0.5",
"@op-engineering/op-sqlite": "^11.4.8",
"@react-native-clipboard/clipboard": "1.16.1",
"@react-native-community/netinfo": "11.4.1",
"@react-navigation/native": "7.1.14",
"@react-navigation/native-stack": "7.3.19",
"@react-navigation/native": "7.0.19",
"@react-navigation/native-stack": "7.2.1",
"clsx": "^2.0.0",
"cojson": "workspace:*",
"cojson-transport-ws": "workspace:*",
"jazz-react-native": "workspace:*",
"jazz-tools": "workspace:*",
"react": "19.1.0",
"react-native": "0.80.0",
"react": "19.0.0",
"react-native": "0.79.2",
"react-native-get-random-values": "^1.11.0",
"react-native-mmkv": "3.3.0",
"react-native-safe-area-context": "5.5.0",
"react-native-screens": "4.11.1",
"react-native-mmkv": "^3.2.0",
"react-native-polyfill-globals": "^3.1.0",
"react-native-safe-area-context": "4.12.0",
"react-native-screens": "4.4.0",
"react-native-url-polyfill": "^2.0.0",
"readable-stream": "4.7.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-export-namespace-from": "^7.27.1",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native-community/cli": "19.0.0",
"@react-native-community/cli-platform-android": "19.0.0",
"@react-native-community/cli-platform-ios": "19.0.0",
"@react-native/babel-preset": "0.80.0",
"@react-native/eslint-config": "0.80.0",
"@react-native/metro-config": "0.80.0",
"@react-native/typescript-config": "0.80.0",
"@react-native-community/cli": "15.0.1",
"@react-native-community/cli-platform-android": "15.0.1",
"@react-native-community/cli-platform-ios": "15.0.1",
"@react-native/babel-preset": "0.76.9",
"@react-native/eslint-config": "0.76.9",
"@react-native/metro-config": "0.76.9",
"@react-native/typescript-config": "0.76.9",
"@rnx-kit/metro-config": "^2.0.1",
"@rnx-kit/metro-resolver-symlinks": "^0.2.5",
"@types/react": "19.1.0",
"@rnx-kit/metro-resolver-symlinks": "^0.2.1",
"@types/react": "^18.3.12",
"@types/react-test-renderer": "^18.0.0",
"eslint": "^8.19.0",
"pod-install": "^0.3.5",
"prettier": "2.8.8",
"react-test-renderer": "18.3.1",
"typescript": "5.6.2"
},
"engines": {

View File

@@ -3,7 +3,7 @@ import {
useNavigationContainerRef,
} from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { JazzReactNativeProvider } from "jazz-tools/react-native";
import { JazzProvider } from "jazz-react-native";
import React, { StrictMode, useEffect, useState } from "react";
import { Linking } from "react-native";
import { apiKey } from "./apiKey";
@@ -49,7 +49,7 @@ function App() {
return (
<StrictMode>
<JazzReactNativeProvider
<JazzProvider
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
@@ -67,7 +67,7 @@ function App() {
/>
</Stack.Navigator>
</NavigationContainer>
</JazzReactNativeProvider>
</JazzProvider>
</StrictMode>
);
}

View File

@@ -1,6 +1,6 @@
import Clipboard from "@react-native-clipboard/clipboard";
import { useAccount, useCoState } from "jazz-react-native";
import { CoPlainText, Group, ID, Loaded, Profile } from "jazz-tools";
import { useAccount, useCoState } from "jazz-tools/react-native";
import { useEffect, useState } from "react";
import {
Button,

View File

@@ -1,4 +1,4 @@
import { useAcceptInviteNative } from "jazz-tools/react-native";
import { useAcceptInvite } from "jazz-react-native";
import React from "react";
import { Text } from "react-native";
import { Chat } from "./schema";
@@ -8,7 +8,7 @@ export function HandleInviteScreen({
}: {
navigation: any;
}) {
useAcceptInviteNative({
useAcceptInvite({
invitedObjectSchema: Chat,
onAccept: async (chatId) => {
navigation.navigate("ChatScreen", { chatId });

View File

@@ -4,6 +4,9 @@
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions";
// import 'react-native-polyfill-globals/auto';
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);

View File

@@ -1,41 +0,0 @@
{
"name": "chat-svelte",
"version": "0.0.90",
"type": "module",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "prettier --check . && eslint .",
"format-and-lint": "pnpm run format && pnpm run lint",
"format-and-lint:fix": "pnpm run format --write && pnpm run lint --fix",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/kit": "^2.21.1",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@types/eslint": "^9.6.1",
"eslint": "^9.27.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.46.1",
"globals": "^15.15.0",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0",
"svelte": "^5.31.1",
"svelte-check": "^4.2.1",
"typescript": "5.6.2",
"typescript-eslint": "^8.32.1",
"vite": "6.0.11"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.7",
"jazz-tools": "workspace:*",
"tailwindcss": "^4.1.7"
}
}

View File

@@ -1,54 +0,0 @@
import { defineConfig, devices } from '@playwright/test';
import isCI from 'is-ci';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: isCI,
/* Retry on CI only */
retries: isCI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: isCI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'http://localhost:5173/',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
permissions: ['clipboard-read', 'clipboard-write']
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
}
],
/* Run your local dev server before starting the tests */
webServer: [
{
command: 'pnpm preview --port 5173',
url: 'http://localhost:5173/',
reuseExistingServer: !isCI
}
]
});

View File

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

View File

@@ -1,21 +0,0 @@
@import 'tailwindcss';
@theme {
--color-stone-50: oklch(0.988281 0.002 75);
--color-stone-75: oklch(0.980563 0.002 75);
--color-stone-100: oklch(0.964844 0.002 75);
--color-stone-200: oklch(0.917969 0.002 75);
--color-stone-300: oklch(0.853516 0.002 75);
--color-stone-400: oklch(0.789063 0.002 75);
--color-stone-500: oklch(0.726563 0.002 75);
--color-stone-600: oklch(0.613281 0.002 75);
--color-stone-700: oklch(0.523438 0.002 75);
--color-stone-800: oklch(0.412109 0.002 75);
--color-stone-900: oklch(0.302734 0.002 75);
--color-stone-925: oklch(0.22 0.002 75);
--color-stone-950: oklch(0.193359 0.002 75);
--color-blue: oklch(0.57 0.2346 261.2);
}
:focus {
@apply outline-hidden;
}

View File

@@ -1,9 +0,0 @@
<script lang="ts">
let { children } = $props();
</script>
<div
class="flex flex-col justify-between w-screen h-screen bg-stone-50 dark:bg-stone-925 dark:text-white"
>
{@render children()}
</div>

View File

@@ -1,13 +0,0 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let { children, fromMe }: { children: Snippet; fromMe: boolean | undefined } = $props();
</script>
<div
class="line-clamp-10 text-ellipsis rounded-2xl overflow-hidden max-w-[calc(100%-5rem)] shadow-sm p-1 {fromMe
? 'bg-white dark:bg-stone-900 dark:text-white'
: 'bg-blue text-white'}"
>
{@render children()}
</div>

View File

@@ -1,9 +0,0 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let { children, fromMe }: { children: Snippet; fromMe: boolean | undefined } = $props();
const align = fromMe ? 'items-end' : 'items-start';
</script>
<div class="{align} flex flex-col m-3" role="row">
{@render children()}
</div>

View File

@@ -1,12 +0,0 @@
<script lang="ts">
import { ImageDefinition, type Loaded } from 'jazz-tools';
import { useProgressiveImg } from '$lib/utils/useProgressiveImage.svelte';
let { image }: { image: Loaded<typeof ImageDefinition> } = $props();
const { src } = $derived(
useProgressiveImg({
image
})
);
</script>
<img class="h-auto max-h-[20rem] max-w-full rounded-t-xl mb-1" {src} alt="" />

View File

@@ -1,7 +0,0 @@
<script lang="ts">
let { by, madeAt }: { by: string | undefined; madeAt: Date } = $props();
</script>
<div class="text-xs text-neutral-500 mt-1.5">
{by} · {madeAt.toLocaleTimeString()}
</div>

View File

@@ -1,7 +0,0 @@
<script lang="ts">
import type { CoPlainText } from 'jazz-tools';
let { text, className }: { text: CoPlainText | string | null; className?: string } = $props();
</script>
<div class="whitespace-pre-wrap"><p class="px-2 leading-relaxed {className}">{text}</p></div>

View File

@@ -1,7 +0,0 @@
<script lang="ts">
let { children } = $props();
</script>
<div class="flex flex-col-reverse flex-1 overflow-y-auto" role="application">
{@render children()}
</div>

View File

@@ -1,36 +0,0 @@
<script lang="ts">
import BubbleBody from '$lib/components/BubbleBody.svelte';
import BubbleContainer from '$lib/components/BubbleContainer.svelte';
import BubbleImage from '$lib/components/BubbleImage.svelte';
import BubbleInfo from '$lib/components/BubbleInfo.svelte';
import BubbleText from '$lib/components/BubbleText.svelte';
import type { Message } from '$lib/schema';
import type { Account, Loaded } from 'jazz-tools';
let {
me,
msg
}: {
me: Loaded<typeof Account> | null | undefined;
msg: Loaded<typeof Message>;
} = $props();
const lastEdit = $derived(msg._edits.text);
const fromMe = $derived(lastEdit?.by?.isMe);
const { text, image } = $derived(msg);
</script>
{#if me && (!me.canRead(msg) || !msg.text?.toString())}
<BubbleContainer fromMe={false}>
<BubbleBody fromMe={false}>
<BubbleText text="Message not readable" className="italic text-gray-500"></BubbleText>
</BubbleBody>
</BubbleContainer>
{:else}
<BubbleContainer {fromMe}>
<BubbleBody {fromMe}>
{#if image}
<BubbleImage {image} />{/if}
<BubbleText {text} />
</BubbleBody>
<BubbleInfo by={lastEdit?.by?.profile?.name} madeAt={lastEdit?.madeAt || new Date()} />
</BubbleContainer>
{/if}

View File

@@ -1,3 +0,0 @@
<div class="flex items-center justify-center h-full px-3 text-base text-stone-500 md:text-2xl">
Start a conversation below.
</div>

View File

@@ -1,19 +0,0 @@
<script lang="ts">
let { size = 24, strokeWidth = 2 } = $props();
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width={strokeWidth}
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-image-icon lucide-image"
><rect width="18" height="18" x="3" y="3" rx="2" ry="2" /><circle cx="9" cy="9" r="2" /><path
d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"
/></svg
>

View File

@@ -1,32 +0,0 @@
<script lang="ts">
import type { ChangeEventHandler } from 'svelte/elements';
import ImageIcon from '$lib/components/ImageIcon.svelte';
let { onImageChange }: { onImageChange?: ChangeEventHandler<HTMLInputElement> } = $props();
let input = $state<HTMLInputElement>();
const onUploadClick = () => {
input?.click();
};
</script>
<button
type="button"
aria-label="Send image"
title="Send image"
onclick={onUploadClick}
class="text-stone-500 p-1.5 rounded-full hover:bg-stone-100 hover:text-stone-800 dark:hover:bg-stone-800 dark:hover:text-stone-200 transition-colors"
>
<ImageIcon size={24} strokeWidth={1.5} />
</button>
<label class="sr-only">
Image
<input
bind:this={input}
type="file"
accept="image/png, image/jpeg, image/gif"
onchange={onImageChange}
/>
</label>

View File

@@ -1,9 +0,0 @@
<script lang="ts">
let { children } = $props();
</script>
<div
class="flex gap-1 p-3 mt-auto bg-white border-t shadow-2xl border-stone-200 dark:bg-transparent dark:border-stone-900"
>
{@render children()}
</div>

View File

@@ -1,20 +0,0 @@
<script lang="ts">
let { onSubmit }: { onSubmit: (text: string) => void } = $props();
const inputId = $props.id();
</script>
<div class="flex-1">
<label class="sr-only" for={inputId}> Type a message and press Enter </label>
<input
id={inputId}
class="block w-full px-3 py-1 border rounded-full border-stone-200 placeholder:text-stone-500 dark:bg-stone-925 dark:text-white dark:border-stone-900"
placeholder="Type a message and press Enter"
maxLength={2048}
type="text"
onkeydown={({ key, currentTarget: input }) => {
if (key !== 'Enter' || !input.value) return;
onSubmit(input.value);
input.value = '';
}}
/>
</div>

View File

@@ -1,9 +0,0 @@
<script lang="ts">
let { children } = $props();
</script>
<div
class="flex justify-between w-full gap-2 p-3 bg-white border-b border-stone-200 dark:bg-transparent dark:border-stone-900"
>
{@render children()}
</div>

View File

@@ -1,8 +0,0 @@
import { co, z } from 'jazz-tools';
export const Message = co.map({
text: co.plainText(),
image: z.optional(co.image())
});
export const Chat = co.list(Message);

View File

@@ -1,16 +0,0 @@
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,53 +0,0 @@
import { ImageDefinition, type Loaded } from 'jazz-tools';
import { onDestroy } from 'svelte';
export function useProgressiveImg({
image,
maxWidth,
targetWidth
}: {
image: Loaded<typeof ImageDefinition> | null | undefined;
maxWidth?: number;
targetWidth?: number;
}) {
let current = $state<{
src?: string;
res?: `${number}x${number}` | 'placeholder';
}>();
const originalSize = $state(image?.originalSize);
const unsubscribe = image?.subscribe({}, (update: Loaded<typeof ImageDefinition>) => {
const highestRes = ImageDefinition.highestResAvailable(update, { maxWidth, targetWidth });
if (highestRes) {
if (highestRes.res !== current?.res) {
const blob = highestRes.stream.toBlob();
if (blob) {
const blobURI = URL.createObjectURL(blob);
current = { src: blobURI, res: highestRes.res };
setTimeout(() => URL.revokeObjectURL(blobURI), 200);
}
}
} else {
current = {
src: update?.placeholderDataURL,
res: 'placeholder'
};
}
});
onDestroy(() => () => {
unsubscribe?.();
});
return {
get src() {
return current?.src;
},
get res() {
return current?.res;
},
originalSize
};
}

View File

@@ -1,32 +0,0 @@
<script lang="ts">
import 'jazz-tools/inspector/register-custom-element';
import '../app.css';
import { JazzSvelteProvider } from 'jazz-tools/svelte';
import { apiKey } from '../apiKey';
import { getRandomUsername } from '$lib/utils';
let { children } = $props();
const defaultProfileName = getRandomUsername();
</script>
<svelte:head>
<title>Jazz Chat Example</title>
</svelte:head>
<div class="h-full bg-white text-stone-700 dark:text-stone-400 dark:bg-stone-925">
<JazzSvelteProvider
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`
}}
{defaultProfileName}
>
{@render children?.()}
</JazzSvelteProvider>
<jazz-inspector></jazz-inspector>
</div>
<style>
:global(html, body) {
margin: 0;
}
</style>

View File

@@ -1,16 +0,0 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { Chat } from '$lib/schema';
import { AccountCoState } from 'jazz-tools/svelte';
import { Account, Group } from 'jazz-tools';
const account = new AccountCoState(Account);
const me = $derived(account.current);
$effect(() => {
if (!me) return;
const group = Group.create();
group.addMember('everyone', 'writer');
const chat = Chat.create([], group);
goto(`/chat/${chat.id}`);
});
</script>

View File

@@ -1,116 +0,0 @@
<script lang="ts">
import { createImage } from 'jazz-tools/browser-media-images';
import { AccountCoState, CoState } from 'jazz-tools/svelte';
import { Account, CoPlainText, type ID } from 'jazz-tools';
import { page } from '$app/state';
import { Chat, Message } from '$lib/schema';
import AppContainer from '$lib/components/AppContainer.svelte';
import ChatBody from '$lib/components/ChatBody.svelte';
import ChatBubble from '$lib/components/ChatBubble.svelte';
import EmptyChatMessage from '$lib/components/EmptyChatMessage.svelte';
import ImageInput from '$lib/components/ImageInput.svelte';
import InputBar from '$lib/components/InputBar.svelte';
import TopBar from '$lib/components/TopBar.svelte';
import TextInput from '$lib/components/TextInput.svelte';
const chatId = $derived(page.params.id) as ID<typeof Chat>;
const chat = $derived(
new CoState(Chat, chatId, {
resolve: {
$each: {
text: true
}
}
})
);
const account = new AccountCoState(Account, {
resolve: {
profile: true
}
});
const me = $derived(account.current);
let showNLastMessages = $state(30);
const sendImage = (event: Event & { currentTarget: HTMLInputElement }) => {
const file = event.currentTarget.files?.[0];
if (!file || !chat.current) return;
if (file.size > 5000000) {
alert('Please upload an image less than 5MB.');
return;
}
createImage(file, { owner: chat.current._owner }).then((image) => {
if (!chat.current) return;
chat.current.push(
Message.create(
{
text: CoPlainText.create(file.name, chat.current._owner),
image: image
},
chat.current._owner
)
);
});
};
</script>
<AppContainer>
<TopBar>
<input
type="text"
value={me?.profile?.name ?? ''}
class="bg-transparent"
onchange={(e) => {
if (!me?.profile) return;
const target = e.target as HTMLInputElement;
me.profile.name = target.value;
}}
placeholder="Set username"
/>
<button
onclick={() => {
account.logOut();
window.location.reload(); // Otherwise the provider will not update with default profile name
}}>Log out</button
>
</TopBar>
{#if !chat}
<div class="flex items-center justify-center flex-1">Loading...</div>
{:else}
<ChatBody>
{#if chat.current && chat.current.length > 0}
{#each chat.current.slice(-showNLastMessages).reverse() as msg (msg.id)}
<ChatBubble {me} {msg} />
{/each}
{:else}
<EmptyChatMessage />
{/if}
{#if chat.current && chat.current.length > showNLastMessages}
<button
class="block px-4 py-1 mx-auto my-2 border rounded"
onclick={() => (showNLastMessages += 10)}
>
Show more
</button>
{/if}
</ChatBody>
<InputBar>
<ImageInput onImageChange={sendImage} />
<TextInput
onSubmit={(text: string) => {
if (!chat.current) return;
chat.current.push(
Message.create(
{ text: CoPlainText.create(text, chat.current._owner) },
chat.current._owner
)
);
}}
/>
</InputBar>
{/if}
</AppContainer>

View File

@@ -1,39 +0,0 @@
import { ChatPage } from './pages/ChatPage';
import { test } from '@playwright/test';
test('chat works between two windows', async ({ page: marioPage, browser }) => {
await marioPage.goto('/');
const context = await browser.newContext();
const luigiPage = await context.newPage();
await marioPage.waitForURL('/chat/**');
const roomUrl = marioPage.url();
await luigiPage.goto(roomUrl);
const marioChat = new ChatPage(marioPage);
const luigiChat = new ChatPage(luigiPage);
await marioChat.setUsername('Mario');
const message1ByMario = 'Hello Luigi, are you ready to save the princess?';
await marioChat.sendMessage(message1ByMario);
await marioChat.expectMessageRow(message1ByMario);
const roomURL = marioPage.url();
await luigiPage.goto(roomURL);
await luigiChat.setUsername('Luigi');
await luigiChat.expectMessageRow(message1ByMario);
const message2ByLuigi = "No, I'm not ready yet. I'm still trying to find the key to the castle.";
await luigiChat.sendMessage(message2ByLuigi);
await luigiChat.expectMessageRow(message2ByLuigi);
await marioChat.expectMessageRow(message1ByMario);
await luigiChat.expectMessageRow(message2ByLuigi);
await context.close();
});

View File

@@ -1,41 +0,0 @@
import { type Locator, type Page, expect } from '@playwright/test';
export class ChatPage {
readonly page: Page;
readonly messageInput: Locator;
readonly logoutButton: Locator;
readonly usernameInput: Locator;
constructor(page: Page) {
this.page = page;
this.messageInput = page.getByRole('textbox', {
name: 'Type a message and press Enter'
});
this.logoutButton = page.getByRole('button', {
name: 'Log out'
});
this.usernameInput = page.getByPlaceholder('Set username');
}
async setUsername(username: string) {
await this.usernameInput.fill(username);
}
async sendMessage(message: string) {
await this.messageInput.fill(message);
await this.messageInput.press('Enter');
}
async expectMessageRow(message: string) {
await expect(this.page.getByText(message)).toBeVisible();
}
async expectUserNameNotToBeAnonymousUser() {
await expect(this.usernameInput).not.toHaveValue(/anonymous user/i);
await expect(this.usernameInput).toHaveValue(/^Anonymous \w+/);
}
async logout() {
await this.logoutButton.click();
await this.page.goto('/');
}
}

1
examples/chat-vue/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
dist

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,25 @@
# Chat example with Jazz and Svelte
# Chat example with Jazz and Vue
## Getting started
You can either
1. Clone the jazz repository, and run the app within the monorepo.
2. Or create a new Jazz project using this example as a template.
### Using the example as a template
Create a new Jazz project, and use this example as a template.
```bash
npx create-jazz-app@latest chat-app --example chat-svelte
npx create-jazz-app@latest chat-vue-app --example chat-vue
```
Go to the new project directory.
```bash
cd chat-app
cd chat-vue-app
```
Run the dev server.
```bash
npm run dev
```
@@ -32,25 +29,21 @@ npm run dev
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/chat-svelte/
cd jazz/examples/chat-vue/
```
Start the dev server.
```bash
pnpm dev
```
@@ -65,4 +58,4 @@ If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or
By default, the example app uses [Jazz Cloud](https://jazz.tools/cloud) (`wss://cloud.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzSvelteProvider` in [./src/routes/+layout.svelte](./src/routes/+layout.svelte) to `{ peer: "ws://localhost:4200" }`.
You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzProvider` in [./src/main.ts](./src/main.ts) to `{ peer: "ws://localhost:4200" }`.

1
examples/chat-vue/env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="./public/favicon.ico" type="image/png">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jazz Chat Vue Example</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,39 @@
{
"name": "chat-vue",
"version": "0.0.119",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build-type-check": "run-p type-check \"build {@}\" --",
"preview": "vite preview",
"build": "vite build",
"type-check": "vue-tsc --build --force",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
},
"dependencies": {
"jazz-browser": "workspace:*",
"jazz-tools": "workspace:*",
"jazz-vue": "workspace:*",
"vue": "^3.5.11",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/node": "^22.5.1",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/tsconfig": "^0.5.1",
"autoprefixer": "^10.4.20",
"eslint": "^9.7.0",
"eslint-plugin-vue": "^9.28.0",
"npm-run-all2": "^6.2.3",
"postcss": "^8.4.27",
"tailwindcss": "^3.4.17",
"typescript": "5.6.2",
"vite": "6.3.5",
"vite-plugin-vue-devtools": "^7.4.6",
"vue-tsc": "^2.1.6"
}
}

View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

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