From 5430d8daa44fb78d65bfa2824b697945c9077b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 18 Jan 2025 00:54:42 +0100 Subject: [PATCH] 0.1.0 (#1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Julien Valverdé Reviewed-on: https://gitea:3000/Thilawyn/reffuse/pulls/1 --- .gitea/workflows/lint.yaml | 6 +- .gitea/workflows/publish.yaml | 12 +- .gitea/workflows/test-build.yaml | 8 +- README.md | 11 +- bun.lockb | Bin 0 -> 152424 bytes bunfig.toml | 2 + package.json | 6 + packages/example/.gitignore | 24 ++ packages/example/README.md | 50 +++ packages/example/eslint.config.js | 28 ++ packages/example/index.html | 13 + packages/example/package.json | 41 +++ packages/example/public/vite.svg | 1 + packages/example/src/domain/Todo.ts | 26 ++ packages/example/src/domain/index.ts | 1 + packages/example/src/index.css | 0 packages/example/src/main.tsx | 36 ++ packages/example/src/reffuse.ts | 13 + packages/example/src/routeTree.gen.ts | 134 ++++++++ packages/example/src/routes/__root.tsx | 27 ++ packages/example/src/routes/count.tsx | 27 ++ packages/example/src/routes/index.tsx | 34 ++ packages/example/src/routes/time.tsx | 49 +++ packages/example/src/services/index.ts | 1 + packages/example/src/todos/reffuse.ts | 7 + .../example/src/todos/services/TodosState.ts | 69 ++++ packages/example/src/todos/services/index.ts | 1 + packages/example/src/todos/views/VNewTodo.tsx | 52 +++ packages/example/src/todos/views/VTodo.tsx | 56 ++++ packages/example/src/todos/views/VTodos.tsx | 36 ++ packages/example/src/vite-env.d.ts | 1 + packages/example/tsconfig.app.json | 30 ++ packages/example/tsconfig.json | 7 + packages/example/tsconfig.node.json | 24 ++ packages/example/vite.config.ts | 19 ++ packages/reffuse/README.md | 12 + packages/reffuse/package.json | 8 +- packages/reffuse/src/Reffuse.ts | 309 ++++++++++++++++++ packages/reffuse/src/ReffuseContext.tsx | 72 ++++ packages/reffuse/src/ReffuseRuntime.tsx | 15 + packages/reffuse/src/SetStateAction.ts | 12 + packages/reffuse/src/index.ts | 4 + 42 files changed, 1271 insertions(+), 13 deletions(-) create mode 100755 bun.lockb create mode 100644 bunfig.toml create mode 100644 packages/example/.gitignore create mode 100644 packages/example/README.md create mode 100644 packages/example/eslint.config.js create mode 100644 packages/example/index.html create mode 100644 packages/example/package.json create mode 100644 packages/example/public/vite.svg create mode 100644 packages/example/src/domain/Todo.ts create mode 100644 packages/example/src/domain/index.ts create mode 100644 packages/example/src/index.css create mode 100644 packages/example/src/main.tsx create mode 100644 packages/example/src/reffuse.ts create mode 100644 packages/example/src/routeTree.gen.ts create mode 100644 packages/example/src/routes/__root.tsx create mode 100644 packages/example/src/routes/count.tsx create mode 100644 packages/example/src/routes/index.tsx create mode 100644 packages/example/src/routes/time.tsx create mode 100644 packages/example/src/services/index.ts create mode 100644 packages/example/src/todos/reffuse.ts create mode 100644 packages/example/src/todos/services/TodosState.ts create mode 100644 packages/example/src/todos/services/index.ts create mode 100644 packages/example/src/todos/views/VNewTodo.tsx create mode 100644 packages/example/src/todos/views/VTodo.tsx create mode 100644 packages/example/src/todos/views/VTodos.tsx create mode 100644 packages/example/src/vite-env.d.ts create mode 100644 packages/example/tsconfig.app.json create mode 100644 packages/example/tsconfig.json create mode 100644 packages/example/tsconfig.node.json create mode 100644 packages/example/vite.config.ts create mode 100644 packages/reffuse/README.md create mode 100644 packages/reffuse/src/Reffuse.ts create mode 100644 packages/reffuse/src/ReffuseContext.tsx create mode 100644 packages/reffuse/src/ReffuseRuntime.tsx create mode 100644 packages/reffuse/src/SetStateAction.ts diff --git a/.gitea/workflows/lint.yaml b/.gitea/workflows/lint.yaml index 87cc2d2..7efb5a0 100644 --- a/.gitea/workflows/lint.yaml +++ b/.gitea/workflows/lint.yaml @@ -8,13 +8,9 @@ jobs: steps: - name: Setup Bun uses: oven-sh/setup-bun@v1 - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: "20" - name: Clone repo uses: actions/checkout@v4 - name: Install dependencies run: bun install --frozen-lockfile - name: Lint TypeScript - run: npm run lint:tsc + run: bun run lint:tsc diff --git a/.gitea/workflows/publish.yaml b/.gitea/workflows/publish.yaml index 1b33cdb..7289f97 100644 --- a/.gitea/workflows/publish.yaml +++ b/.gitea/workflows/publish.yaml @@ -15,14 +15,18 @@ jobs: uses: actions/setup-node@v4 with: node-version: "20" + registry-url: "https://registry.npmjs.org" - name: Clone repo uses: actions/checkout@v4 - name: Install dependencies run: bun install --frozen-lockfile - name: Build - run: npm run build + run: | + cd packages/reffuse + bun run build - name: Publish run: | - npm config set @thilawyn:registry https://git.valverde.cloud/api/packages/thilawyn/npm/ - npm config set -- //git.valverde.cloud/api/packages/thilawyn/npm/:_authToken "${{ vars.NODE_AUTH_TOKEN }}" - npm publish + cd packages/reffuse + npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitea/workflows/test-build.yaml b/.gitea/workflows/test-build.yaml index 0f61dc5..ee239a7 100644 --- a/.gitea/workflows/test-build.yaml +++ b/.gitea/workflows/test-build.yaml @@ -18,6 +18,10 @@ jobs: - name: Install dependencies run: bun install --frozen-lockfile - name: Build - run: npm run build + run: | + cd packages/reffuse + bun run build - name: Pack - run: npm pack --dry-run + run: | + cd packages/reffuse + npm pack --dry-run diff --git a/README.md b/README.md index 7d92783..dab847a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ # Reffuse -Effect integration for React +[Effect-TS](https://effect.website/) integration for React 19+ with the aim of integrating the Effect context system within React's component hierarchy, while avoiding touching React's internals. + +This library is in early development. While it is (almost) feature complete and mostly usable, expect bugs and quirks. Things are still being ironed out, so ideas and criticisms are more than welcome. + +Documentation is currently being written. In the meantime, you can take a look at the `packages/example` directory. + +## Dependencies +(needs to be manually installed) +- `effect` +- `react` 19+ diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..ea854204c0c64f0ae4a18816aaeeca67a4f65389 GIT binary patch literal 152424 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ȹpnxY`avo7_oZ0JXg0wmCT!e@lX?_zIqhX6BJ~ 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 literal 0 HcmV?d00001 diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..4fe1e6e --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[install.scopes] +"@thilawyn" = "https://git.valverde.cloud/api/packages/thilawyn/npm/" diff --git a/package.json b/package.json index 8781544..6d4beab 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,14 @@ "private": true, "workspaces": ["./packages/*"], "scripts": { + "lint:tsc": "bun run --filter '*' lint:tsc", "clean:cache": "rm -f tsconfig.tsbuildinfo", "clean:dist": "rm -rf dist", "clean:node": "rm -rf node_modules" + }, + "devDependencies": { + "npm-check-updates": "^17.1.13", + "npm-sort": "^0.0.4", + "typescript": "^5.7.3" } } diff --git a/packages/example/.gitignore b/packages/example/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/packages/example/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/example/README.md b/packages/example/README.md new file mode 100644 index 0000000..74872fd --- /dev/null +++ b/packages/example/README.md @@ -0,0 +1,50 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default tseslint.config({ + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Optionally add `...tseslint.configs.stylisticTypeChecked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: + +```js +// eslint.config.js +import react from 'eslint-plugin-react' + +export default tseslint.config({ + // Set the react version + settings: { react: { version: '18.3' } }, + plugins: { + // Add the react plugin + react, + }, + rules: { + // other rules... + // Enable its recommended rules + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + }, +}) +``` diff --git a/packages/example/eslint.config.js b/packages/example/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/packages/example/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/packages/example/index.html b/packages/example/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/packages/example/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/packages/example/package.json b/packages/example/package.json new file mode 100644 index 0000000..d73afec --- /dev/null +++ b/packages/example/package.json @@ -0,0 +1,41 @@ +{ + "name": "@reffuse/example", + "version": "0.0.0", + "type": "module", + "private": true, + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint:tsc": "tsc --noEmit", + "lint:eslint": "eslint .", + "preview": "vite preview" + }, + "devDependencies": { + "@eslint/js": "^9.17.0", + "@tanstack/react-router": "^1.95.3", + "@tanstack/router-devtools": "^1.95.3", + "@tanstack/router-plugin": "^1.95.3", + "@thilawyn/thilaschema": "^0.1.4", + "@types/react": "^19.0.4", + "@types/react-dom": "^19.0.2", + "@vitejs/plugin-react": "^4.3.4", + "effect": "^3.12.1", + "eslint": "^9.17.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.16", + "globals": "^15.14.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "reffuse": "workspace:*", + "typescript-eslint": "^8.18.2", + "vite": "^6.0.5" + }, + "dependencies": { + "@effect/platform": "^0.73.1", + "@effect/platform-browser": "^0.52.1", + "@radix-ui/themes": "^3.1.6", + "@typed/id": "^0.17.1", + "lucide-react": "^0.471.1", + "mobx": "^6.13.5" + } +} diff --git a/packages/example/public/vite.svg b/packages/example/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/packages/example/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/example/src/domain/Todo.ts b/packages/example/src/domain/Todo.ts new file mode 100644 index 0000000..cd120b3 --- /dev/null +++ b/packages/example/src/domain/Todo.ts @@ -0,0 +1,26 @@ +import { ThSchema } from "@thilawyn/thilaschema" +import { GetRandomValues, makeUuid4 } from "@typed/id" +import { Effect, Schema } from "effect" + + +export class Todo extends Schema.Class("Todo")({ + _tag: Schema.tag("Todo"), + id: Schema.String, + content: Schema.String, + completedAt: Schema.OptionFromSelf(Schema.DateTimeUtcFromSelf), +}) {} + + +export const TodoFromJsonStruct = Schema.Struct({ + ...Todo.fields, + completedAt: Schema.Option(Schema.DateTimeUtc), +}).pipe( + ThSchema.assertEncodedJsonifiable +) + +export const TodoFromJson = TodoFromJsonStruct.pipe(Schema.compose(Todo)) + + +export const generateUniqueID = makeUuid4.pipe( + Effect.provide(GetRandomValues.CryptoRandom) +) diff --git a/packages/example/src/domain/index.ts b/packages/example/src/domain/index.ts new file mode 100644 index 0000000..8d70c13 --- /dev/null +++ b/packages/example/src/domain/index.ts @@ -0,0 +1 @@ +export * as Todo from "./Todo" diff --git a/packages/example/src/index.css b/packages/example/src/index.css new file mode 100644 index 0000000..e69de29 diff --git a/packages/example/src/main.tsx b/packages/example/src/main.tsx new file mode 100644 index 0000000..f3e3987 --- /dev/null +++ b/packages/example/src/main.tsx @@ -0,0 +1,36 @@ +import { FetchHttpClient } from "@effect/platform" +import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser" +import { createRouter, RouterProvider } from "@tanstack/react-router" +import { ReffuseRuntime } from "@thilawyn/reffuse" +import { Layer } from "effect" +import { StrictMode } from "react" +import { createRoot } from "react-dom/client" +import { GlobalContext } from "./reffuse" +import { routeTree } from "./routeTree.gen" + + +const layer = Layer.empty.pipe( + Layer.provideMerge(Clipboard.layer), + Layer.provideMerge(Geolocation.layer), + Layer.provideMerge(Permissions.layer), + Layer.provideMerge(FetchHttpClient.layer), +) + +const router = createRouter({ routeTree }) + +declare module "@tanstack/react-router" { + interface Register { + router: typeof router + } +} + + +createRoot(document.getElementById("root")!).render( + + + + + + + +) diff --git a/packages/example/src/reffuse.ts b/packages/example/src/reffuse.ts new file mode 100644 index 0000000..beba3d3 --- /dev/null +++ b/packages/example/src/reffuse.ts @@ -0,0 +1,13 @@ +import { HttpClient } from "@effect/platform" +import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser" +import { Reffuse, ReffuseContext } from "reffuse" + + +export const GlobalContext = ReffuseContext.make< + | Clipboard.Clipboard + | Geolocation.Geolocation + | Permissions.Permissions + | HttpClient.HttpClient +>() + +export const R = Reffuse.make(GlobalContext) diff --git a/packages/example/src/routeTree.gen.ts b/packages/example/src/routeTree.gen.ts new file mode 100644 index 0000000..2cec328 --- /dev/null +++ b/packages/example/src/routeTree.gen.ts @@ -0,0 +1,134 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +// Import Routes + +import { Route as rootRoute } from './routes/__root' +import { Route as TimeImport } from './routes/time' +import { Route as CountImport } from './routes/count' +import { Route as IndexImport } from './routes/index' + +// Create/Update Routes + +const TimeRoute = TimeImport.update({ + id: '/time', + path: '/time', + getParentRoute: () => rootRoute, +} as any) + +const CountRoute = CountImport.update({ + id: '/count', + path: '/count', + getParentRoute: () => rootRoute, +} as any) + +const IndexRoute = IndexImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRoute, +} as any) + +// Populate the FileRoutesByPath interface + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexImport + parentRoute: typeof rootRoute + } + '/count': { + id: '/count' + path: '/count' + fullPath: '/count' + preLoaderRoute: typeof CountImport + parentRoute: typeof rootRoute + } + '/time': { + id: '/time' + path: '/time' + fullPath: '/time' + preLoaderRoute: typeof TimeImport + parentRoute: typeof rootRoute + } + } +} + +// Create and export the route tree + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/count': typeof CountRoute + '/time': typeof TimeRoute +} + +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/count': typeof CountRoute + '/time': typeof TimeRoute +} + +export interface FileRoutesById { + __root__: typeof rootRoute + '/': typeof IndexRoute + '/count': typeof CountRoute + '/time': typeof TimeRoute +} + +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/count' | '/time' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/count' | '/time' + id: '__root__' | '/' | '/count' | '/time' + fileRoutesById: FileRoutesById +} + +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + CountRoute: typeof CountRoute + TimeRoute: typeof TimeRoute +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + CountRoute: CountRoute, + TimeRoute: TimeRoute, +} + +export const routeTree = rootRoute + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +/* ROUTE_MANIFEST_START +{ + "routes": { + "__root__": { + "filePath": "__root.tsx", + "children": [ + "/", + "/count", + "/time" + ] + }, + "/": { + "filePath": "index.tsx" + }, + "/count": { + "filePath": "count.tsx" + }, + "/time": { + "filePath": "time.tsx" + } + } +} +ROUTE_MANIFEST_END */ diff --git a/packages/example/src/routes/__root.tsx b/packages/example/src/routes/__root.tsx new file mode 100644 index 0000000..beef033 --- /dev/null +++ b/packages/example/src/routes/__root.tsx @@ -0,0 +1,27 @@ +import { Container, Flex, Theme } from "@radix-ui/themes" +import "@radix-ui/themes/styles.css" +import { createRootRoute, Link, Outlet } from "@tanstack/react-router" +import { TanStackRouterDevtools } from "@tanstack/router-devtools" +import "../index.css" + + +export const Route = createRootRoute({ + component: Root +}) + +function Root() { + return ( + + + + Index + Time + Count + + + + + + + ) +} diff --git a/packages/example/src/routes/count.tsx b/packages/example/src/routes/count.tsx new file mode 100644 index 0000000..77aedc7 --- /dev/null +++ b/packages/example/src/routes/count.tsx @@ -0,0 +1,27 @@ +import { R } from "@/reffuse" +import { createFileRoute } from "@tanstack/react-router" +import { Ref } from "effect" + + +export const Route = createFileRoute("/count")({ + component: Count +}) + +function Count() { + + const runSync = R.useRunSync() + + const countRef = R.useRef(0) + const [count] = R.useRefState(countRef) + + + return ( +
+ {/* +
+ ) + +} diff --git a/packages/example/src/routes/index.tsx b/packages/example/src/routes/index.tsx new file mode 100644 index 0000000..c4caacd --- /dev/null +++ b/packages/example/src/routes/index.tsx @@ -0,0 +1,34 @@ +import { R } from "@/reffuse" +import { TodosContext } from "@/todos/reffuse" +import { TodosState } from "@/todos/services" +import { VTodos } from "@/todos/views/VTodos" +import { Container } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import { Console, Effect, Layer } from "effect" +import { useMemo } from "react" + + +export const Route = createFileRoute("/")({ + component: Index +}) + +function Index() { + + const todosLayer = useMemo(() => Layer.empty.pipe( + Layer.provideMerge(TodosState.make("todos")) + ), []) + + R.useEffect(Effect.addFinalizer(() => Console.log("Effect cleanup")).pipe( + Effect.flatMap(() => Console.log("Effect recalculated")) + )) + + + return ( + + + + + + ) + +} diff --git a/packages/example/src/routes/time.tsx b/packages/example/src/routes/time.tsx new file mode 100644 index 0000000..e2ba3d8 --- /dev/null +++ b/packages/example/src/routes/time.tsx @@ -0,0 +1,49 @@ +import { R } from "@/reffuse" +import { createFileRoute } from "@tanstack/react-router" +import { Console, DateTime, Effect, Ref, Schedule, Stream } from "effect" + + +const timeEverySecond = Stream.repeatEffectWithSchedule( + DateTime.now, + Schedule.intersect(Schedule.forever, Schedule.spaced("1 second")), +) + + +export const Route = createFileRoute("/time")({ + component: Time +}) + +function Time() { + + const timeRef = R.useRefFromEffect(DateTime.now) + + R.useFork(Effect.addFinalizer(() => Console.log("Cleanup")).pipe( + Effect.flatMap(() => + Stream.runForEach(timeEverySecond, v => Ref.set(timeRef, v)) + ) + ), [timeRef]) + // Reffuse.useFork(Effect.addFinalizer(() => Console.log("Cleanup")).pipe( + // Effect.flatMap(() => DateTime.now), + // Effect.flatMap(v => Ref.set(timeRef, v)), + // Effect.repeat(Schedule.intersect( + // Schedule.forever, + // Schedule.spaced("1 second"), + // )), + // ), [timeRef]) + + const [time] = R.useRefState(timeRef) + + + return ( +
+

+ {DateTime.format(time, { + hour: "numeric", + minute: "numeric", + second: "numeric", + })} +

+
+ ) + +} diff --git a/packages/example/src/services/index.ts b/packages/example/src/services/index.ts new file mode 100644 index 0000000..336ce12 --- /dev/null +++ b/packages/example/src/services/index.ts @@ -0,0 +1 @@ +export {} diff --git a/packages/example/src/todos/reffuse.ts b/packages/example/src/todos/reffuse.ts new file mode 100644 index 0000000..8502e12 --- /dev/null +++ b/packages/example/src/todos/reffuse.ts @@ -0,0 +1,7 @@ +import { GlobalContext } from "@/reffuse" +import { Reffuse, ReffuseContext } from "reffuse" +import { TodosState } from "./services" + + +export const TodosContext = ReffuseContext.make() +export const R = Reffuse.make(GlobalContext, TodosContext) diff --git a/packages/example/src/todos/services/TodosState.ts b/packages/example/src/todos/services/TodosState.ts new file mode 100644 index 0000000..d9b2bea --- /dev/null +++ b/packages/example/src/todos/services/TodosState.ts @@ -0,0 +1,69 @@ +import { Todo } from "@/domain" +import { KeyValueStore } from "@effect/platform" +import { BrowserKeyValueStore } from "@effect/platform-browser" +import { PlatformError } from "@effect/platform/Error" +import { Chunk, Context, Effect, identity, Layer, ParseResult, Ref, Schema, SubscriptionRef } from "effect" + + +export class TodosState extends Context.Tag("TodosState")> + + readonly readFromLocalStorage: Effect.Effect + readonly saveToLocalStorage: Effect.Effect + + readonly prepend: (todo: Todo.Todo) => Effect.Effect + readonly replace: (index: number, todo: Todo.Todo) => Effect.Effect + readonly remove: (index: number) => Effect.Effect + // readonly moveUp: (index: number) => Effect.Effect + // readonly moveDown: (index: number) => Effect.Effect +}>() {} + + +export const make = (key: string) => Layer.effect(TodosState, Effect.gen(function*() { + const todos = yield* SubscriptionRef.make(Chunk.empty()) + + const readFromLocalStorage = KeyValueStore.KeyValueStore.pipe( + Effect.flatMap(kv => kv.get(key)), + Effect.flatMap(identity), + Effect.flatMap(Schema.parseJson().pipe( + Schema.compose(Schema.Chunk(Todo.TodoFromJson)), + Schema.decode, + )), + Effect.flatMap(v => Ref.set(todos, v)), + + Effect.catchTag("NoSuchElementException", () => Ref.set(todos, Chunk.empty())), + + Effect.provide(BrowserKeyValueStore.layerLocalStorage), + ) + + const saveToLocalStorage = Effect.all([KeyValueStore.KeyValueStore, todos]).pipe( + Effect.flatMap(([kv, values]) => values.pipe( + Schema.parseJson().pipe( + Schema.compose(Schema.Chunk(Todo.TodoFromJson)), + Schema.encode, + ), + Effect.flatMap(v => kv.set(key, v)), + )), + + Effect.provide(BrowserKeyValueStore.layerLocalStorage), + ) + + const prepend = (todo: Todo.Todo) => Ref.update(todos, Chunk.prepend(todo)) + const replace = (index: number, todo: Todo.Todo) => Ref.update(todos, Chunk.replace(index, todo)) + const remove = (index: number) => Ref.update(todos, Chunk.remove(index)) + + // const moveUp = (index: number) => Effect.gen(function*() { + + // }) + + yield* readFromLocalStorage + + return { + todos, + readFromLocalStorage, + saveToLocalStorage, + prepend, + replace, + remove, + } +})) diff --git a/packages/example/src/todos/services/index.ts b/packages/example/src/todos/services/index.ts new file mode 100644 index 0000000..5d1c39e --- /dev/null +++ b/packages/example/src/todos/services/index.ts @@ -0,0 +1 @@ +export * as TodosState from "./TodosState" diff --git a/packages/example/src/todos/views/VNewTodo.tsx b/packages/example/src/todos/views/VNewTodo.tsx new file mode 100644 index 0000000..fee4f16 --- /dev/null +++ b/packages/example/src/todos/views/VNewTodo.tsx @@ -0,0 +1,52 @@ +import { Todo } from "@/domain" +import { Box, Button, Card, Flex, TextArea } from "@radix-ui/themes" +import { Effect, Option } from "effect" +import { R } from "../reffuse" +import { TodosState } from "../services" + + +export function VNewTodo() { + + const runSync = R.useRunSync() + + const createEmptyTodo = Todo.generateUniqueID.pipe( + Effect.map(id => Todo.Todo.make({ + id, + content: "", + completedAt: Option.none(), + }, true)) + ) + + const todoRef = R.useRefFromEffect(createEmptyTodo) + const [todo, setTodo] = R.useRefState(todoRef) + + + return ( + + + +