Skip to content

Commit

Permalink
Merge pull request #2 from LesFabricants/first-release
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyLenglet authored Jun 3, 2024
2 parents 6583350 + f7fe124 commit eaf84a0
Show file tree
Hide file tree
Showing 12 changed files with 852 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/cut-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
# this assumes that you have created a personal access token
# (PAT) and configured it as a GitHub action secret named
# `MY_RELEASE_PLEASE_TOKEN` (this secret name is not important).
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
# this is a built-in strategy in release-please, see "Action Inputs"
# for more options
release-type: simple
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
dist/
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20
187 changes: 187 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# Fluxie

Super small helper class meant to greatly simplify the creation of a flux architecture using angular service, complete with redux devtool and caching support

**Warning**
**Requires 2Kb of available space, make sure you have the space before installing !**

## Syntax

> The `store.ts` files is a base class that can be extended by services to add the necessary methods and properties required to handle a centralized state
```ts
interface UsersState {
users: IUser[];
selection: IUser[];
}

@Injectable({ providedIn: "root" })
export class UsersService extends Store<UsersState> {
constructor() {
super({ users: [], selection: [] }, { cache: true, storeName: "Users" });
}
}
```

## Parameters

- `initialState`
the initial state of the store

- `options` (optional)
- `storeName`
name to use for the store
- `cache`
a boolean value, if true, enables the caching of the state via IndexedDB

## Examples

`users.service.ts`

```ts
interface UsersState {
users: IUser[];
selection: IUser[];
}

@Injectable({ providedIn: "root" })
export class UsersService extends Store<UsersState> {
constructor() {
super({ users: [], selection: [] });
}

users = computed(() => {
return this.store.select().users.map((user) => new User(user));
});

users$: Observable<User[]> = this.store.select$.pipe(
map((state) => state.users),
map((users) => users.map((user) => new User(user)))
);

get() {
this.http.get<IUser[]>(`${URL}/users`).subscribe((users) => {
this.setUsers(users);
});
}

updateRole(id: string, role: RoleEnum) {
this.http.patch<IUser>(`${URL}/users/${id}`, { role }).subscribe((user) => {
this.updateUser(id, user);
});
}

setUsers(users: IUser[]) {
this.setState("set users", (state) => ({
...state,
users,
}));
}

updateUser(id: string, user: IUser) {
this.setState("update user", (state) => {
return {
...state,
users: state.users.map((currentUser) =>
currentUser.id === id ? user : currentUser
),
};
});
}

toggleUserSelection(user: IUser) {
this.setState("toggle user selection", (state) => {
const newSelection = state.selection;
const index = newSelection.findIndex(({ id }) => id === user.id);

if (index > -1) {
newSelection.splice(index, 1);
} else {
newSelection.push(user);
}

return {
...state,
selection: newSelection,
};
});
}

emptySelection() {
this.setState("empty selection", (state) => ({
...state,
selection: [],
}));
}
}
```

`users.component.ts`

```ts
@Component({
selector: "oney-users",
templateUrl: "./users.component.html",
styleUrls: ["./users.component.scss"],
})
export class UsersComponent implements OnInit {
protected usersService = inject(UsersService);

constructor() {}

ngOnInit(): void {
this.usersService.get();
}
}
```

Via Observables:

```html
<article>
@for (user of usersService.users$ | async) {
<app-user [user]="user"></app-user>
}
</article>
```

Via Signals:

```html
<article>
@for (user of usersService.users()) {
<app-user [user]="user"></app-user>
}
</article>
```

Note: You are free to organize this however you want, although the recommended organization would be to split your service file into 3 files:

- `users.service.ts`
makes http calls, and is overall the file your application calls to request state changes

- in the example above, this would contain get and updateRole

- `users.query.ts`
contains all of the variations of the filtered state used throughout the application

- in the example above, this would contain users and users$

- `users.store.ts`
the file that is only called by `users.service.ts` and `users.query.ts`, this is the file that contains the store initialization code, the state typing, and every method that directly alters the state

- in the example above, this would contain setUsers, updateUser, toggleUserSelection and emptySelection

## Devtool support

In order to add devtool support add this snippet to your `app.module.ts`, right after your imports:

```ts
if (!environment.production) {
enableReduxDevtools();
}
```

Your redux devtool menu will now show all your store actions, the option `storeName` will be the store instance name there (defaults to the class name if not provided), and every service extending the `Store` class will be its own instance in the dropdown at the top.

![The redux devtool window is opened, showing the store called "Users", in the "action" section, "set users" and "toggle user selection" are shown being called multiple times](assets/devtool.png)
Binary file added assets/devtool.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions esbuild.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { build } from "esbuild";
import pkg from "./package.json" with { type: "json" };

build({
entryPoints: ["src/main.ts"],
outdir: "dist",
format: "esm",
bundle: true,
minify: true,
treeShaking: true,
external: [...Object.keys(pkg.peerDependencies)],
logLevel: "info",
});
Loading

0 comments on commit eaf84a0

Please sign in to comment.