Skip to content

Commit

Permalink
Merge pull request #27 from osovv/move-to-reatom
Browse files Browse the repository at this point in the history
Move to reatom
  • Loading branch information
osovv authored Jan 9, 2024
2 parents 47bb06c + 74c2c6f commit d774c4d
Show file tree
Hide file tree
Showing 31 changed files with 413 additions and 305 deletions.
4 changes: 2 additions & 2 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ module.exports = configure({
presets.prettier(),
presets.typescript(),
presets.react({ newJSXTransform: true }),
presets.effector(),
],
extend: {
extends: ['plugin:storybook/recommended'],
plugins: ['@reatom'],
extends: ['plugin:storybook/recommended', 'plugin:@reatom/recommended'],
overrides: [
{
files: ['vite.config.ts', 'src/**/*.stories.tsx'],
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ jobs:

steps:
- uses: actions/checkout@v3
- name: Use Node.js 16.16.0
- name: Use Node.js 18.17.0
uses: actions/setup-node@v3
with:
node-version: 16.16.0
node-version: 18.17.0
cache: 'yarn'
- name: Install dependencies
run: |
Expand Down
3 changes: 0 additions & 3 deletions babel.config.cjs

This file was deleted.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
"@dnd-kit/sortable": "^7.0.1",
"@heroicons/react": "^2.0.13",
"@material-tailwind/react": "^1.2.4",
"@reatom/framework": "^3.4.30",
"@reatom/npm-react": "^3.8.1",
"@reatom/persist-web-storage": "^3.2.3",
"@tanstack/react-router": "^1.2.2",
"@types/localforage": "^0.0.34",
"atomic-router": "^0.7.1",
"atomic-router-react": "^0.8.0",
"classnames": "^2.3.2",
"compose-function": "^3.0.3",
"effector": "^22.3.0",
"effector-react": "^22.3.4",
"history": "^5.3.0",
"localforage": "^1.10.0",
"react": "^18.2.0",
Expand All @@ -32,6 +32,7 @@
},
"devDependencies": {
"@babel/core": "^7.19.6",
"@reatom/eslint-plugin": "^3.4.3",
"@rollup/plugin-babel": "^6.0.2",
"@storybook/addon-actions": "^6.5.13",
"@storybook/addon-essentials": "^6.5.13",
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@

Tasks are stored in IndexedDB.

This app is built with with [React](https://reactjs.org/) [TypeScript](https://www.typescriptlang.org/), [Vite](https://vitejs.dev/), [TailwindCSS](https://tailwindcss.com/), [Material Tailwind](https://www.material-tailwind.com/), [Effector](https://effector.dev), [Atomic Router](https://atomic-router.github.io/), [Storybook](https://storybook.js.org/) and [GitHub Actions](https://docs.github.com/en/actions). Deployed with [Vercel](https://vercel.app).
This app is built with with [React](https://reactjs.org/) [TypeScript](https://www.typescriptlang.org/), [Vite](https://vitejs.dev/), [TailwindCSS](https://tailwindcss.com/), [Material Tailwind](https://www.material-tailwind.com/), [Reatom](https://reatom.dev), [TanStack Router](https://tanstack.com/router/v1), [Storybook](https://storybook.js.org/) and [GitHub Actions](https://docs.github.com/en/actions). Deployed with [Vercel](https://vercel.app).
5 changes: 2 additions & 3 deletions src/app/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import compose from 'compose-function';
import { withEffectorScope } from './with-effector-scope';
import { withRouting } from './with-routing';
import { withReatomContext } from './with-reatom-context';
import { withTheme } from './with-theme';

export const withProviders = compose(withTheme, withRouting, withEffectorScope);
export const withProviders = compose(withTheme, withReatomContext);
8 changes: 0 additions & 8 deletions src/app/providers/with-effector-scope.tsx

This file was deleted.

10 changes: 10 additions & 0 deletions src/app/providers/with-reatom-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createCtx } from '@reatom/core';
import { reatomContext } from '@reatom/npm-react';

const ctx = createCtx();

export const withReatomContext = (children: () => React.ReactNode) => () => {
return (
<reatomContext.Provider value={ctx}>{children()}</reatomContext.Provider>
);
};
18 changes: 0 additions & 18 deletions src/app/providers/with-routing.tsx

This file was deleted.

90 changes: 48 additions & 42 deletions src/entities/task/model.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
import { combine, createEvent } from 'effector';
import { action, atom } from '@reatom/framework';
import { withLocalStorage } from '@reatom/persist-web-storage';
import { arrayMove } from '~/shared/lib/array';
import { createLocalStorageStore } from '~/shared/lib/effector-localstorage';
import { Id } from '~/shared/lib/id';
import { Optional } from '~/shared/lib/typescript';

type TaskStatus = 'active' | 'completed';

export interface Filter {
status: TaskStatus | undefined;
}

const DEFAULT_FILTER: Filter = {
status: undefined,
};

export const { $store: $filter, getLocalStorageValueFx: getFilterValueFx } =
createLocalStorageStore<Filter>('filter', DEFAULT_FILTER);

type TaskId = Id;

export interface Task {
Expand All @@ -32,22 +21,15 @@ export type TaskDataWithoutStatus = Omit<TaskData, 'status'>;

export type TaskDataOptional = Optional<TaskData>;

const updatedTask = (task: Task, updatedTaskData: TaskDataOptional): Task => ({
...task,
...updatedTaskData,
});

export const taskUpdated = createEvent<{
id: TaskId;
data: TaskDataOptional;
}>();
export interface Filter {
status: TaskStatus | undefined;
}

export const taskMoved = createEvent<{
from: number;
to: number;
}>();
const DEFAULT_FILTER: Filter = {
status: undefined,
};

const initialTasks: Array<Task> = [
const INITIAL_TASKS: Array<Task> = [
{
id: '1',
status: 'active',
Expand All @@ -61,26 +43,50 @@ const initialTasks: Array<Task> = [
},
];

export const { $store: $tasks, getLocalStorageValueFx: getTasksValueFx } =
createLocalStorageStore('tasks', initialTasks);
export const filterAtom = atom(DEFAULT_FILTER, 'filterAtom').pipe(
withLocalStorage('filter'),
);

export const $visibleTasks = combine($tasks, $filter, (tasks, filter) =>
tasks.filter(
(task) => filter.status === undefined || filter.status === task.status,
),
export const tasksAtom = atom(INITIAL_TASKS, 'tasksAtom').pipe(
withLocalStorage('tasks'),
);

$tasks.on(
taskUpdated,
(currentTasks, { id: updatedTaskId, data: updatedTaskData }) =>
currentTasks.map((task) => {
if (task.id === updatedTaskId) {
return updatedTask(task, updatedTaskData);
export const visibleTasksAtom = atom((ctx) => {
const tasks = ctx.spy(tasksAtom);

const filter = ctx.spy(filterAtom);

return tasks.filter(
(task) => filter.status === undefined || filter.status === task.status,
);
}, 'visibleTasksAtom');

const updatedTask = (task: Task, updatedTaskData: TaskDataOptional): Task => ({
...task,
...updatedTaskData,
});

export const updateTask = action(
(ctx, { id, data }: { id: TaskId; data: TaskDataOptional }) => {
const oldTasks = ctx.get(tasksAtom);

const newTasks = oldTasks.map((task) => {
if (task.id === id) {
return updatedTask(task, data);
}
return task;
}),
});

tasksAtom(ctx, newTasks);
},
'updateTask',
);

$tasks.on(taskMoved, (currentTasks, { from, to }) =>
arrayMove(currentTasks, { from, to }),
export const moveTask = action(
(ctx, { from, to }: { from: number; to: number }) => {
const oldTasks = ctx.get(tasksAtom);
const newTasks = arrayMove(oldTasks, { from, to });
tasksAtom(ctx, newTasks);
},
'moveTask',
);
34 changes: 15 additions & 19 deletions src/entities/task/ui/task-card/ui.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
import { reatomContext, useCreateCtx } from '@reatom/npm-react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { fork } from 'effector';
import { Provider } from 'effector-react/scope';
import { $tasks } from '../../model';
import { taskModel } from '../..';
import { TaskCard } from './ui';

export default {
title: 'Entities/Task/TaskCard',
component: TaskCard,
decorators: [
(storyFn) => {
const scope = fork({
values: [
[
$tasks,
[
{
id: '1',
status: 'active',
title: 'Make a leather wallet',
description: 'Check YouTube for tutorial',
},
],
],
],
});
const ctx = useCreateCtx((ctx) => {});

Check warning on line 11 in src/entities/task/ui/task-card/ui.stories.tsx

View workflow job for this annotation

GitHub Actions / build

'ctx' is defined but never used. Allowed unused args must match /^_/u

return <Provider value={scope}>{storyFn()}</Provider>;
taskModel.tasksAtom(ctx, [
{
id: '1',
status: 'active',
title: 'Make a leather wallet',
description: 'Check YouTube for tutorial',
},
]);

return (
<reatomContext.Provider value={ctx}>{storyFn()}</reatomContext.Provider>
);
},
],
} as ComponentMeta<typeof TaskCard>;
Expand Down
16 changes: 8 additions & 8 deletions src/entities/task/ui/task-card/ui.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Typography } from '@material-tailwind/react';
import { useAtom } from '@reatom/npm-react';
import cn from 'classnames';
import { useStoreMap } from 'effector-react/scope';
import React from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { getEntityById } from '~/shared/lib/effector';
import { getEntityById } from '~/shared/lib/entity';
import { mergeRefs } from '~/shared/lib/react';
import { $tasks, Task } from '../../model';
import { Task, tasksAtom } from '../../model';

export interface TaskCardProps {
id: Task['id'];
Expand All @@ -24,10 +24,10 @@ export const TaskCard = ({
onDelete,
onEdit,
}: TaskCardProps) => {
const task = useStoreMap({
store: $tasks,
keys: [id],
fn: (tasks, [taskId]) => getEntityById(tasks, taskId),
const [task] = useAtom((ctx) => {
const tasks = ctx.spy(tasksAtom);

return getEntityById(tasks, id);
});

const idStr = React.useMemo(() => `task-card-${id}`, [id]);
Expand Down Expand Up @@ -67,7 +67,7 @@ export const TaskCard = ({
{task.title}
</Typography>
<Typography variant='small' className='truncate text-gray-500'>
{task.description}
{task.description ?? ''}
</Typography>
<div className='absolute top-0 right-0 flex gap-1 bg-white opacity-0 group-focus-within:opacity-100 group-hover:opacity-100 group-focus:bg-gray-100 '>
{EditSlot}
Expand Down
9 changes: 5 additions & 4 deletions src/entities/task/ui/task-editor/ui.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { reatomContext, useCreateCtx } from '@reatom/npm-react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { fork } from 'effector';
import { Provider } from 'effector-react/scope';
import { TaskEditor } from './ui';

export default {
Expand All @@ -18,9 +17,11 @@ export default {
},
decorators: [
(storyFn) => {
const scope = fork();
const ctx = useCreateCtx();

return <Provider value={scope}>{storyFn()}</Provider>;
return (
<reatomContext.Provider value={ctx}>{storyFn()}</reatomContext.Provider>
);
},
],
} as ComponentMeta<typeof TaskEditor>;
Expand Down
23 changes: 12 additions & 11 deletions src/features/add-task/model.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { createEvent } from 'effector';
import { action } from '@reatom/framework';
import { taskModel } from '~/entities/task';
import { TaskDataWithoutStatus } from '~/entities/task/model';
import { getId } from '~/shared/lib/id';

const taskCreated = createEvent<taskModel.Task>();

export const taskCreatedByUser =
taskCreated.prepend<taskModel.TaskDataWithoutStatus>((taskData) => ({
export const createTask = action((ctx, data: TaskDataWithoutStatus) => {
const newTask = {
id: getId(),
status: 'active',
...taskData,
}));
...data,
} as taskModel.Task;

const tasks = ctx.get(taskModel.tasksAtom);

const newTasks = [...tasks, newTask];

taskModel.$tasks.on(taskCreated, (currentTasks, newTask) => [
...currentTasks,
newTask,
]);
taskModel.tasksAtom(ctx, newTasks);
}, 'createTask');
6 changes: 3 additions & 3 deletions src/features/add-task/ui.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Button } from '@material-tailwind/react';
import { useUnit } from 'effector-react/scope';
import { useAction } from '@reatom/npm-react';
import { useCallback, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { TaskEditor, taskModel } from '~/entities/task';
import { taskCreatedByUser } from './model';
import { createTask } from './model';

export const AddTask = () => {
const [showForm, setShowForm] = useState(false);
Expand All @@ -13,7 +13,7 @@ export const AddTask = () => {

const submitButtonText = 'Add task';

const onSubmit = useUnit(taskCreatedByUser);
const onSubmit = useAction(createTask);

const handleSubmit = (payload: taskModel.TaskDataWithoutStatus) => {
setEditorKey((key) => key + 1);
Expand Down
Loading

1 comment on commit d774c4d

@vercel
Copy link

@vercel vercel bot commented on d774c4d Jan 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

todo – ./

todo-osovv.vercel.app
todo-beta-azure.vercel.app
todo-git-main-osovv.vercel.app

Please sign in to comment.