Compare commits

..

1 Commits

Author SHA1 Message Date
Guido D'Orsi
72999d8832 feat: migrate to a request to join pattern 2024-12-27 15:59:05 +01:00
310 changed files with 2846 additions and 6207 deletions

View File

@@ -10,21 +10,19 @@
"cojson-storage-indexeddb",
"cojson-storage-sqlite",
"cojson-transport-ws",
"create-jazz-app",
"jazz-browser",
"jazz-browser-auth-clerk",
"jazz-browser-media-images",
"jazz-nodejs",
"jazz-react",
"jazz-react-auth-clerk",
"jazz-react-core",
"jazz-react-native",
"jazz-react-native-auth-clerk",
"jazz-react-native-media-images",
"jazz-run",
"jazz-svelte",
"jazz-tools",
"jazz-vue",
"jazz-worker"
"jazz-vue"
]
],
"access": "public",

View File

@@ -1,5 +0,0 @@
---
"jazz-tools": patch
---
Return field name on \_edits

View File

@@ -1,5 +0,0 @@
---
"jazz-tools": patch
---
Simplify the .create calls by accepting directly "Account | Group" as second param

View File

@@ -1,5 +0,0 @@
---
"cojson": patch
---
Add a crypto entry to optionally import the crypto modules

View File

@@ -1,8 +0,0 @@
---
"jazz-react": minor
"jazz-svelte": minor
---
Change the way the JazzProvider is created and make the hooks available as top-level imports.
This is a breaking change.

View File

@@ -1,7 +0,0 @@
---
"jazz-worker": minor
---
- Renamed `jazz-nodejs` to `jazz-worker`
- Dropped support for Node.js versions before v22
- Added support for all the runtimes that support WASM and WebSocket clients

View File

@@ -1,5 +0,0 @@
---
"jazz-tools": patch
---
Optimize the subscribe to resolve the CoValues stored in memory synchronously

View File

@@ -0,0 +1,5 @@
---
"jazz-tools": patch
---
Remove the requirement of calling super.migrate when defining the account migration

View File

@@ -1,31 +1,5 @@
# jazz-example-book-shelf
## 0.1.38
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.1.37
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.1.36
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.1.35
### Patch Changes

View File

@@ -38,4 +38,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 adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<JazzProvider>` provider component in [./src/components/JazzAndAuth.tsx](./src/components/JazzAndAuth.tsx).
You can also run a local sync server by running `npx jazz-run sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/components/JazzAndAuth.tsx](./src/components/JazzAndAuth.tsx).

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-example-book-shelf",
"version": "0.1.38",
"version": "0.1.35",
"private": true,
"scripts": {
"dev": "next dev",
@@ -11,9 +11,9 @@
},
"dependencies": {
"clsx": "^2.0.0",
"jazz-browser-media-images": "workspace:0.8.50",
"jazz-react": "workspace:0.8.50",
"jazz-tools": "workspace:0.8.50",
"jazz-browser-media-images": "workspace:0.8.45",
"jazz-react": "workspace:0.8.45",
"jazz-tools": "workspace:0.8.45",
"next": "14.2.5",
"react": "^18.3.1",
"react-dom": "^18.3.1"

View File

@@ -2,10 +2,10 @@
import { Button } from "@/components/Button";
import { Container } from "@/components/Container";
import { useAccount } from "@/components/JazzAndAuth";
import RatingInput from "@/components/RatingInput";
import { BookReview, ListOfBookReviews } from "@/schema";
import { createImage } from "jazz-browser-media-images";
import { useAccount } from "jazz-react";
import { Group, ImageDefinition } from "jazz-tools";
import { useRouter } from "next/navigation";
import { ChangeEvent, useState } from "react";

View File

@@ -2,11 +2,11 @@
import { BookCover } from "@/components/BookCover";
import { Container } from "@/components/Container";
import { useCoState } from "@/components/JazzAndAuth";
import Rating from "@/components/Rating";
import RatingInput from "@/components/RatingInput";
import { BookReview } from "@/schema";
import clsx from "clsx";
import { useCoState } from "jazz-react";
import { Group, ID } from "jazz-tools";
const BookReviewTitle = ({

View File

@@ -1,9 +1,9 @@
"use client";
import { Container } from "@/components/Container";
import { useAccount } from "@/components/JazzAndAuth";
import UserProfile from "@/components/UserProfile";
import { JazzAccount } from "@/schema";
import { useAccount } from "jazz-react";
import { ID } from "jazz-tools";
export default function Home() {

View File

@@ -1,11 +1,12 @@
import { Button } from "@/components/Button";
import { useAccount } from "@/components/JazzAndAuth";
import PlusIcon from "@/components/icons/PlusIcon";
import { BookReview } from "@/schema";
import clsx from "clsx";
import { createImage } from "jazz-browser-media-images";
import { useAccount } from "jazz-react";
import { ProgressiveImg } from "jazz-react";
import { ChangeEvent, useRef } from "react";
import { Group, ImageDefinition } from "jazz-tools";
import { ChangeEvent, useRef, useState } from "react";
const BookCoverContainer = ({
children,

View File

@@ -1,8 +1,8 @@
"use client";
import { BookCoverReadOnly } from "@/components/BookCover";
import { BookCover, BookCoverReadOnly } from "@/components/BookCover";
import { useCoState } from "@/components/JazzAndAuth";
import StarIcon from "@/components/icons/StarIcon";
import { useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import Link from "next/link";
import { BookReview } from "../schema";

View File

@@ -1,30 +1,29 @@
"use client";
import { JazzAccount } from "@/schema";
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { DemoAuthBasicUI, createJazzReactApp, useDemoAuth } from "jazz-react";
const Jazz = createJazzReactApp({
AccountSchema: JazzAccount,
});
export const { useAccount, useCoState } = Jazz;
export function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
<Jazz.Provider
auth={auth}
AccountSchema={JazzAccount}
// replace `you@example.com` with your email as a temporary API key
peer="wss://cloud.jazz.tools/?key=you@example.com"
>
{children}
</JazzProvider>
</Jazz.Provider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Jazz Book Shelf" state={authState} />
)}
</>
);
}
declare module "jazz-react" {
interface Register {
Account: JazzAccount;
}
}

View File

@@ -2,7 +2,7 @@
import { Button } from "@/components/Button";
import { Container } from "@/components/Container";
import { useAccount } from "jazz-react";
import { useAccount } from "@/components/JazzAndAuth";
import Link from "next/link";
export function Nav() {

View File

@@ -2,8 +2,8 @@
import { BookReviewThumbnail } from "@/components/BookReviewThumbnail";
import { Button } from "@/components/Button";
import { useCoState } from "@/components/JazzAndAuth";
import { JazzAccount, JazzProfile, ListOfBookReviews } from "@/schema";
import { useCoState } from "jazz-react";
import { Group, ID } from "jazz-tools";
export default function UserProfile({ id }: { id: ID<JazzAccount> }) {

View File

@@ -1,34 +1,5 @@
# chat-rn-clerk
## 1.0.40
### Patch Changes
- jazz-react-native@0.8.50
- jazz-react-native-auth-clerk@0.8.50
- jazz-tools@0.8.50
- jazz-react-native-media-images@0.8.50
## 1.0.39
### Patch Changes
- jazz-react-native@0.8.49
- jazz-react-native-auth-clerk@0.8.49
- jazz-tools@0.8.49
- jazz-react-native-media-images@0.8.49
## 1.0.38
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react-native@0.8.48
- jazz-react-native-auth-clerk@0.8.48
- jazz-react-native-media-images@0.8.48
## 1.0.37
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "chat-rn-clerk",
"main": "index.js",
"version": "1.0.40",
"version": "1.0.37",
"scripts": {
"build": "expo export -p ios",
"start": "expo start",

View File

@@ -1,28 +1,5 @@
# chat-rn
## 1.0.37
### Patch Changes
- jazz-react-native@0.8.50
- jazz-tools@0.8.50
## 1.0.36
### Patch Changes
- jazz-react-native@0.8.49
- jazz-tools@0.8.49
## 1.0.35
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react-native@0.8.48
## 1.0.34
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn",
"version": "1.0.37",
"version": "1.0.34",
"main": "index.js",
"scripts": {
"build": "expo export -p ios",

View File

@@ -1,31 +1,5 @@
# chat-vue
## 0.0.29
### Patch Changes
- jazz-browser@0.8.50
- jazz-tools@0.8.50
- jazz-vue@0.8.50
## 0.0.28
### Patch Changes
- jazz-browser@0.8.49
- jazz-tools@0.8.49
- jazz-vue@0.8.49
## 0.0.27
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser@0.8.48
- jazz-vue@0.8.48
## 0.0.26
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-vue",
"version": "0.0.29",
"version": "0.0.26",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,31 +1,5 @@
# jazz-example-chat
## 0.0.124
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.123
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.122
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.121
### Patch Changes

View File

@@ -34,4 +34,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 adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<JazzProvider>` provider component in [./src/main.tsx](./src/main.tsx).
You can also run a local sync server by running `npx jazz-run sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/main.tsx](./src/main.tsx).

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.124",
"version": "0.0.121",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,13 +1,9 @@
import { inIframe, onChatLoad } from "@/util.ts";
import { useIframeHashRouter } from "hash-slash";
import { useAccount } from "jazz-react";
import { Group, ID } from "jazz-tools";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { ChatScreen } from "./chatScreen.tsx";
import { JazzAndAuth } from "./jazz.tsx";
import { useAccount } from "./main.tsx";
import { Chat } from "./schema.ts";
import { ThemeProvider } from "./themeProvider.tsx";
import { AppContainer, TopBar } from "./ui.tsx";
export function App() {
@@ -38,13 +34,3 @@ export function App() {
</AppContainer>
);
}
createRoot(document.getElementById("root")!).render(
<ThemeProvider>
<StrictMode>
<JazzAndAuth>
<App />
</JazzAndAuth>
</StrictMode>
</ThemeProvider>,
);

View File

@@ -1,7 +1,7 @@
import { createImage } from "jazz-browser-media-images";
import { useAccount, useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { useState } from "react";
import { useAccount, useCoState } from "./main.tsx";
import { Chat, Message } from "./schema.ts";
import {
BubbleBody,

View File

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

View File

@@ -0,0 +1,36 @@
import { ThemeProvider } from "@/themeProvider.tsx";
import { DemoAuthBasicUI, createJazzReactApp, useDemoAuth } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { App } from "./app.tsx";
const Jazz = createJazzReactApp();
export const { useAccount, useCoState } = Jazz;
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, state] = useDemoAuth();
return (
<>
<Jazz.Provider
auth={auth}
peer="wss://cloud.jazz.tools/?key=chat-example-jazz@garden.co"
>
{children}
</Jazz.Provider>
{state.state !== "signedIn" && (
<DemoAuthBasicUI appName="Jazz Chat" state={state} />
)}
</>
);
}
createRoot(document.getElementById("root")!).render(
<ThemeProvider>
<StrictMode>
<JazzAndAuth>
<App />
</JazzAndAuth>
</StrictMode>
</ThemeProvider>,
);

View File

@@ -1,31 +1,5 @@
# minimal-auth-clerk
## 0.0.23
### Patch Changes
- jazz-react@0.8.50
- jazz-react-auth-clerk@0.8.50
- jazz-tools@0.8.50
## 0.0.22
### Patch Changes
- jazz-react@0.8.49
- jazz-react-auth-clerk@0.8.49
- jazz-tools@0.8.49
## 0.0.21
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react@0.8.48
- jazz-react-auth-clerk@0.8.48
## 0.0.20
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "clerk",
"private": true,
"version": "0.0.23",
"version": "0.0.20",
"type": "module",
"scripts": {
"dev": "vite",
@@ -13,7 +13,7 @@
"dependencies": {
"@clerk/clerk-react": "^5.4.1",
"jazz-react": "workspace:*",
"jazz-react-auth-clerk": "workspace:0.8.50",
"jazz-react-auth-clerk": "workspace:0.8.45",
"jazz-tools": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"

View File

@@ -1,4 +1,4 @@
import { useAccount } from "jazz-react";
import { useAccount } from "./main";
function App() {
const { me, logOut } = useAccount();

View File

@@ -1,10 +1,10 @@
import { ClerkProvider, SignInButton, useClerk } from "@clerk/clerk-react";
import { createJazzReactApp } from "jazz-react";
import { useJazzClerkAuth } from "jazz-react-auth-clerk";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { JazzProvider } from "jazz-react";
// Import your publishable key
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
@@ -13,6 +13,9 @@ if (!PUBLISHABLE_KEY) {
throw new Error("Add your Clerk publishable key to the .env.local file");
}
const Jazz = createJazzReactApp();
export const { useAccount, useCoState } = Jazz;
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const clerk = useClerk();
const [auth, state] = useJazzClerkAuth(clerk);
@@ -23,12 +26,12 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
<div key={error}>{error}</div>
))}
{clerk.user && auth ? (
<JazzProvider
<Jazz.Provider
auth={auth}
peer="wss://cloud.jazz.tools/?key=minimal-auth-clerk-example@garden.co"
>
{children}
</JazzProvider>
</Jazz.Provider>
) : (
<SignInButton />
)}

View File

@@ -1,28 +1,5 @@
# file-share-svelte
## 0.0.9
### Patch Changes
- jazz-tools@0.8.50
- jazz-svelte@0.8.50
## 0.0.8
### Patch Changes
- jazz-tools@0.8.49
- jazz-svelte@0.8.49
## 0.0.7
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-svelte@0.8.48
## 0.0.6
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "file-share-svelte",
"version": "0.0.9",
"version": "0.0.6",
"private": true,
"type": "module",
"scripts": {

View File

@@ -3,7 +3,7 @@
import { SharedFile } from '$lib/schema';
import { FileStream } from 'jazz-tools';
import { File, FileDown, Trash2, Link2 } from 'lucide-svelte';
import { useAccount } from 'jazz-svelte';
import { useAccount } from '$lib/jazz';
import { toast } from 'svelte-sonner';
import { formatFileSize } from '$lib/utils';

View File

@@ -0,0 +1,7 @@
import { createJazzApp } from 'jazz-svelte';
import { FileShareAccount } from './schema';
export const { useAccount, useCoState, useAcceptInvite, useAccountOrGuest, Provider } =
createJazzApp({
AccountSchema: FileShareAccount
});

View File

@@ -1,17 +1,8 @@
<script lang="ts" module>
declare module 'jazz-svelte' {
interface Register {
Account: FileShareAccount;
}
}
</script>
<script lang="ts">
import { JazzProvider } from 'jazz-svelte';
import { Provider } from '$lib/jazz';
import { PasskeyAuthBasicUI, usePasskeyAuth } from 'jazz-svelte';
import { Toaster } from 'svelte-sonner';
import '../app.css';
import { FileShareAccount } from '$lib/schema';
let { children } = $props();
const auth = usePasskeyAuth({
@@ -33,13 +24,9 @@
</div>
{/if}
{#if auth.current}
<JazzProvider
AccountSchema={FileShareAccount}
auth={auth.current}
peer="wss://cloud.jazz.tools/?key=file-share-svelte@garden.co"
>
<Provider auth={auth.current} peer="wss://cloud.jazz.tools/?key=file-share-svelte@garden.co">
<div class="min-h-screen bg-gray-100">
{@render children()}
</div>
</JazzProvider>
</Provider>
{/if}

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { useAccount, useCoState } from 'jazz-svelte';
import { useAccount, useCoState } from '$lib/jazz';
import { SharedFile, ListOfSharedFiles } from '$lib/schema';
import { createInviteLink } from 'jazz-svelte';
import { FileStream } from 'jazz-tools';

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { page } from '$app/stores';
import { useAccount, useCoState } from 'jazz-svelte';
import { useAccount, useCoState } from '$lib/jazz';
import { SharedFile } from '$lib/schema';
import { File, FileDown, Link2 } from 'lucide-svelte';
import type { ID } from 'jazz-tools';

View File

@@ -1,31 +1,5 @@
# form
## 0.0.19
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.18
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.17
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.16
### Patch Changes

View File

@@ -15,8 +15,6 @@ which has the same structure as `BubbleTeaOrder`, but with all fields set to `op
When the user is ready to submit the order, we treat `DraftBubbleTeaOrder` as a "real order" by
converting it into a `BubbleTeaOrder`.
[See the full guide here.](https://jazz.tools/docs/react/design-patterns/form)
## Installing & running the example locally
(This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation))
@@ -49,4 +47,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 adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<JazzProvider>` provider component in [./src/main.tsx](./src/main.tsx).
You can also run a local sync server by running `npx jazz-run sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/main.tsx](./src/main.tsx).

View File

@@ -1,7 +1,7 @@
{
"name": "form",
"private": true,
"version": "0.0.19",
"version": "0.0.16",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,9 +1,9 @@
import { useIframeHashRouter } from "hash-slash";
import { useAccount } from "jazz-react";
import { ID } from "jazz-tools";
import { CreateOrder } from "./CreateOrder.tsx";
import { EditOrder } from "./EditOrder.tsx";
import { Orders } from "./Orders.tsx";
import { useAccount } from "./main";
import { BubbleTeaOrder } from "./schema.ts";
function App() {

View File

@@ -1,10 +1,10 @@
import { useIframeHashRouter } from "hash-slash";
import { useAccount, useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { useState } from "react";
import { Errors } from "./Errors.tsx";
import { LinkToHome } from "./LinkToHome.tsx";
import { OrderForm } from "./OrderForm.tsx";
import { useAccount, useCoState } from "./main.tsx";
import {
BubbleTeaOrder,
DraftBubbleTeaOrder,
@@ -32,9 +32,9 @@ export function CreateOrder() {
// reset the draft
me.root.draft = DraftBubbleTeaOrder.create(
{
addOns: ListOfBubbleTeaAddOns.create([], me),
addOns: ListOfBubbleTeaAddOns.create([], { owner: me }),
},
me,
{ owner: me },
);
router.navigate("/");

View File

@@ -1,4 +1,4 @@
import { useAccount } from "jazz-react";
import { useAccount } from "./main.tsx";
export function DraftIndicator() {
const { me } = useAccount({

View File

@@ -1,8 +1,8 @@
import { useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { LinkToHome } from "./LinkToHome.tsx";
import { OrderForm } from "./OrderForm.tsx";
import { OrderThumbnail } from "./OrderThumbnail.tsx";
import { useCoState } from "./main.tsx";
import { BubbleTeaOrder } from "./schema.ts";
export function EditOrder(props: { id: ID<BubbleTeaOrder> }) {

View File

@@ -1,6 +1,6 @@
import { useAccount } from "jazz-react";
import { DraftIndicator } from "./DraftIndicator.tsx";
import { OrderThumbnail } from "./OrderThumbnail.tsx";
import { useAccount } from "./main.tsx";
export function Orders() {
const { me } = useAccount({

View File

@@ -1,22 +1,27 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { DemoAuthBasicUI, createJazzReactApp, useDemoAuth } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { JazzAccount } from "./schema.ts";
const Jazz = createJazzReactApp({
AccountSchema: JazzAccount,
});
export const { useAccount, useCoState } = Jazz;
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
<Jazz.Provider
auth={auth}
peer="wss://cloud.jazz.tools/?key=form-example@garden.co"
AccountSchema={JazzAccount}
>
{children}
</JazzProvider>
</Jazz.Provider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Form" state={authState} />
@@ -25,12 +30,6 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
);
}
declare module "jazz-react" {
interface Register {
Account: JazzAccount;
}
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<JazzAndAuth>

View File

@@ -69,19 +69,18 @@ export class AccountRoot extends CoMap {
export class JazzAccount extends Account {
root = co.ref(AccountRoot);
migrate() {
const account = this;
migrate(this: JazzAccount) {
if (!this._refs.root) {
const orders = ListOfBubbleTeaOrders.create([], account);
const ownership = { owner: this };
const orders = ListOfBubbleTeaOrders.create([], ownership);
const draft = DraftBubbleTeaOrder.create(
{
addOns: ListOfBubbleTeaAddOns.create([], account),
addOns: ListOfBubbleTeaAddOns.create([], ownership),
},
account,
ownership,
);
this.root = AccountRoot.create({ draft, orders }, account);
this.root = AccountRoot.create({ draft, orders }, ownership);
}
}
}

View File

@@ -1,31 +1,5 @@
# image-upload
## 0.0.21
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.20
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.19
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.18
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "image-upload",
"private": true,
"version": "0.0.21",
"version": "0.0.18",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,5 @@
import { useAccount } from "jazz-react";
import ImageUpload from "./ImageUpload.tsx";
import { useAccount } from "./main";
function App() {
const { me, logOut } = useAccount();

View File

@@ -1,7 +1,8 @@
import { createImage } from "jazz-browser-media-images";
import { ProgressiveImg, useAccount } from "jazz-react";
import { ProgressiveImg } from "jazz-react";
import { ImageDefinition } from "jazz-tools";
import { ChangeEvent, useRef } from "react";
import { useAccount } from "./main.tsx";
function Image({ image }: { image: ImageDefinition }) {
return (

View File

@@ -1,22 +1,27 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { DemoAuthBasicUI, createJazzReactApp, useDemoAuth } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { JazzAccount } from "./schema.ts";
const Jazz = createJazzReactApp({
AccountSchema: JazzAccount,
});
export const { useAccount, useCoState } = Jazz;
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
<Jazz.Provider
auth={auth}
peer="wss://cloud.jazz.tools/?key=image-upload-example@garden.co"
AccountSchema={JazzAccount}
>
{children}
</JazzProvider>
</Jazz.Provider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Image upload" state={authState} />
@@ -25,12 +30,6 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
);
}
declare module "jazz-react" {
interface Register {
Account: JazzAccount;
}
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<JazzAndAuth>

View File

@@ -1,29 +1,5 @@
# jazz-example-inspector
## 0.0.92
### Patch Changes
- Updated dependencies [43378ef]
- cojson@0.8.50
- cojson-transport-ws@0.8.50
## 0.0.91
### Patch Changes
- Updated dependencies [25dfd90]
- cojson@0.8.49
- cojson-transport-ws@0.8.49
## 0.0.90
### Patch Changes
- Updated dependencies [10ea733]
- cojson@0.8.48
- cojson-transport-ws@0.8.48
## 0.0.89
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-inspector",
"private": true,
"version": "0.0.92",
"version": "0.0.89",
"type": "module",
"scripts": {
"dev": "vite",
@@ -16,8 +16,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cojson": "workspace:0.8.50",
"cojson-transport-ws": "workspace:0.8.50",
"cojson": "workspace:0.8.45",
"cojson-transport-ws": "workspace:0.8.45",
"hash-slash": "workspace:0.2.1",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",

View File

@@ -1,28 +1,5 @@
# jazz-example-musicplayer
## 0.0.44
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
## 0.0.43
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
## 0.0.42
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react@0.8.48
## 0.0.41
### Patch Changes

View File

@@ -34,4 +34,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 cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<JazzProvider>` provider component in [./src/2_main.tsx](./src/2_main.tsx).
You can also run a local sync server by running `npx cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/2_main.tsx](./src/2_main.tsx).

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.44",
"version": "0.0.41",
"type": "module",
"scripts": {
"dev": "vite",
@@ -18,8 +18,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.8.50",
"jazz-tools": "workspace:0.8.50",
"jazz-react": "workspace:0.8.45",
"jazz-tools": "workspace:0.8.45",
"lucide-react": "^0.274.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View File

@@ -10,20 +10,26 @@ import { PlayerControls } from "./components/PlayerControls";
import "./index.css";
import { MusicaAccount } from "@/1_schema";
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { DemoAuthBasicUI, createJazzReactApp, useDemoAuth } from "jazz-react";
import { useUploadExampleData } from "./lib/useUploadExampleData";
/**
* Walkthrough: The top-level provider `<JazzProvider/>`
* Walkthrough: The top-level provider `<Jazz.Provider/>`
*
* This shows how to use the top-level provider `<JazzProvider/>`,
* This shows how to use the top-level provider `<Jazz.Provider/>`,
* which provides the rest of the app with a controlled account (used through `useAccount` later).
* Here we use `DemoAuth` which is great for prototyping you app without wasting time on figuring out
* the best way to do auth.
*
* `<JazzProvider/>` also runs our account migration
* `<Jazz.Provider/>` also runs our account migration
*/
const Jazz = createJazzReactApp({
AccountSchema: MusicaAccount,
});
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
function Main() {
const mediaPlayer = useMediaPlayer();
@@ -64,25 +70,18 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
return (
<>
<JazzProvider
<Jazz.Provider
storage={["singleTabOPFS", "indexedDB"]}
auth={auth}
peer={peer}
AccountSchema={MusicaAccount}
>
{children}
</JazzProvider>
</Jazz.Provider>
<DemoAuthBasicUI appName="Jazz Music Player" state={state} />
</>
);
}
declare module "jazz-react" {
interface Register {
Account: MusicaAccount;
}
}
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<JazzAndAuth>

View File

@@ -1,8 +1,9 @@
import { useToast } from "@/hooks/use-toast";
import { createInviteLink, useAccount, useCoState } from "jazz-react";
import { createInviteLink } from "jazz-react";
import { ID } from "jazz-tools";
import { useNavigate, useParams } from "react-router";
import { Playlist } from "./1_schema";
import { useAccount, useCoState } from "./2_main";
import { createNewPlaylist, uploadMusicTracks } from "./4_actions";
import { MediaPlayer } from "./5_useMediaPlayer";
import { FileUploadButton } from "./components/FileUploadButton";

View File

@@ -29,23 +29,25 @@ export async function uploadMusicTracks(
for (const file of files) {
// The ownership object defines the user that owns the created coValues
// We are creating a group for each CoValue in order to be able to share them via Playlist
const group = Group.create(account);
const ownership = {
owner: Group.create({ owner: account }),
};
const data = await getAudioFileData(file);
// We transform the file blob into a FileStream
// making it a collaborative value that is encrypted, easy
// to share across devices and users and available offline!
const fileStream = await FileStream.createFromBlob(file, group);
const fileStream = await FileStream.createFromBlob(file, ownership);
const musicTrack = MusicTrack.create(
{
file: fileStream,
duration: data.duration,
waveform: MusicTrackWaveform.create({ data: data.waveform }, group),
waveform: MusicTrackWaveform.create({ data: data.waveform }, ownership),
title: file.name,
},
group,
ownership,
);
// The newly created musicTrack can be associated to the
@@ -58,14 +60,16 @@ export async function createNewPlaylist(account: MusicaAccount) {
// Since playlists are meant to be shared we associate them
// to a group which will contain the keys required to get
// access to the "owned" values
const playlistGroup = Group.create(account);
const playlistGroup = Group.create({ owner: account });
const ownership = { owner: playlistGroup };
const playlist = Playlist.create(
{
title: "New Playlist",
tracks: ListOfTracks.create([], playlistGroup),
tracks: ListOfTracks.create([], ownership),
},
playlistGroup,
ownership,
);
// Again, we associate the new playlist to the
@@ -108,6 +112,7 @@ export async function addTrackToPlaylist(
*
* Doing this for backwards compatibility for when the Group inheritance wasn't possible
*/
const ownership = { owner: playlist._owner };
const blob = await FileStream.loadAsBlob(track._refs.file.id, account);
const waveform = await MusicTrackWaveform.load(
track._refs.waveform.id,
@@ -119,16 +124,13 @@ export async function addTrackToPlaylist(
const trackClone = MusicTrack.create(
{
file: await FileStream.createFromBlob(blob, playlist._owner),
file: await FileStream.createFromBlob(blob, ownership),
duration: track.duration,
waveform: MusicTrackWaveform.create(
{ data: waveform.data },
playlist._owner,
),
waveform: MusicTrackWaveform.create({ data: waveform.data }, ownership),
title: track.title,
sourceTrack: track,
},
playlist._owner,
ownership,
);
playlist.tracks?.push(trackClone);

View File

@@ -1,9 +1,9 @@
import { MusicTrack, Playlist } from "@/1_schema";
import { usePlayMedia } from "@/lib/audio/usePlayMedia";
import { usePlayState } from "@/lib/audio/usePlayState";
import { useAccount } from "jazz-react";
import { FileStream, ID } from "jazz-tools";
import { useRef, useState } from "react";
import { useAccount } from "./2_main";
import { updateActivePlaylist, updateActiveTrack } from "./4_actions";
import { getNextTrack, getPrevTrack } from "./lib/getters";

View File

@@ -1,8 +1,8 @@
import { useAcceptInvite, useAccount } from "jazz-react";
import { ID } from "jazz-tools";
import { useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { Playlist } from "./1_schema";
import { useAcceptInvite, useAccount } from "./2_main";
export function InvitePage() {
const navigate = useNavigate();

View File

@@ -1,4 +1,4 @@
import { useAccount } from "jazz-react";
import { useAccount } from "@/2_main";
import { Button } from "./ui/button";
export function LogoutButton() {

View File

@@ -1,4 +1,5 @@
import { MusicTrack, Playlist } from "@/1_schema";
import { useAccount, useCoState } from "@/2_main";
import { addTrackToPlaylist } from "@/4_actions";
import {
DropdownMenu,
@@ -7,7 +8,6 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { cn } from "@/lib/utils";
import { useAccount, useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { MoreHorizontal } from "lucide-react";
import { MusicTrackTitleInput } from "./MusicTrackTitleInput";

View File

@@ -1,6 +1,6 @@
import { MusicTrack } from "@/1_schema";
import { useCoState } from "@/2_main";
import { updateMusicTrackTitle } from "@/4_actions";
import { useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { ChangeEvent, useState } from "react";

View File

@@ -1,9 +1,9 @@
import { MusicTrack } from "@/1_schema";
import { useAccount, useCoState } from "@/2_main";
import { MediaPlayer } from "@/5_useMediaPlayer";
import { useMediaEndListener } from "@/lib/audio/useMediaEndListener";
import { usePlayState } from "@/lib/audio/usePlayState";
import { useKeyboardListener } from "@/lib/useKeyboardListener";
import { useAccount, useCoState } from "jazz-react";
import { Pause, Play, SkipBack, SkipForward } from "lucide-react";
import { Waveform } from "./Waveform";

View File

@@ -1,6 +1,6 @@
import { Playlist } from "@/1_schema";
import { useCoState } from "@/2_main";
import { updatePlaylistTitle } from "@/4_actions";
import { useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { ChangeEvent, useState } from "react";

View File

@@ -1,4 +1,4 @@
import { useAccount } from "jazz-react";
import { useAccount } from "@/2_main";
import { useNavigate, useParams } from "react-router";
export function SidePanel() {

View File

@@ -1,7 +1,7 @@
import { MusicTrack, MusicTrackWaveform } from "@/1_schema";
import { useCoState } from "@/2_main";
import { usePlayerCurrentTime } from "@/lib/audio/usePlayerCurrentTime";
import { cn } from "@/lib/utils";
import { useCoState } from "jazz-react";
export function Waveform(props: { track: MusicTrack; height: number }) {
const { track, height } = props;

View File

@@ -1,8 +1,8 @@
import { useAccount } from "jazz-react";
// eslint-disable-next-line react-compiler/react-compiler
/* eslint-disable react-hooks/exhaustive-deps */
import { useEffect } from "react";
import { MusicaAccount } from "../1_schema";
import { useAccount } from "../2_main";
import { uploadMusicTracks } from "../4_actions";
export function useUploadExampleData() {

View File

@@ -1,31 +1,5 @@
# jazz-example-onboarding
## 0.0.25
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.24
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.23
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.22
### Patch Changes

View File

@@ -32,4 +32,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 cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<JazzProvider>` provider component in [./src/main.tsx](./src/main.tsx).
You can also run a local sync server by running `npx cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/main.tsx](./src/main.tsx).

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-onboarding",
"private": true,
"version": "0.0.25",
"version": "0.0.22",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,9 +1,9 @@
import { Button } from "@/components/Button.tsx";
import { useAcceptInvite, useAccount, useCoState } from "@/main.tsx";
import { EmployeeList } from "@/pages/EmployeeList.tsx";
import { EmployeeOnboading } from "@/pages/EmployeeOnboarding.tsx";
import { NewEmployee } from "@/pages/NewEmployee.tsx";
import { CoEmployee, EmployeeCoList } from "@/schema.ts";
import { useAcceptInvite, useAccount, useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { useEffect } from "react";
import {

View File

@@ -1,10 +1,15 @@
import App from "@/App.tsx";
import "@/index.css";
import { HRAccount } from "@/schema.ts";
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { DemoAuthBasicUI, createJazzReactApp, useDemoAuth } from "jazz-react";
import React from "react";
import ReactDOM from "react-dom/client";
const Jazz = createJazzReactApp({
AccountSchema: HRAccount,
});
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
const peer =
(new URL(window.location.href).searchParams.get(
"peer",
@@ -15,9 +20,9 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider AccountSchema={HRAccount} auth={auth} peer={peer}>
<Jazz.Provider auth={auth} peer={peer}>
{children}
</JazzProvider>
</Jazz.Provider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Jazz Onboarding" state={authState} />
)}
@@ -25,12 +30,6 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
);
}
declare module "jazz-react" {
interface Register {
Account: HRAccount;
}
}
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<JazzAndAuth>

View File

@@ -1,8 +1,8 @@
import { NavLink } from "@/components/NavLink.tsx";
import { NavigateButton } from "@/components/NavigateBack.tsx";
import { Stack } from "@/components/Stack.tsx";
import { useCoState } from "@/main.tsx";
import { CoEmployee, EmployeeCoList } from "@/schema.ts";
import { useCoState } from "jazz-react";
import { ID } from "jazz-tools";
export function EmployeeList({

View File

@@ -2,8 +2,8 @@ import { Button } from "@/components/Button.tsx";
import { NavigateBack } from "@/components/NavigateBack.tsx";
import { Stack } from "@/components/Stack.tsx";
import { TextInput } from "@/components/TextInput.tsx";
import { useCoState } from "@/main.tsx";
import { createImage } from "jazz-browser-media-images";
import { useCoState } from "jazz-react";
import { ProgressiveImg, createInviteLink } from "jazz-react";
import { CoMap, ID } from "jazz-tools";
import { ChangeEvent, ReactNode, useCallback } from "react";

View File

@@ -2,7 +2,7 @@ import { Button } from "@/components/Button.tsx";
import { NavigateBack } from "@/components/NavigateBack.tsx";
import { Stack } from "@/components/Stack.tsx";
import { TextInput } from "@/components/TextInput.tsx";
import { useAccount, useCoState } from "jazz-react";
import { useAccount, useCoState } from "@/main.tsx";
import { Group, ID } from "jazz-tools";
import { useCallback, useState } from "react";
import { useNavigate } from "react-router-dom";

View File

@@ -1,28 +1,5 @@
# organization
## 0.0.17
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
## 0.0.16
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
## 0.0.15
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react@0.8.48
## 0.0.14
### Patch Changes

View File

@@ -37,4 +37,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 adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<JazzProvider>` provider component in [./src/main.tsx](./src/main.tsx).
You can also run a local sync server by running `npx jazz-run sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/main.tsx](./src/main.tsx).

View File

@@ -1,7 +1,7 @@
{
"name": "organization",
"private": true,
"version": "0.0.17",
"version": "0.0.14",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,6 +1,6 @@
import { useAcceptInvite, useAccount } from "jazz-react";
import { ID } from "jazz-tools";
import { useNavigate } from "react-router";
import { useAcceptInvite, useAccount } from "./main.tsx";
import { Organization } from "./schema.ts";
export function AcceptInvitePage() {

View File

@@ -1,7 +1,7 @@
import { useAccount } from "jazz-react";
import { Layout } from "./Layout.tsx";
import { CreateOrganization } from "./components/CreateOrganization.tsx";
import { Heading } from "./components/Heading.tsx";
import { useAccount } from "./main.tsx";
export function HomePage() {
const { me } = useAccount({

View File

@@ -1,5 +1,5 @@
import { useAccount } from "jazz-react";
import { UserIcon } from "lucide-react";
import { useAccount } from "./main.tsx";
export function Layout({ children }: { children: React.ReactNode }) {
const { me, logOut } = useAccount({

View File

@@ -1,22 +1,34 @@
import { useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { useParams } from "react-router";
import { Layout } from "./Layout.tsx";
import { CreateProject } from "./components/CreateProject.tsx";
import { Heading } from "./components/Heading.tsx";
import { InviteLink } from "./components/InviteLink.tsx";
import { OrganizationMembers } from "./components/OrganizationMembers.tsx";
import { RequestJoinButton } from "./components/RequestJoinButton.tsx";
import { useCoState } from "./main.tsx";
import { Organization } from "./schema.ts";
export function OrganizationPage() {
const paramOrganizationId = useParams<{ organizationId: ID<Organization> }>()
.organizationId;
const organization = useCoState(Organization, paramOrganizationId, {
projects: [],
});
const organization = useCoState(Organization, paramOrganizationId, {});
if (!organization) return <p>Loading organization...</p>;
const organizationWithContent = useCoState(
Organization,
paramOrganizationId,
{
content: {
projects: [],
},
},
);
if (!organization) {
return <p>Loading organization...</p>;
}
const readOnlyAccess = organization && !organizationWithContent;
return (
<Layout>
@@ -28,44 +40,46 @@ export function OrganizationPage() {
<div className="flex justify-between items-center">
<h2>Members</h2>
{organization._owner?.myRole() === "admin" && (
<InviteLink organization={organization} />
{readOnlyAccess && (
<RequestJoinButton organizationId={organization.id} />
)}
</div>
</div>
<div className="divide-y">
<OrganizationMembers organization={organization} />
</div>
{organizationWithContent && (
<div className="divide-y">
<OrganizationMembers organization={organizationWithContent} />
</div>
)}
</div>
<div className="rounded-lg border shadow-sm bg-white dark:bg-stone-925">
<div className="border-b px-4 py-5 sm:px-6">
<h2>Projects</h2>
</div>
<div className="divide-y">
{organization.projects.length > 0 ? (
organization.projects.map((project) =>
project ? (
<strong
key={project.id}
className="px-4 py-5 sm:px-6 font-medium block"
>
{project.name}
</strong>
) : null,
)
) : (
<p className="col-span-full text-center px-4 py-8 sm:px-6">
You have no projects yet.
</p>
)}
<div className="p-4 sm:p-6">
<CreateProject organization={organization} />
{organizationWithContent && (
<div className="rounded-lg border shadow-sm bg-white dark:bg-stone-925">
<div className="border-b px-4 py-5 sm:px-6">
<h2>Projects</h2>
</div>
<div className="divide-y">
{organizationWithContent.content.projects.length > 0 ? (
organizationWithContent.content.projects.map((project) =>
project ? (
<strong
key={project.id}
className="px-4 py-5 sm:px-6 font-medium block"
>
{project.name}
</strong>
) : null,
)
) : (
<p className="col-span-full text-center px-4 py-8 sm:px-6">
You have no projects yet.
</p>
)}
<div className="p-4 sm:p-6">
<CreateProject organization={organization} />
</div>
</div>
</div>
</div>
<div></div>
)}
</div>
</Layout>
);

View File

@@ -1,8 +1,8 @@
import { useAccount, useCoState } from "jazz-react";
import { Group, ID } from "jazz-tools";
import { useState } from "react";
import { useNavigate } from "react-router";
import { DraftOrganization, ListOfProjects, Organization } from "../schema.ts";
import { useAccount, useCoState } from "../main.tsx";
import { DraftOrganization, createOrganization } from "../schema.ts";
import { Errors } from "./Errors.tsx";
import { OrganizationForm } from "./OrganizationForm.tsx";
@@ -25,16 +25,17 @@ export function CreateOrganization() {
const group = Group.create({ owner: me });
me.root.organizations.push(draft as Organization);
const organization = createOrganization(me, draft.name ?? "");
me.root.organizations.push(organization);
me.root.draftOrganization = DraftOrganization.create(
{
projects: ListOfProjects.create([], { owner: group }),
name: "",
},
{ owner: group },
);
navigate(`/organizations/${draft.id}`);
navigate(`/organizations/${organization.id}`);
};
return (

View File

@@ -11,11 +11,14 @@ export function CreateProject({
const onSave = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!organization?.projects) return;
if (!organization?.content?.projects) return;
if (name.length > 0) {
const project = Project.create({ name }, { owner: organization._owner });
organization.projects.push(project);
const project = Project.create(
{ name },
{ owner: organization.content._owner },
);
organization.content.projects.push(project);
setName("");
}
};

View File

@@ -1,44 +0,0 @@
import { createInviteLink } from "jazz-react";
import { CheckIcon, CopyIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { Organization } from "../schema.ts";
export function InviteLink({ organization }: { organization: Organization }) {
const [inviteLink, setInviteLink] = useState<string>();
let [copyCount, setCopyCount] = useState(0);
let copied = copyCount > 0;
useEffect(() => {
if (organization) {
setInviteLink(createInviteLink(organization, "writer"));
}
}, [organization.id]);
useEffect(() => {
if (copyCount > 0) {
let timeout = setTimeout(() => setCopyCount(0), 1000);
return () => {
clearTimeout(timeout);
};
}
}, [copyCount]);
const copyUrl = () => {
if (inviteLink) {
navigator.clipboard.writeText(inviteLink).then(() => {
setCopyCount((count) => count + 1);
});
}
};
return (
<button
type="button"
className="inline-flex items-center gap-2 text-blue-500 dark:text-blue-400"
onClick={copyUrl}
>
{copied ? <CheckIcon size={16} /> : <CopyIcon size={16} />}
Copy invite link
</button>
);
}

View File

@@ -1,11 +1,31 @@
import { useCoState } from "jazz-react";
import { Account, Group, ID } from "jazz-tools";
import { Organization } from "../schema.ts";
import { useCoState } from "../main.tsx";
import {
JoinOrganizationRequest,
ListOfJoinOrganizationRequests,
Organization,
OrganizationContent,
acceptJoinOrganizationRequest,
} from "../schema.ts";
export function OrganizationMembers({
organization,
}: { organization: Organization }) {
const group = organization._owner.castAs(Group);
const privateContent = useCoState(
OrganizationContent,
organization._refs.content.id,
{},
);
const joinRequests = useCoState(
ListOfJoinOrganizationRequests,
organization._refs.joinRequests.id,
[{}],
);
if (!privateContent) return null;
const group = privateContent._owner.castAs(Group);
const myRole = group.myRole();
return (
<>
@@ -16,6 +36,10 @@ export function OrganizationMembers({
role={member.role}
/>
))}
{myRole === "admin" &&
joinRequests?.map((request) => (
<JoinRequest key={request.id} request={request} />
))}
</>
);
}
@@ -33,3 +57,31 @@ function Member({
</div>
);
}
function JoinRequest({
request,
}: {
request: JoinOrganizationRequest;
}) {
const account = useCoState(Account, request._refs.account.id, {
profile: {},
});
function handleAccept() {
acceptJoinOrganizationRequest(request);
}
if (!account?.profile) return;
return (
<div className="px-4 py-4 sm:px-6 flex justify-between items-center">
<strong className="font-medium">{account.profile.name}</strong>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-4 rounded"
onClick={handleAccept}
>
Accept
</button>
</div>
);
}

View File

@@ -1,8 +1,8 @@
import { useAccount } from "jazz-react";
import { ID } from "jazz-tools";
import { UsersIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router";
import { useAccount } from "../main.tsx";
import { Organization } from "../schema.ts";
export function OrganizationSelector({ className }: { className?: string }) {

View File

@@ -0,0 +1,43 @@
import { ID } from "jazz-tools";
import { useAccount, useCoState } from "../main.tsx";
import { Organization, requestJoinOrganization } from "../schema.ts";
export function RequestJoinButton({
organizationId,
}: { organizationId: ID<Organization> }) {
const organization = useCoState(Organization, organizationId, {
joinRequests: [{}],
});
const { me } = useAccount();
if (!organization) return null;
const alreadyRequested = organization.joinRequests.some(
(request) => request._refs.account.id === me.id,
);
if (alreadyRequested) {
return (
<button
type="button"
className="inline-flex items-center gap-2 text-blue-500 dark:text-blue-400"
disabled
>
Waiting for approval
</button>
);
}
return (
<button
type="button"
className="inline-flex items-center gap-2 text-blue-500 dark:text-blue-400"
onClick={() => {
requestJoinOrganization(me, organization);
}}
>
Request to join
</button>
);
}

View File

@@ -1,4 +1,4 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { DemoAuthBasicUI, createJazzReactApp, useDemoAuth } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
@@ -8,6 +8,11 @@ import { HomePage } from "./HomePage.tsx";
import { OrganizationPage } from "./OrganizationPage.tsx";
import { JazzAccount } from "./schema.ts";
const Jazz = createJazzReactApp({
AccountSchema: JazzAccount,
});
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
function Router() {
const router = createHashRouter([
{
@@ -32,13 +37,12 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
return (
<>
<JazzProvider
AccountSchema={JazzAccount}
<Jazz.Provider
auth={auth}
peer="wss://cloud.jazz.tools/?key=organization-example@garden.co"
>
{children}
</JazzProvider>
</Jazz.Provider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Organization" state={authState} />
@@ -47,12 +51,6 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
);
}
declare module "jazz-react" {
interface Register {
Account: JazzAccount;
}
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<JazzAndAuth>

View File

@@ -1,3 +1,4 @@
import { createInviteLink, parseInviteLink } from "jazz-react";
import { Account, CoList, CoMap, Group, co } from "jazz-tools";
export class Project extends CoMap {
@@ -7,13 +8,19 @@ export class Project extends CoMap {
export class ListOfProjects extends CoList.Of(co.ref(Project)) {}
export class Organization extends CoMap {
// everyone is a reader
name = co.string;
content = co.ref(OrganizationContent); // limited access
joinRequests = co.ref(ListOfJoinOrganizationRequests); // writeOnly access
joinRequestsInviteLink = co.string;
}
export class OrganizationContent extends CoMap {
projects = co.ref(ListOfProjects);
}
export class DraftOrganization extends CoMap {
name = co.optional.string;
projects = co.ref(ListOfProjects);
validate() {
const errors: string[] = [];
@@ -28,8 +35,24 @@ export class DraftOrganization extends CoMap {
}
}
export class JoinOrganizationRequest extends CoMap {
organization = co.ref(Organization);
account = co.ref(Account);
}
export class ListOfOrganizations extends CoList.Of(co.ref(Organization)) {}
export class ListOfJoinOrganizationRequests extends CoList.Of(
co.ref(JoinOrganizationRequest),
) {
removeRequest(request: JoinOrganizationRequest) {
const index = this.findIndex((r) => r?.id === request.id);
if (index !== -1) {
this.splice(index, 1);
}
}
}
export class JazzAccountRoot extends CoMap {
organizations = co.ref(ListOfOrganizations);
draftOrganization = co.ref(DraftOrganization);
@@ -45,24 +68,18 @@ export class JazzAccount extends Account {
};
const draftOrganization = DraftOrganization.create(
{
projects: ListOfProjects.create([], draftOrganizationOwnership),
name: "",
},
draftOrganizationOwnership,
);
const initialOrganizationOwnership = {
owner: Group.create({ owner: this }),
};
const organizations = ListOfOrganizations.create(
[
Organization.create(
{
name: this.profile?.name
? `${this.profile.name}'s projects`
: "Your projects",
projects: ListOfProjects.create([], initialOrganizationOwnership),
},
initialOrganizationOwnership,
createOrganization(
this,
this.profile?.name
? `${this.profile.name}'s projects`
: "Your projects",
),
],
{ owner: this },
@@ -78,3 +95,93 @@ export class JazzAccount extends Account {
}
}
}
export function createOrganization(account: Account, name: string) {
const organizationOwnership = {
owner: Group.create({ owner: account }),
};
// We give read only access to everyone so that guests can see the organization name
// and request to join
organizationOwnership.owner.addMember("everyone", "reader");
const joinRequestsGroup = Group.create({ owner: account });
const joinRequests = ListOfJoinOrganizationRequests.create([], {
owner: joinRequestsGroup,
});
// We give write only access to the join requests so that only the organization admins
// can see and manage join requests
const joinRequestsInviteLink = createInviteLink(joinRequests, "writeOnly");
const contentOwnership = {
owner: Group.create({ owner: account }),
};
const projects = ListOfProjects.create([], contentOwnership);
const content = OrganizationContent.create({ projects }, contentOwnership);
const organization = Organization.create(
{ name, joinRequests, joinRequestsInviteLink, content },
organizationOwnership,
);
return organization;
}
export async function acceptJoinOrganizationRequest(
joinRequest: JoinOrganizationRequest,
) {
const result = await joinRequest.ensureLoaded({
organization: {
joinRequests: [],
content: {},
},
account: {},
});
if (!result) return;
const { organization, account } = result;
organization.joinRequests.removeRequest(joinRequest);
const organizationContentGroup = organization.content._owner.castAs(Group);
organizationContentGroup.addMember(account, "writer");
}
export async function requestJoinOrganization(
account: JazzAccount,
organization: Organization,
) {
const parsedLink = parseInviteLink<ListOfJoinOrganizationRequests>(
organization.joinRequestsInviteLink,
);
if (!parsedLink) return;
const joinRequests = await account.acceptInvite(
parsedLink.valueID,
parsedLink.inviteSecret,
ListOfJoinOrganizationRequests,
);
if (!joinRequests) return;
const joinRequestsGroup = joinRequests._owner.castAs(Group);
const joinRequest = JoinOrganizationRequest.create(
{ organization, account },
{ owner: joinRequestsGroup },
);
joinRequests.push(joinRequest);
const result = await account.ensureLoaded({ root: { organizations: [] } });
if (!result) return;
const { root } = result;
root.organizations.push(organization);
}

View File

@@ -1,23 +1,5 @@
# passkey-svelte
## 0.0.13
### Patch Changes
- jazz-svelte@0.8.50
## 0.0.12
### Patch Changes
- jazz-svelte@0.8.49
## 0.0.11
### Patch Changes
- jazz-svelte@0.8.48
## 0.0.10
### Patch Changes

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