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
1513 changed files with 76210 additions and 43907 deletions

View File

@@ -6,18 +6,40 @@
"fixed": [
[
"cojson",
"cojson-storage",
"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

View File

@@ -1,23 +0,0 @@
# Description
<!-- Please include a summary of the change and which issue is fixed -->
<!-- Please also include relevant motivation and context -->
<!-- Include any links to documentation like RFCs if necessary -->
<!-- Add a link to to relevant preview environments or anything that would simplify visual review process -->
<!-- Supplemental screenshots and video are encouraged, but the primary description should be in text -->
## Manual testing instructions
<!-- Add any actions required to manually test the changes -->
## Tests
- [ ] Tests have been added and/or updated
- [ ] Tests have not been updated, because: <!-- Insert reason for not updating tests here -->
- [ ] I need help with writing tests
## Checklist
- [ ] I've updated the part of the docs that are affected the PR changes
- [ ] I've generated a changeset, if a version bump is required
- [ ] I've updated the jsDoc comments to the public APIs I've modified, or added them when missing

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

@@ -1,28 +1,19 @@
name: Code quality
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
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

@@ -1,11 +1,5 @@
name: End-to-End Tests for React Native
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
pull_request:
types: [opened, synchronize, reopened]
@@ -66,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

@@ -1,11 +1,5 @@
name: Jazz Run Tests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:
branches: ["main"]
@@ -14,7 +8,7 @@ on:
jobs:
test:
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: blacksmith-4vcpu-ubuntu-2204
timeout-minutes: 5
steps:

View File

@@ -0,0 +1,46 @@
name: Playwright Tests
on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize, reopened]
jobs:
test:
timeout-minutes: 60
runs-on: blacksmith-4vcpu-ubuntu-2204
strategy:
matrix:
project: ["homepage/homepage"]
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Install project dependencies
run: pnpm install
working-directory: ./${{ matrix.project }}
- name: Pnpm Build
run: pnpm turbo build
working-directory: ./${{ matrix.project }}
- name: Install Playwright Browsers
run: pnpm exec playwright install
working-directory: ./${{ matrix.project }}
- name: Run Playwright tests
run: pnpm exec playwright test
working-directory: ./${{ matrix.project }}
- uses: actions/upload-artifact@v4
if: failure()
with:
name: ${{ hashFiles(format('{0}/package.json', matrix.project)) }}-playwright-report
path: ./${{ matrix.project }}/playwright-report/
retention-days: 30

View File

@@ -1,11 +1,5 @@
name: Playwright Tests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:
branches: ["main"]
@@ -15,11 +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:
shard: ["1/2", "2/2"]
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
@@ -29,130 +23,21 @@ jobs:
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Pnpm Build
run: pnpm turbo build
working-directory: ./${{ matrix.project }}
- name: Install Playwright Browsers
run: pnpm exec playwright install
working-directory: ./${{ matrix.project }}
- name: Run Playwright tests for shard ${{ matrix.shard }}
run: |
# Parse shard information (e.g., "1/2" -> shard_num=1, total_shards=2)
IFS='/' read -r shard_num total_shards <<< "${{ matrix.shard }}"
shard_index=$((shard_num - 1)) # Convert to 0-based index
# Debug: Print parsed values
echo "Parsed shard_num: $shard_num"
echo "Parsed total_shards: $total_shards"
echo "Calculated shard_index: $shard_index"
# Define all projects to test
all_projects=(
"tests/e2e"
"examples/chat"
"examples/chat-svelte"
"examples/clerk"
"examples/betterauth"
"examples/file-share-svelte"
"examples/form"
"examples/inspector"
"examples/music-player"
"examples/organization"
"examples/server-worker-http"
"starters/react-passkey-auth"
"starters/svelte-passkey-auth"
"tests/jazz-svelte"
)
# Calculate which projects this shard should run
shard_projects=()
for i in "${!all_projects[@]}"; do
if [ $((i % total_shards)) -eq $shard_index ]; then
shard_projects+=("${all_projects[i]}")
fi
done
# Track project results
overall_exit_code=0
failed_projects=()
passed_projects=()
echo "=== Running tests for shard ${{ matrix.shard }} ==="
echo "Projects in this shard:"
printf '%s\n' "${shard_projects[@]}"
echo
# Run tests for each project
for project in "${shard_projects[@]}"; do
echo "=== Testing project: $project ==="
# Check if project directory exists
if [ ! -d "$project" ]; then
echo "❌ FAILED: Project directory $project does not exist"
failed_projects+=("$project (directory not found)")
overall_exit_code=1
continue
fi
# Check if project has package.json
if [ ! -f "$project/package.json" ]; then
echo "❌ FAILED: No package.json found in $project"
failed_projects+=("$project (no package.json)")
overall_exit_code=1
continue
fi
# Build the project
echo "🔨 Building $project..."
cd "$project"
if [ -f .env.test ]; then
cp .env.test .env
fi
if ! pnpm turbo build; then
echo "❌ BUILD FAILED: $project"
failed_projects+=("$project (build failed)")
overall_exit_code=1
cd - > /dev/null
continue
fi
# Run Playwright tests
echo "🧪 Running Playwright tests for $project..."
if ! pnpm exec playwright test; then
echo "❌ TESTS FAILED: $project"
failed_projects+=("$project (tests failed)")
overall_exit_code=1
else
echo "✅ TESTS PASSED: $project"
passed_projects+=("$project")
fi
cd - > /dev/null
echo "=== Finished testing $project ==="
echo
done
# Print summary report
echo "=========================================="
echo "📊 TEST SUMMARY FOR SHARD ${{ matrix.shard }}"
echo "=========================================="
if [ ${#passed_projects[@]} -gt 0 ]; then
echo "✅ PASSED (${#passed_projects[@]}):"
printf ' - %s\n' "${passed_projects[@]}"
echo
fi
if [ ${#failed_projects[@]} -gt 0 ]; then
echo "❌ FAILED (${#failed_projects[@]}):"
printf ' - %s\n' "${failed_projects[@]}"
echo
fi
echo "Total projects in shard: ${#shard_projects[@]}"
echo "Passed: ${#passed_projects[@]}"
echo "Failed: ${#failed_projects[@]}"
echo "=========================================="
# Exit with overall status
exit $overall_exit_code
- name: Run Playwright tests
run: pnpm exec playwright test
working-directory: ./${{ matrix.project }}
- uses: actions/upload-artifact@v4
if: failure()
with:
name: ${{ hashFiles(format('{0}/package.json', matrix.project)) }}-playwright-report
path: ./${{ matrix.project }}/playwright-report/
retention-days: 30

View File

@@ -1,11 +1,4 @@
name: Pre-Publish tagged Pull Requests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
pull_request:
types: [opened, synchronize, reopened, labeled]

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

@@ -1,11 +1,5 @@
name: Unit Tests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
pull_request:
types: [opened, synchronize, reopened]
@@ -15,7 +9,7 @@ on:
jobs:
unit-tests:
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- name: Checkout

View File

@@ -63,7 +63,7 @@ You'll need Node.js 22.x installed (we're working on support for 23.x), and pnpm
4. **Build the packages**:
```bash
pnpm build:packages
pnpm build
```
5. **Run tests** to verify everything is working:

View File

@@ -11,10 +11,9 @@
"jazz-tools.json",
"**/ios/**",
"**/android/**",
"tests/jazz-svelte/src/**",
"packages/jazz-svelte/**",
"examples/*svelte*/**",
"starters/*svelte*/**",
"examples/server-worker-inbox/src/routeTree.gen.ts",
"examples/jazz-paper-scissors/src/routeTree.gen.ts",
"homepage/homepage/**",
"**/package.json"
]
@@ -56,7 +55,7 @@
}
},
{
"include": ["packages/cojson/src/storage/*/**", "cojson-transport-ws/**"],
"include": ["packages/cojson-storage*/**", "cojson-transport-ws/**"],
"linter": {
"enabled": true,
"rules": {

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";

17
examples/chat-rn-expo-clerk/.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
node_modules/
.expo/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
# macOS
.DS_Store
ios
android

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
# 🎷 Jazz + Expo + `expo-router` + Clerk Auth
## 🚀 How to Run
### 1. Inside the Workspace Root
First, install dependencies and build the project:
```bash
pnpm i
pnpm run build
```
### 2. Inside the `examples/chat-rn-expo-clerk` Directory
Next, navigate to the specific example project and run the following commands:
```bash
pnpm expo prebuild
pnpx pod-install
pnpm expo run:ios
```
This will set up and launch the app on iOS. For Android, you can replace the last command with `pnpm expo run:android`.

View File

@@ -0,0 +1,45 @@
{
"expo": {
"name": "jazz-chat-rn-expo-clerk",
"scheme": "jazz-chat-rn-expo-clerk",
"slug": "jazz-chat-rn-expo-clerk",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.jazz.chatrnclerk"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"package": "com.jazz.chatrnclerk"
},
"newArchEnabled": true,
"plugins": [
"expo-secure-store",
"expo-font",
"expo-router",
"expo-sqlite",
[
"expo-image-picker",
{
"photosPermission": "The app accesses your photos to let you share them with your friends."
}
]
],
"extra": {
"eas": {
"projectId": "ca3d46e5-a10a-47ec-9d77-3b841e1c62d4"
}
}
}
}

View File

@@ -0,0 +1,15 @@
import { Redirect, Stack } from "expo-router";
import { useIsAuthenticated } from "jazz-expo";
import React from "react";
export default function HomeLayout() {
const isAuthenticated = useIsAuthenticated();
if (isAuthenticated) {
return <Redirect href={"/chat"} />;
}
return (
<Stack screenOptions={{ headerShown: false, headerBackVisible: true }} />
);
}

View File

@@ -0,0 +1,33 @@
import { SignedOut } from "@clerk/clerk-expo";
import { Link } from "expo-router";
import React from "react";
import { Text, View } from "react-native";
export default function HomePage() {
return (
<View className="flex-1 justify-center items-center bg-gray-100 p-6">
<SignedOut>
<View className="bg-white p-6 rounded-lg shadow-lg w-11/12 max-w-md">
<Text className="text-2xl font-bold text-center text-gray-900 mb-4">
Jazz 🤝 Clerk 🤝 Expo
</Text>
<Link href="/sign-in" className="mb-4">
<Text className="text-center text-blue-600 underline text-lg">
Sign In
</Text>
</Link>
<Link href="/sign-in-oauth" className="mb-4">
<Text className="text-center text-blue-600 underline text-lg">
Sign In OAuth
</Text>
</Link>
<Link href="/sign-up">
<Text className="text-center text-blue-600 underline text-lg">
Sign Up
</Text>
</Link>
</View>
</SignedOut>
</View>
);
}

View File

@@ -0,0 +1,20 @@
import { Redirect, Stack } from "expo-router";
import { useIsAuthenticated } from "jazz-expo";
export default function UnAuthenticatedLayout() {
const isAuthenticated = useIsAuthenticated();
if (isAuthenticated) {
return <Redirect href={"/chat"} />;
}
return (
<Stack
screenOptions={{
headerShown: true,
headerBackVisible: true,
headerTitle: "",
}}
/>
);
}

View File

@@ -0,0 +1,65 @@
import { useOAuth } from "@clerk/clerk-expo";
import * as Linking from "expo-linking";
import { Link } from "expo-router";
import * as WebBrowser from "expo-web-browser";
import React from "react";
import { Text, TouchableOpacity, View } from "react-native";
export const useWarmUpBrowser = () => {
React.useEffect(() => {
// Warm up the android browser to improve UX
// https://docs.expo.dev/guides/authentication/#improving-user-experience
void WebBrowser.warmUpAsync();
return () => {
void WebBrowser.coolDownAsync();
};
}, []);
};
WebBrowser.maybeCompleteAuthSession();
const SignInWithOAuth = () => {
useWarmUpBrowser();
const { startOAuthFlow } = useOAuth({ strategy: "oauth_google" });
const onPress = React.useCallback(async () => {
try {
const { createdSessionId, signIn, signUp, setActive } =
await startOAuthFlow({
redirectUrl: Linking.createURL("/", {
scheme: "jazz-chat-rn-expo-clerk",
}),
});
if (createdSessionId) {
setActive!({ session: createdSessionId });
} else {
// Use signIn or signUp for next steps such as MFA
}
} catch (err) {
console.error("OAuth error", err);
}
}, []);
return (
<View className="flex-1 justify-center items-center bg-gray-50 p-6">
<View className="bg-white w-11/12 max-w-md p-8 rounded-lg shadow-lg items-center">
<TouchableOpacity
onPress={onPress}
className="w-full bg-red-500 py-3 rounded-lg flex items-center justify-center"
>
<Text className="text-white text-lg font-semibold">
Sign in with Google
</Text>
</TouchableOpacity>
<Link href="/" className="mt-4">
<Text className="text-blue-600 text-lg font-semibold underline mb-6">
Back to Home
</Text>
</Link>
</View>
</View>
);
};
export default SignInWithOAuth;

View File

@@ -0,0 +1,79 @@
import { useSignIn } from "@clerk/clerk-expo";
import { Link } from "expo-router";
import React from "react";
import { Text, TextInput, TouchableOpacity, View } from "react-native";
export default function SignInPage() {
const { signIn, setActive, isLoaded } = useSignIn();
const [emailAddress, setEmailAddress] = React.useState("");
const [password, setPassword] = React.useState("");
const [errorMessage, setErrorMessage] = React.useState("");
const onSignInPress = React.useCallback(async () => {
if (!isLoaded) {
return;
}
setErrorMessage("");
try {
const signInAttempt = await signIn.create({
identifier: emailAddress,
password,
});
if (signInAttempt.status === "complete") {
await setActive({ session: signInAttempt.createdSessionId });
} else {
console.error(JSON.stringify(signInAttempt, null, 2));
setErrorMessage("Invalid credentials. Please try again.");
}
} catch (err: any) {
console.error(JSON.stringify(err, null, 2));
if (err.errors && err.errors[0]?.message) {
setErrorMessage(err.errors[0].message);
} else {
setErrorMessage("An unexpected error occurred. Please try again.");
}
}
}, [isLoaded, emailAddress, password]);
return (
<View className="flex-1 justify-center items-center bg-gray-50 p-6">
<View className="bg-white w-11/12 max-w-md p-8 rounded-lg shadow-md">
<Text className="text-3xl font-bold text-center text-gray-800 mb-6">
Sign In
</Text>
{errorMessage ? (
<Text className="text-red-500 text-center mb-4">{errorMessage}</Text>
) : null}
<TextInput
autoCapitalize="none"
value={emailAddress}
placeholder="Email..."
onChangeText={(emailAddress) => setEmailAddress(emailAddress)}
className="w-full h-12 mb-4 px-4 bg-gray-100 border border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none"
/>
<TextInput
value={password}
placeholder="Password..."
secureTextEntry={true}
onChangeText={(password) => setPassword(password)}
className="w-full h-12 mb-6 px-4 bg-gray-100 border border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none"
/>
<TouchableOpacity
onPress={onSignInPress}
className="w-full h-12 bg-blue-600 rounded-lg flex items-center justify-center"
>
<Text className="text-white text-lg font-semibold">Sign In</Text>
</TouchableOpacity>
<View className="flex-row items-center justify-center mt-4">
<Text className="text-gray-600">Don't have an account?</Text>
<Link href="/sign-up">
<Text className="text-blue-500 ml-2 font-semibold">Sign up</Text>
</Link>
</View>
</View>
</View>
);
}

View File

@@ -0,0 +1,120 @@
import { useSignUp } from "@clerk/clerk-expo";
import { useNavigation } from "@react-navigation/native";
import * as React from "react";
import { Text, TextInput, TouchableOpacity, View } from "react-native";
export default function SignUpPage() {
const { isLoaded, signUp, setActive } = useSignUp();
const [emailAddress, setEmailAddress] = React.useState("");
const [password, setPassword] = React.useState("");
const [pendingVerification, setPendingVerification] = React.useState(false);
const [code, setCode] = React.useState("");
const [errorMessage, setErrorMessage] = React.useState("");
const navigation = useNavigation();
const onSignUpPress = async () => {
if (!isLoaded) return;
setErrorMessage("");
try {
await signUp.create({
emailAddress,
password,
});
await signUp.prepareEmailAddressVerification({
strategy: "email_code",
});
setPendingVerification(true);
} catch (err: any) {
console.error(JSON.stringify(err, null, 2));
if (err.errors && err.errors[0]?.message) {
setErrorMessage(err.errors[0].message);
} else {
setErrorMessage("An unexpected error occurred. Please try again.");
}
}
};
const onPressVerify = async () => {
if (!isLoaded) return;
setErrorMessage("");
try {
const completeSignUp = await signUp.attemptEmailAddressVerification({
code,
});
if (completeSignUp.status === "complete") {
await setActive({ session: completeSignUp.createdSessionId });
} else {
console.error(JSON.stringify(completeSignUp, null, 2));
setErrorMessage("Failed to verify. Please check your code.");
}
} catch (err: any) {
console.error(JSON.stringify(err, null, 2));
setErrorMessage("Invalid verification code. Please try again.");
}
};
return (
<View className="flex-1 justify-center items-center bg-gray-50 p-6">
<View className="bg-white w-11/12 max-w-md p-8 rounded-lg shadow-lg">
<Text className="text-3xl font-bold text-center text-gray-800 mb-6">
{pendingVerification ? "Verify Email" : "Sign Up"}
</Text>
{errorMessage ? (
<Text className="text-red-500 text-center mb-4">{errorMessage}</Text>
) : null}
{!pendingVerification && (
<>
<TextInput
autoCapitalize="none"
value={emailAddress}
placeholder="Email..."
onChangeText={(email) => setEmailAddress(email)}
className="w-full h-12 mb-4 px-4 bg-gray-100 border border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none"
/>
<TextInput
value={password}
placeholder="Password..."
secureTextEntry={true}
onChangeText={(password) => setPassword(password)}
className="w-full h-12 mb-6 px-4 bg-gray-100 border border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none"
/>
<TouchableOpacity
onPress={onSignUpPress}
className="w-full h-12 bg-blue-600 rounded-lg flex justify-center items-center mb-4"
>
<Text className="text-white text-lg font-semibold">Sign Up</Text>
</TouchableOpacity>
</>
)}
{pendingVerification && (
<>
<TextInput
value={code}
placeholder="Verification Code..."
onChangeText={(code) => setCode(code)}
className="w-full h-12 mb-4 px-4 bg-gray-100 border border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none"
/>
<TouchableOpacity
onPress={onPressVerify}
className="w-full h-12 bg-green-600 rounded-lg flex justify-center items-center mb-4"
>
<Text className="text-white text-lg font-semibold">
Verify Email
</Text>
</TouchableOpacity>
</>
)}
</View>
</View>
);
}

View File

@@ -0,0 +1,42 @@
import { ScrollViewStyleReset } from "expo-router/html";
import { type PropsWithChildren } from "react";
/**
* This file is web-only and used to configure the root HTML for every web page during static rendering.
* The contents of this function only run in Node.js environments and do not have access to the DOM or browser APIs.
*/
export default function Root({ children }: PropsWithChildren) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
{/*
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
*/}
<ScrollViewStyleReset />
{/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
<style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
{/* Add any additional <head> elements that you want globally available on web... */}
</head>
<body>{children}</body>
</html>
);
}
const responsiveBackground = `
body {
background-color: #fff;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #000;
}
}`;

View File

@@ -0,0 +1,29 @@
import { Link, Stack } from "expo-router";
import { StyleSheet, Text, View } from "react-native";
export default function NotFoundScreen() {
return (
<>
<Stack.Screen options={{ title: "Oops!" }} />
<View style={styles.container}>
<Text>This screen doesn't exist.</Text>
<Link href="/" style={styles.link}>
<Text>Go to home screen!</Text>
</Link>
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
padding: 20,
},
link: {
marginTop: 15,
paddingVertical: 15,
},
});

View File

@@ -0,0 +1,71 @@
import "../global.css";
import { ClerkLoaded, ClerkProvider } from "@clerk/clerk-expo";
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-expo";
import React, { useEffect } from "react";
import { tokenCache } from "../cache";
import { JazzAndAuth } from "../src/auth-context";
SplashScreen.preventAutoHideAsync();
function InitialLayout() {
const isAuthenticated = useIsAuthenticated();
const segments = useSegments();
const router = useRouter();
useEffect(() => {
const inAuthGroup = segments[0] === "(auth)";
if (isAuthenticated && inAuthGroup) {
router.replace("/chat");
} else if (!isAuthenticated && !inAuthGroup) {
router.replace("/");
}
SplashScreen.hideAsync();
}, [isAuthenticated, segments, router]);
return <Slot />;
}
export default function RootLayout() {
const [fontsLoaded] = useFonts({
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
});
const publishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY;
if (!publishableKey) {
throw new Error(
"Missing Publishable Key. Please set EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY in your .env",
);
}
useEffect(() => {
if (fontsLoaded) {
} else {
SplashScreen.preventAutoHideAsync();
}
}, [fontsLoaded]);
if (!fontsLoaded) {
return null;
}
return (
<ClerkProvider
tokenCache={tokenCache}
publishableKey={publishableKey}
__experimental_resourceCache={secureStore}
>
<ClerkLoaded>
<JazzAndAuth>
<InitialLayout />
</JazzAndAuth>
</ClerkLoaded>
</ClerkProvider>
);
}

View File

@@ -0,0 +1,234 @@
import { Chat, Message } from "@/src/schema";
import { useNavigation } from "@react-navigation/native";
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 { useEffect, useLayoutEffect, useState } from "react";
import React, {
SafeAreaView,
View,
Text,
Alert,
TouchableOpacity,
FlatList,
KeyboardAvoidingView,
TextInput,
Button,
Image,
ActivityIndicator,
} from "react-native";
export default function Conversation() {
const { chatId } = useLocalSearchParams();
const { me } = useAccount();
const [chat, setChat] = useState<Loaded<typeof Chat>>();
const [message, setMessage] = useState("");
const loadedChat = useCoState(Chat, chat?.id, { resolve: { $each: true } });
const navigation = useNavigation();
const [isUploading, setIsUploading] = useState(false);
useEffect(() => {
if (chat) return;
if (chatId === "new") {
createChat();
} else {
loadChat(chatId as string);
}
}, [chat]);
// Effect to dynamically set header options
useLayoutEffect(() => {
navigation.setOptions({
headerTitle: "Chat",
headerRight: () =>
chat ? (
<Button
onPress={() => {
if (chat?.id) {
Clipboard.setStringAsync(
`https://chat.jazz.tools/#/chat/${chat.id}`,
);
Alert.alert("Copied to clipboard", `Chat ID: ${chat.id}`);
}
}}
title="Share"
/>
) : null,
});
}, [navigation, chat]);
const createChat = () => {
const group = Group.create({ owner: me });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
setChat(chat);
};
const loadChat = async (chatId: string) => {
try {
const chat = await Chat.load(chatId);
if (chat) setChat(chat);
} catch (error) {
console.log("Error loading chat", error);
Alert.alert("Error", `Error loading chat: ${error}`);
}
};
const sendMessage = () => {
if (!chat) return;
if (message.trim()) {
chat.push(
Message.create(
{ text: CoPlainText.create(message, chat._owner) },
chat._owner,
),
);
setMessage("");
}
};
const handleImageUpload = async () => {
try {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
base64: true,
quality: 0.7,
});
if (!result.canceled && result.assets[0].base64 && chat) {
setIsUploading(true);
const base64Uri = `data:image/jpeg;base64,${result.assets[0].base64}`;
const image = await createImage(base64Uri, {
owner: chat._owner,
maxSize: 2048,
});
chat.push(
Message.create(
{ text: CoPlainText.create("", chat._owner), image },
chat._owner,
),
);
}
} catch (error) {
Alert.alert("Error", "Failed to upload image");
} finally {
setIsUploading(false);
}
};
const renderMessageItem = ({ item }: { item: Loaded<typeof Message> }) => {
const isMe = item._edits.text?.by?.isMe;
return (
<View
className={clsx(
`rounded-xl px-3 py-2 max-w-[75%] my-1`,
isMe ? `bg-blue-500 self-end` : `bg-gray-200 self-start`,
)}
>
{!isMe ? (
<Text
className={clsx(
`text-xs text-gray-500 mb-1`,
isMe ? "text-right" : "text-left",
)}
>
{item._edits.text?.by?.profile?.name}
</Text>
) : null}
<View
className={clsx(
"flex relative items-end justify-between",
isMe ? "flex-row" : "flex-row",
)}
>
{item.image && (
<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
className={clsx(
!isMe ? "text-black" : "text-gray-200",
`text-md max-w-[85%]`,
)}
>
{item.text}
</Text>
)}
<Text
className={clsx(
"text-[10px] text-right ml-2",
!isMe ? "mt-2 text-gray-500" : "mt-1 text-gray-200",
)}
>
{item._edits.text?.madeAt?.getHours().toString().padStart(2, "0")}:
{item._edits.text?.madeAt?.getMinutes().toString().padStart(2, "0")}
</Text>
</View>
</View>
);
};
return (
<View className="flex-1 bg-gray-50">
<FlatList
contentContainerStyle={{
flexGrow: 1,
paddingVertical: 10,
paddingHorizontal: 8,
}}
className="flex"
data={loadedChat}
keyExtractor={(item) => item.id}
renderItem={renderMessageItem}
/>
<KeyboardAvoidingView
keyboardVerticalOffset={110}
behavior="padding"
className="p-3 bg-white border-t border-gray-300"
>
<SafeAreaView className="flex-row items-center gap-2">
<TouchableOpacity
onPress={handleImageUpload}
disabled={isUploading}
className="h-10 w-10 items-center justify-center"
>
{isUploading ? (
<ActivityIndicator size="small" color="#0000ff" />
) : (
<Text className="text-2xl">🖼</Text>
)}
</TouchableOpacity>
<TextInput
className="flex-1 rounded-full h-10 px-4 bg-gray-100 border border-gray-300 focus:border-blue-500 focus:bg-white"
value={message}
onChangeText={setMessage}
placeholder="Type a message..."
textAlignVertical="center"
onSubmitEditing={sendMessage}
/>
<TouchableOpacity
onPress={sendMessage}
className="bg-blue-500 rounded-full h-10 w-10 items-center justify-center"
>
<Text className="text-white text-xl"></Text>
</TouchableOpacity>
</SafeAreaView>
</KeyboardAvoidingView>
</View>
);
}

View File

@@ -0,0 +1,14 @@
import { Stack } from "expo-router";
import React from "react";
export default function ChatLayout() {
return (
<Stack
screenOptions={{
headerShown: true,
headerBackVisible: true,
headerTitle: "",
}}
/>
);
}

View File

@@ -0,0 +1,90 @@
import { useNavigation } from "@react-navigation/native";
import { useRouter } from "expo-router";
import { ID } from "jazz-tools";
import { useLayoutEffect } from "react";
import React, {
Button,
Text,
TouchableOpacity,
View,
Alert,
} from "react-native";
import { useUser } from "@clerk/clerk-expo";
import { useAccount } from "jazz-expo";
import { Chat } from "../../src/schema";
export default function ChatScreen() {
const { logOut } = useAccount();
const router = useRouter();
const navigation = useNavigation();
const { user } = useUser();
function handleLogOut() {
logOut();
router.navigate("/");
}
useLayoutEffect(() => {
navigation.setOptions({
headerTitle: "Chat",
headerRight: () => <Button onPress={handleLogOut} title="Logout" />,
});
}, [navigation]);
const loadChat = async (chatId: string | "new") => {
router.navigate(`/chat/${chatId}`);
};
const joinChat = () => {
Alert.prompt(
"Join Chat",
"Enter the Chat ID (example: co_zBGEHYvRfGuT2YSBraY3njGjnde)",
[
{
text: "Cancel",
style: "cancel",
},
{
text: "Join",
onPress: (chatId) => {
if (chatId) {
loadChat(chatId);
} else {
Alert.alert("Error", "Chat ID cannot be empty.");
}
},
},
],
"plain-text",
);
};
return (
<View className="flex-1 bg-gray-50">
<View className="flex-1 justify-center items-center px-6">
<View className="w-full max-w-sm bg-white p-8 rounded-lg shadow-lg">
<Text className="text-xl font-semibold text-gray-800">
Welcome, {user?.emailAddresses[0].emailAddress}
</Text>
<TouchableOpacity
onPress={() => loadChat("new")}
className="w-full bg-blue-600 py-4 rounded-md mb-4 mt-4"
>
<Text className="text-white text-lg font-semibold text-center">
Start New Chat
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={joinChat}
className="w-full bg-green-500 py-4 rounded-md"
>
<Text className="text-white text-lg font-semibold text-center">
Join Chat
</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
}

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -0,0 +1,9 @@
module.exports = function (api) {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
};
};

View File

@@ -0,0 +1,39 @@
import * as SecureStore from "expo-secure-store";
import { Platform } from "react-native";
export interface TokenCache {
getToken: (key: string) => Promise<string | undefined | null>;
saveToken: (key: string, token: string) => Promise<void>;
clearToken: (key: string) => void;
}
const createTokenCache = (): TokenCache => {
return {
getToken: async (key: string) => {
try {
const item = await SecureStore.getItemAsync(key);
if (item) {
console.log(`${key} was used 🔐 \n`);
} else {
console.log("No values stored under key: " + key);
}
return item;
} catch (error) {
console.error("secure store get item error: ", error);
await SecureStore.deleteItemAsync(key);
return null;
}
},
saveToken: (key: string, token: string) => {
return SecureStore.setItemAsync(key, token);
},
clearToken: (key: string) => {
return SecureStore.deleteItemAsync(key);
},
};
};
// SecureStore is not supported on the web
// https://github.com/expo/expo/issues/7744#issuecomment-611093485
export const tokenCache =
Platform.OS !== "web" ? createTokenCache() : undefined;

View File

@@ -0,0 +1,27 @@
{
"cli": {
"version": ">= 12.5.1",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"ios-simulator": {
"extends": "development",
"ios": {
"simulator": true
}
},
"preview": {
"distribution": "internal"
},
"production": {
"autoIncrement": true
}
},
"submit": {
"production": {}
}
}

View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -0,0 +1,2 @@
import "./polyfills";
import "expo-router/entry";

View File

@@ -0,0 +1,35 @@
// Learn more https://docs.expo.dev/guides/monorepos
const { getDefaultConfig } = require("expo/metro-config");
const { withNativeWind } = require("nativewind/metro");
const { FileStore } = require("metro-cache");
const path = require("path");
// eslint-disable-next-line no-undef
const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, "../..");
const config = getDefaultConfig(projectRoot);
// Since we are using pnpm, we have to setup the monorepo manually for Metro
// #1 - Watch all files in the monorepo
config.watchFolders = [workspaceRoot];
// #2 - Try resolving with project modules first, then workspace modules
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, "node_modules"),
path.resolve(workspaceRoot, "node_modules"),
];
config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"];
config.resolver.requireCycleIgnorePatterns = [
/(^|\/|\\)node_modules($|\/|\\)/,
/(^|\/|\\)packages($|\/|\\)/,
];
// Use turborepo to restore the cache when possible
config.cacheStores = [
new FileStore({
root: path.join(projectRoot, "node_modules", ".cache", "metro"),
}),
];
// module.exports = config;
module.exports = withNativeWind(config, { input: "./global.css" });

View File

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

View File

@@ -0,0 +1,67 @@
{
"name": "chat-rn-expo-clerk",
"main": "index.js",
"version": "1.0.144",
"scripts": {
"build": "expo export -p ios",
"start": "expo start",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write",
"android": "expo run:android",
"ios": "expo prebuild && pnpx pod-install && expo run:ios",
"web": "expo start --web",
"run:ios": "pnpm expo prebuild && npx pod-install && pnpm expo run:ios"
},
"dependencies": {
"@azure/core-asynciterator-polyfill": "^1.0.2",
"@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",
"@react-navigation/native-stack": "7.2.1",
"clsx": "^2.0.0",
"expo": "^53.0.8",
"expo-build-properties": "~0.14.6",
"expo-clipboard": "~7.1.4",
"expo-constants": "~17.1.6",
"expo-crypto": "~14.1.4",
"expo-dev-client": "~5.1.8",
"expo-file-system": "^18.1.9",
"expo-font": "~13.3.1",
"expo-image-picker": "~16.1.4",
"expo-linking": "~7.1.4",
"expo-router": "~5.0.6",
"expo-secure-store": "~14.2.3",
"expo-splash-screen": "~0.30.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",
"react-dom": "19.0.0",
"react-native": "0.79.2",
"react-native-gesture-handler": "~2.24.0",
"react-native-get-random-values": "^1.11.0",
"react-native-reanimated": "~3.17.5",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "4.10.0",
"react-native-url-polyfill": "^2.0.0",
"react-native-web": "~0.20.0",
"readable-stream": "4.7.0"
},
"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"
},
"private": true
}

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

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

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
// NOTE: Update this to include the paths to all of your component files.
content: [
"./app/**/*.{js,jsx,ts,tsx}",
"./components/**/*.{js,jsx,ts,tsx}",
"./src/**/*.{js,jsx,ts,tsx}",
],
presets: [require("nativewind/preset")],
theme: {
extend: {},
},
plugins: [],
};

View File

@@ -0,0 +1,11 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"moduleResolution": "bundler",
"paths": {
"@/*": ["./*"]
}
},
"include": ["**/*.ts", "**/*.tsx", "nativewind-env.d.ts"]
}

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,15 +12,17 @@
"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": "54.0.0-canary-20250701-6a945c5",
"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.1.0",
"react-native": "0.80.0",
"react": "19.0.0",
"react-native": "0.79.2",
"react-native-get-random-values": "^1.11.0",
"readable-stream": "^4.7.0"
},
@@ -29,4 +32,4 @@
"typescript": "~5.8.3"
},
"private": true
}
}

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

@@ -11,11 +11,11 @@ react {
// The root of your project, i.e. where "package.json" lives. Default is '../..'
// root = file("../../")
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
reactNativeDir = file("../../../../node_modules/react-native")
// reactNativeDir = file("../../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
codegenDir = file("../../../../node_modules/@react-native/codegen")
// codegenDir = file("../../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
cliFile = file("../../../../node_modules/react-native/cli.js")
// cliFile = file("../../node_modules/react-native/cli.js")
/* Variants */
// The list of variants to that are debuggable. For those we're going to
@@ -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,6 +1,6 @@
pluginManagement { includeBuild("../../../node_modules/@react-native/gradle-plugin") }
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
plugins { id("com.facebook.react.settings") }
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
rootProject.name = 'ChatRN'
include ':app'
includeBuild('../../../node_modules/@react-native/gradle-plugin')
includeBuild('../node_modules/@react-native/gradle-plugin')

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 */;
@@ -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.3",
"@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);

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