From 74e7d8c9e799d012e3f9bae9b3941272885f7829 Mon Sep 17 00:00:00 2001 From: Markus Huggler Date: Fri, 23 Dec 2022 15:15:11 +0100 Subject: [PATCH] feat: adds list editor feat: adds list editor --- .editorconfig | 4 + .../services/mastodon-api-lists.service.ts | 12 ++ .../src/lib/services/mastodon-api.service.ts | 10 +- .../mastolists/src/app/app-routing.module.ts | 26 ++-- projects/mastolists/src/app/app.component.ts | 7 +- .../followings-list-routing.module.ts | 17 --- .../followings-list/followings-list.module.ts | 23 ---- .../followings-matrix-routing.module.ts | 17 --- .../followings-matrix.module.ts | 25 ---- .../followings-table.module.ts | 22 --- .../followings-list-routing.module.ts | 27 ++++ .../src/app/followings/followings.module.ts | 31 +++++ .../list/list.component.html | 0 .../list/list.component.scss | 0 .../list/list.component.spec.ts | 0 .../list/list.component.ts | 0 .../matrix/matrix.component.html | 0 .../matrix/matrix.component.scss | 0 .../matrix/matrix.component.spec.ts | 0 .../matrix/matrix.component.ts | 0 .../table/table.component.html | 0 .../table/table.component.scss | 0 .../table/table.component.spec.ts | 0 .../table/table.component.ts | 0 .../src/app/home/home/home.component.html | 4 +- .../lists-routing.module.ts} | 6 +- .../mastolists/src/app/lists/lists.module.ts | 24 ++++ .../src/app/lists/lists/lists.component.html | 31 +++++ .../src/app/lists/lists/lists.component.scss | 0 .../app/lists/lists/lists.component.spec.ts | 23 ++++ .../src/app/lists/lists/lists.component.ts | 43 ++++++ .../src/app/shared/services/list.service.ts | 25 +++- .../src/app/shared/state/store/actions.ts | 6 + .../src/app/shared/state/store/effects.ts | 56 +++++++- .../src/app/shared/state/store/reducers.ts | 127 ++++++++++-------- .../src/app/sync/sync/sync.component.html | 5 +- 36 files changed, 389 insertions(+), 182 deletions(-) delete mode 100644 projects/mastolists/src/app/followings-list/followings-list-routing.module.ts delete mode 100644 projects/mastolists/src/app/followings-list/followings-list.module.ts delete mode 100644 projects/mastolists/src/app/followings-matrix/followings-matrix-routing.module.ts delete mode 100644 projects/mastolists/src/app/followings-matrix/followings-matrix.module.ts delete mode 100644 projects/mastolists/src/app/followings-table/followings-table.module.ts create mode 100644 projects/mastolists/src/app/followings/followings-list-routing.module.ts create mode 100644 projects/mastolists/src/app/followings/followings.module.ts rename projects/mastolists/src/app/{followings-list => followings}/list/list.component.html (100%) rename projects/mastolists/src/app/{followings-list => followings}/list/list.component.scss (100%) rename projects/mastolists/src/app/{followings-list => followings}/list/list.component.spec.ts (100%) rename projects/mastolists/src/app/{followings-list => followings}/list/list.component.ts (100%) rename projects/mastolists/src/app/{followings-matrix => followings}/matrix/matrix.component.html (100%) rename projects/mastolists/src/app/{followings-matrix => followings}/matrix/matrix.component.scss (100%) rename projects/mastolists/src/app/{followings-matrix => followings}/matrix/matrix.component.spec.ts (100%) rename projects/mastolists/src/app/{followings-matrix => followings}/matrix/matrix.component.ts (100%) rename projects/mastolists/src/app/{followings-table => followings}/table/table.component.html (100%) rename projects/mastolists/src/app/{followings-table => followings}/table/table.component.scss (100%) rename projects/mastolists/src/app/{followings-table => followings}/table/table.component.spec.ts (100%) rename projects/mastolists/src/app/{followings-table => followings}/table/table.component.ts (100%) rename projects/mastolists/src/app/{followings-table/followings-table-routing.module.ts => lists/lists-routing.module.ts} (73%) create mode 100644 projects/mastolists/src/app/lists/lists.module.ts create mode 100644 projects/mastolists/src/app/lists/lists/lists.component.html create mode 100644 projects/mastolists/src/app/lists/lists/lists.component.scss create mode 100644 projects/mastolists/src/app/lists/lists/lists.component.spec.ts create mode 100644 projects/mastolists/src/app/lists/lists/lists.component.ts diff --git a/.editorconfig b/.editorconfig index 0792692..48464db 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,10 @@ trim_trailing_whitespace = true [*.ts] quote_type = single +max_line_length = 180 + +[*.html] +max_line_length = 180 [*.md] max_line_length = off diff --git a/projects/mastodon-api/src/lib/services/mastodon-api-lists.service.ts b/projects/mastodon-api/src/lib/services/mastodon-api-lists.service.ts index 7ce53f5..1cd4842 100644 --- a/projects/mastodon-api/src/lib/services/mastodon-api-lists.service.ts +++ b/projects/mastodon-api/src/lib/services/mastodon-api-lists.service.ts @@ -75,4 +75,16 @@ export class MastodonApiListsService { return this.mastodonApiService .deleteAuthenticated(url, {account_ids: [accountId]}, accessToken); } + + updateList(instanceName: string, accessToken: string, listId: string, newTitle: string) { + const url = `https://${instanceName}/api/v1/lists/${listId}`; + return this.mastodonApiService + .putAuthenticated(url, {title: newTitle}, accessToken); + } + + createList(instanceName: string, accessToken: string, title: string) { + const url = `https://${instanceName}/api/v1/lists`; + return this.mastodonApiService + .postAuthenticated(url, {title}, accessToken); + } } diff --git a/projects/mastodon-api/src/lib/services/mastodon-api.service.ts b/projects/mastodon-api/src/lib/services/mastodon-api.service.ts index 2f4aa5c..831409b 100644 --- a/projects/mastodon-api/src/lib/services/mastodon-api.service.ts +++ b/projects/mastodon-api/src/lib/services/mastodon-api.service.ts @@ -33,7 +33,7 @@ export class MastodonApiService { return this.httpClient.post(url, parameters); } - postAuthenticated(url: string, body: { account_ids: string[] }, accessToken: string) { + postAuthenticated(url: string, body: {}, accessToken: string) { const reqHeader = new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + accessToken @@ -41,6 +41,14 @@ export class MastodonApiService { return this.httpClient.post(url, body, {headers: reqHeader}); } + putAuthenticated(url: string, body: {}, accessToken: string) { + const reqHeader = new HttpHeaders({ + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + accessToken + }); + return this.httpClient.put(url, body, {headers: reqHeader}); + } + deleteAuthenticated(url: string, body: { account_ids: string[] }, accessToken: string) { const reqHeader = new HttpHeaders({ 'Content-Type': 'application/json', diff --git a/projects/mastolists/src/app/app-routing.module.ts b/projects/mastolists/src/app/app-routing.module.ts index c83fe90..1321db6 100644 --- a/projects/mastolists/src/app/app-routing.module.ts +++ b/projects/mastolists/src/app/app-routing.module.ts @@ -8,6 +8,18 @@ const routes: Routes = [ redirectTo: 'home', pathMatch: 'full' }, + { + path: 'list', + redirectTo: 'followings/list', + }, + { + path: 'matrix', + redirectTo: 'followings/matrix', + }, + { + path: 'table', + redirectTo: 'followings/table', + }, { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomeModule), @@ -22,20 +34,16 @@ const routes: Routes = [ canActivate: [AuthGuard], }, { - path: 'list', - loadChildren: () => import('./followings-list/followings-list.module').then(m => m.FollowingsListModule), + path: 'lists', + loadChildren: () => import('./lists/lists.module').then(m => m.ListsModule), canActivate: [AuthGuard], }, { - path: 'matrix', - loadChildren: () => import('./followings-matrix/followings-matrix.module').then(m => m.FollowingsMatrixModule), - canActivate: [AuthGuard], - }, - { - path: 'table', - loadChildren: () => import('./followings-table/followings-table.module').then(m => m.FollowingsTableModule), + path: 'followings', + loadChildren: () => import('./followings/followings.module').then(m => m.FollowingsModule), canActivate: [AuthGuard], }, + ]; @NgModule({ diff --git a/projects/mastolists/src/app/app.component.ts b/projects/mastolists/src/app/app.component.ts index b70f460..7e8615a 100644 --- a/projects/mastolists/src/app/app.component.ts +++ b/projects/mastolists/src/app/app.component.ts @@ -14,9 +14,10 @@ export class AppComponent { navigationItems = [ {title: 'Authorize', link: '/auth'}, {title: 'Stats', link: '/sync'}, - {title: 'List view', link: '/list'}, - {title: 'Matrix View', link: '/matrix'}, - {title: 'Table View', link: '/table'}, + {title: 'Edit Lists', link: '/lists'}, + {title: 'List view', link: '/followings/list'}, + {title: 'Matrix View', link: '/followings/matrix'}, + {title: 'Table View', link: '/followings/table'}, ]; constructor(private store: Store, private persistentStore: PersistentStore) { diff --git a/projects/mastolists/src/app/followings-list/followings-list-routing.module.ts b/projects/mastolists/src/app/followings-list/followings-list-routing.module.ts deleted file mode 100644 index 41950bc..0000000 --- a/projects/mastolists/src/app/followings-list/followings-list-routing.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {RouterModule, Routes} from "@angular/router"; -import {NgModule} from "@angular/core"; -import {ListComponent} from "./list/list.component"; - -const routes: Routes = [ - { - path: '', - component: ListComponent - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class FollowingsListRoutingModule { -} diff --git a/projects/mastolists/src/app/followings-list/followings-list.module.ts b/projects/mastolists/src/app/followings-list/followings-list.module.ts deleted file mode 100644 index bf3b1ce..0000000 --- a/projects/mastolists/src/app/followings-list/followings-list.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {NgModule} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {ListComponent} from './list/list.component'; -import {SharedModule} from '../shared/shared.module'; -import {FollowingsListRoutingModule} from './followings-list-routing.module'; -import {NbListModule, NbToggleModule} from "@nebular/theme"; - - -@NgModule({ - declarations: [ - ListComponent - ], - imports: [ - CommonModule, - SharedModule, - FollowingsListRoutingModule, - // Nebula - NbListModule, - NbToggleModule, - ] -}) -export class FollowingsListModule { -} diff --git a/projects/mastolists/src/app/followings-matrix/followings-matrix-routing.module.ts b/projects/mastolists/src/app/followings-matrix/followings-matrix-routing.module.ts deleted file mode 100644 index c37e87f..0000000 --- a/projects/mastolists/src/app/followings-matrix/followings-matrix-routing.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {RouterModule, Routes} from "@angular/router"; -import {NgModule} from "@angular/core"; -import {MatrixComponent} from "./matrix/matrix.component"; - -const routes: Routes = [ - { - path: '', - component: MatrixComponent, - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class FollowingsMatrixRoutingModule { -} diff --git a/projects/mastolists/src/app/followings-matrix/followings-matrix.module.ts b/projects/mastolists/src/app/followings-matrix/followings-matrix.module.ts deleted file mode 100644 index 37dbcf8..0000000 --- a/projects/mastolists/src/app/followings-matrix/followings-matrix.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {NgModule} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {MatrixComponent} from './matrix/matrix.component'; -import {FollowingsMatrixRoutingModule} from './followings-matrix-routing.module'; -import {SharedModule} from "../shared/shared.module"; -import {NbBadgeModule, NbCheckboxModule, NbPopoverModule} from "@nebular/theme"; - - -@NgModule({ - declarations: [ - MatrixComponent - ], - imports: [ - CommonModule, - SharedModule, - FollowingsMatrixRoutingModule, - // Nebular - NbCheckboxModule, - NbBadgeModule, - NbPopoverModule, - ] - -}) -export class FollowingsMatrixModule { -} diff --git a/projects/mastolists/src/app/followings-table/followings-table.module.ts b/projects/mastolists/src/app/followings-table/followings-table.module.ts deleted file mode 100644 index aebb983..0000000 --- a/projects/mastolists/src/app/followings-table/followings-table.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {NgModule} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {TableComponent} from './table/table.component'; -import {SharedModule} from "../shared/shared.module"; -import {FollowingsTableRoutingModule} from "./followings-table-routing.module"; -import {NbUserModule} from "@nebular/theme"; - - -@NgModule({ - declarations: [ - TableComponent - ], - imports: [ - CommonModule, - SharedModule, - FollowingsTableRoutingModule, - // Nebular - NbUserModule, - ] -}) -export class FollowingsTableModule { -} diff --git a/projects/mastolists/src/app/followings/followings-list-routing.module.ts b/projects/mastolists/src/app/followings/followings-list-routing.module.ts new file mode 100644 index 0000000..b018833 --- /dev/null +++ b/projects/mastolists/src/app/followings/followings-list-routing.module.ts @@ -0,0 +1,27 @@ +import {RouterModule, Routes} from "@angular/router"; +import {NgModule} from "@angular/core"; +import {ListComponent} from "./list/list.component"; +import {MatrixComponent} from "./matrix/matrix.component"; +import {TableComponent} from "./table/table.component"; + +const routes: Routes = [ + { + path: 'list', + component: ListComponent, + }, + { + path: 'matrix', + component: MatrixComponent, + }, + { + path: 'table', + component: TableComponent, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class FollowingsRoutingModule { +} diff --git a/projects/mastolists/src/app/followings/followings.module.ts b/projects/mastolists/src/app/followings/followings.module.ts new file mode 100644 index 0000000..8e5012f --- /dev/null +++ b/projects/mastolists/src/app/followings/followings.module.ts @@ -0,0 +1,31 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {SharedModule} from "../shared/shared.module"; +import {FollowingsRoutingModule} from "./followings-list-routing.module"; +import {NbBadgeModule, NbCheckboxModule, NbIconModule, NbListModule, NbPopoverModule, NbToggleModule, NbUserModule} from "@nebular/theme"; +import {ListComponent} from "./list/list.component"; +import {TableComponent} from "./table/table.component"; +import {MatrixComponent} from "./matrix/matrix.component"; + + +@NgModule({ + declarations: [ + ListComponent, + TableComponent, + MatrixComponent, + ], + imports: [ + CommonModule, + SharedModule, + FollowingsRoutingModule, + // Nebula + NbCheckboxModule, + NbListModule, + NbToggleModule, + NbBadgeModule, + NbPopoverModule, + NbUserModule, + ] +}) +export class FollowingsModule { +} diff --git a/projects/mastolists/src/app/followings-list/list/list.component.html b/projects/mastolists/src/app/followings/list/list.component.html similarity index 100% rename from projects/mastolists/src/app/followings-list/list/list.component.html rename to projects/mastolists/src/app/followings/list/list.component.html diff --git a/projects/mastolists/src/app/followings-list/list/list.component.scss b/projects/mastolists/src/app/followings/list/list.component.scss similarity index 100% rename from projects/mastolists/src/app/followings-list/list/list.component.scss rename to projects/mastolists/src/app/followings/list/list.component.scss diff --git a/projects/mastolists/src/app/followings-list/list/list.component.spec.ts b/projects/mastolists/src/app/followings/list/list.component.spec.ts similarity index 100% rename from projects/mastolists/src/app/followings-list/list/list.component.spec.ts rename to projects/mastolists/src/app/followings/list/list.component.spec.ts diff --git a/projects/mastolists/src/app/followings-list/list/list.component.ts b/projects/mastolists/src/app/followings/list/list.component.ts similarity index 100% rename from projects/mastolists/src/app/followings-list/list/list.component.ts rename to projects/mastolists/src/app/followings/list/list.component.ts diff --git a/projects/mastolists/src/app/followings-matrix/matrix/matrix.component.html b/projects/mastolists/src/app/followings/matrix/matrix.component.html similarity index 100% rename from projects/mastolists/src/app/followings-matrix/matrix/matrix.component.html rename to projects/mastolists/src/app/followings/matrix/matrix.component.html diff --git a/projects/mastolists/src/app/followings-matrix/matrix/matrix.component.scss b/projects/mastolists/src/app/followings/matrix/matrix.component.scss similarity index 100% rename from projects/mastolists/src/app/followings-matrix/matrix/matrix.component.scss rename to projects/mastolists/src/app/followings/matrix/matrix.component.scss diff --git a/projects/mastolists/src/app/followings-matrix/matrix/matrix.component.spec.ts b/projects/mastolists/src/app/followings/matrix/matrix.component.spec.ts similarity index 100% rename from projects/mastolists/src/app/followings-matrix/matrix/matrix.component.spec.ts rename to projects/mastolists/src/app/followings/matrix/matrix.component.spec.ts diff --git a/projects/mastolists/src/app/followings-matrix/matrix/matrix.component.ts b/projects/mastolists/src/app/followings/matrix/matrix.component.ts similarity index 100% rename from projects/mastolists/src/app/followings-matrix/matrix/matrix.component.ts rename to projects/mastolists/src/app/followings/matrix/matrix.component.ts diff --git a/projects/mastolists/src/app/followings-table/table/table.component.html b/projects/mastolists/src/app/followings/table/table.component.html similarity index 100% rename from projects/mastolists/src/app/followings-table/table/table.component.html rename to projects/mastolists/src/app/followings/table/table.component.html diff --git a/projects/mastolists/src/app/followings-table/table/table.component.scss b/projects/mastolists/src/app/followings/table/table.component.scss similarity index 100% rename from projects/mastolists/src/app/followings-table/table/table.component.scss rename to projects/mastolists/src/app/followings/table/table.component.scss diff --git a/projects/mastolists/src/app/followings-table/table/table.component.spec.ts b/projects/mastolists/src/app/followings/table/table.component.spec.ts similarity index 100% rename from projects/mastolists/src/app/followings-table/table/table.component.spec.ts rename to projects/mastolists/src/app/followings/table/table.component.spec.ts diff --git a/projects/mastolists/src/app/followings-table/table/table.component.ts b/projects/mastolists/src/app/followings/table/table.component.ts similarity index 100% rename from projects/mastolists/src/app/followings-table/table/table.component.ts rename to projects/mastolists/src/app/followings/table/table.component.ts diff --git a/projects/mastolists/src/app/home/home/home.component.html b/projects/mastolists/src/app/home/home/home.component.html index 5daae67..604e62a 100644 --- a/projects/mastolists/src/app/home/home/home.component.html +++ b/projects/mastolists/src/app/home/home/home.component.html @@ -8,8 +8,8 @@ diff --git a/projects/mastolists/src/app/followings-table/followings-table-routing.module.ts b/projects/mastolists/src/app/lists/lists-routing.module.ts similarity index 73% rename from projects/mastolists/src/app/followings-table/followings-table-routing.module.ts rename to projects/mastolists/src/app/lists/lists-routing.module.ts index e6fac2b..566fab2 100644 --- a/projects/mastolists/src/app/followings-table/followings-table-routing.module.ts +++ b/projects/mastolists/src/app/lists/lists-routing.module.ts @@ -1,11 +1,11 @@ import {RouterModule, Routes} from "@angular/router"; import {NgModule} from "@angular/core"; -import {TableComponent} from "./table/table.component"; +import {ListsComponent} from "./lists/lists.component"; const routes: Routes = [ { path: '', - component: TableComponent + component: ListsComponent } ]; @@ -13,5 +13,5 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) -export class FollowingsTableRoutingModule { +export class ListsRoutingModule { } diff --git a/projects/mastolists/src/app/lists/lists.module.ts b/projects/mastolists/src/app/lists/lists.module.ts new file mode 100644 index 0000000..e222e02 --- /dev/null +++ b/projects/mastolists/src/app/lists/lists.module.ts @@ -0,0 +1,24 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {ListsComponent} from './lists/lists.component'; +import {SharedModule} from "../shared/shared.module"; +import {ListsRoutingModule} from "./lists-routing.module"; +import {NbSpinnerModule} from "@nebular/theme"; +import {ReactiveFormsModule} from "@angular/forms"; + + +@NgModule({ + declarations: [ + ListsComponent + ], + imports: [ + CommonModule, + SharedModule, + ListsRoutingModule, + ReactiveFormsModule, + // Nebular + NbSpinnerModule, + ] +}) +export class ListsModule { +} diff --git a/projects/mastolists/src/app/lists/lists/lists.component.html b/projects/mastolists/src/app/lists/lists/lists.component.html new file mode 100644 index 0000000..218d674 --- /dev/null +++ b/projects/mastolists/src/app/lists/lists/lists.component.html @@ -0,0 +1,31 @@ + + +

Lists

+
+
+ + +
+
+ + + + + + + + + + +
IdTitle
+ + {{list.id}} + + + +
+
+
diff --git a/projects/mastolists/src/app/lists/lists/lists.component.scss b/projects/mastolists/src/app/lists/lists/lists.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/projects/mastolists/src/app/lists/lists/lists.component.spec.ts b/projects/mastolists/src/app/lists/lists/lists.component.spec.ts new file mode 100644 index 0000000..3e54a58 --- /dev/null +++ b/projects/mastolists/src/app/lists/lists/lists.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ListsComponent } from './lists.component'; + +describe('ListsComponent', () => { + let component: ListsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ListsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ListsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/mastolists/src/app/lists/lists/lists.component.ts b/projects/mastolists/src/app/lists/lists/lists.component.ts new file mode 100644 index 0000000..61ef34f --- /dev/null +++ b/projects/mastolists/src/app/lists/lists/lists.component.ts @@ -0,0 +1,43 @@ +import {Component} from '@angular/core'; +import {List} from 'projects/mastodon-api/src/public-api'; +import {Observable} from "rxjs"; +import {select, Store} from "@ngrx/store"; +import {selectLists} from "../../shared/state/store/selectors"; +import {PersistentStore} from "../../shared/state/persistent/persistent-store.service"; +import {ListActions} from "../../shared/state/store/actions"; +import {FormControl, FormGroup} from "@angular/forms"; + +@Component({ + selector: 'app-lists', + templateUrl: './lists.component.html', + styleUrls: ['./lists.component.scss'] +}) +export class ListsComponent { + + lists$: Observable>; + instanceName: string; + creatingList: boolean = false; + + newListForm = new FormGroup({ + title: new FormControl(''), + }); + + constructor(private store: Store, private persistentStore: PersistentStore) { + this.instanceName = persistentStore.value.currentInstance.instanceName; + this.lists$ = this.store.pipe(select(selectLists)); + } + + listNameChanged(id: string, event: Event) { + const target = event.target as HTMLInputElement; + const newTitle = target.value; + this.store.dispatch(ListActions.updateList({listId: id, newTitle})); + } + + createList() { + let title = this.newListForm.value.title ?? ''; + if (title.length > 0) { + this.store.dispatch(ListActions.createList({title})); + this.newListForm.reset(); + } + } +} diff --git a/projects/mastolists/src/app/shared/services/list.service.ts b/projects/mastolists/src/app/shared/services/list.service.ts index 34c1fab..c8c5073 100644 --- a/projects/mastolists/src/app/shared/services/list.service.ts +++ b/projects/mastolists/src/app/shared/services/list.service.ts @@ -23,6 +23,30 @@ export class ListService { .pipe(map(result => result.sort((a, b) => a.title.localeCompare(b.title)))); } + + createList(title: string) { + const applicationState = this.store.value; + const instanceName = applicationState.currentInstance?.instanceName; + const accessToken = applicationState.currentInstance?.accessToken; + return this.mastodonApiListsService + .createList(instanceName, accessToken!, title) + .pipe( + take(1), + ); + } + + updateList(listId: string, newName: string) { + const applicationState = this.store.value; + const instanceName = applicationState.currentInstance?.instanceName; + const accessToken = applicationState.currentInstance?.accessToken; + return this.mastodonApiListsService + .updateList(instanceName, accessToken!, listId, newName) + .pipe( + take(1), + ); + } + + loadAccountsIdsForList(listId: string): Observable<{ [id: string]: string[] }> { const applicationState = this.store.value; const instanceName = applicationState.currentInstance?.instanceName; @@ -74,6 +98,5 @@ export class ListService { take(1), ) } - } diff --git a/projects/mastolists/src/app/shared/state/store/actions.ts b/projects/mastolists/src/app/shared/state/store/actions.ts index d9117b0..ee262a8 100644 --- a/projects/mastolists/src/app/shared/state/store/actions.ts +++ b/projects/mastolists/src/app/shared/state/store/actions.ts @@ -25,6 +25,12 @@ export const ListActions = createActionGroup({ 'Remove Account from List': props<{ accountId: string, listId: string }>(), 'Remove Account from List Success': emptyProps(), 'Remove Account from List Error': emptyProps(), + 'Create List': props<{ title: string }>(), + 'Create List Success': props<{ listId: string, title: string }>(), + 'Create List Error': emptyProps(), + 'Update List': props<{ listId: string, newTitle: string }>(), + 'Update List Success': props<{ listId: string, newTitle: string }>(), + 'Update List Error': emptyProps(), }, }); diff --git a/projects/mastolists/src/app/shared/state/store/effects.ts b/projects/mastolists/src/app/shared/state/store/effects.ts index f148447..6ccadce 100644 --- a/projects/mastolists/src/app/shared/state/store/effects.ts +++ b/projects/mastolists/src/app/shared/state/store/effects.ts @@ -1,10 +1,11 @@ import {Injectable} from "@angular/core"; import {act, Actions, createEffect, ofType} from "@ngrx/effects"; import {ListActions, MastodonApiActions} from "./actions"; -import {catchError, concat, exhaustMap, forkJoin, map, merge, of, switchMap} from "rxjs"; +import {catchError, concat, exhaustMap, forkJoin, map, merge, of, switchMap, tap} from "rxjs"; import {ListService} from "../../services/list.service"; import {AccountService} from "../../services/account.service"; import {NbToastrService} from "@nebular/theme"; +import {List} from "projects/mastodon-api/src/public-api"; @Injectable() export class ApplicationEffects { @@ -19,6 +20,59 @@ export class ApplicationEffects { ) ); + updateList$ = createEffect(() => + this.actions$.pipe( + ofType(ListActions.updateList), + exhaustMap((action) => this.listService.updateList(action.listId, action.newTitle).pipe( + map((resp) => { + const list = resp as List; + return ListActions.updateListSuccess({listId: list.id, newTitle: list.title}); + }), + catchError(() => of(ListActions.updateListError)), + ) + ) + ) + ); + updateListSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(ListActions.updateListSuccess), + map(() => this.toastService.success('List updated successfully', 'Success')), + ), {dispatch: false} + ); + updateListError$ = createEffect(() => + this.actions$.pipe( + ofType(ListActions.updateListError), + map(() => this.toastService.success('Could not update the list', 'Error')), + ), {dispatch: false} + ); + + createList$ = createEffect(() => + this.actions$.pipe( + ofType(ListActions.createList), + exhaustMap((action) => this.listService.createList(action.title).pipe( + map((resp) => { + const list = resp as List; + return ListActions.createListSuccess({listId: list.id, title: list.title}); + }), + catchError(() => of(ListActions.createListError())), + ) + ) + ) + ); + createListSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(ListActions.createListSuccess), + map(() => this.toastService.success('List created successfully', 'Success')), + ), {dispatch: false} + ); + createListError$ = createEffect(() => + this.actions$.pipe( + ofType(ListActions.createListError), + map(() => this.toastService.success('Could not create the list', 'Error')), + ), {dispatch: false} + ); + + listsLoaded$ = createEffect(() => this.actions$.pipe( ofType(MastodonApiActions.listsLoaded), diff --git a/projects/mastolists/src/app/shared/state/store/reducers.ts b/projects/mastolists/src/app/shared/state/store/reducers.ts index 9386882..948c473 100644 --- a/projects/mastolists/src/app/shared/state/store/reducers.ts +++ b/projects/mastolists/src/app/shared/state/store/reducers.ts @@ -35,59 +35,74 @@ export const initialState: ApplicationState = { } export const applicationStateReducers = createReducer( - initialState, - on(MastodonApiActions.loadLists, _state => { - return {..._state, listsLoading: true}; - }), - on(MastodonApiActions.listsLoaded, (_state, {lists}) => { - return {..._state, listsLoading: false, listAccountsLoading: true, lists: lists,}; - }), - on(MastodonApiActions.accountIdsForListsLoaded, (_state, {mappings}) => { - return {..._state, listAccountsLoading: false, listsAccounts: mappings}; - }), - on(MastodonApiActions.followingsLoaded, (_state, {followings}) => { - return {..._state, followingsLoading: false, followings}; - }), - on(MastodonApiActions.followingsLoadedError, (_state, {error}) => { - console.error(error); - return {..._state}; - }), - on(ListActions.addAccountToList, (_state, {accountId, listId}) => { - const existingAccountIds = _state.listsAccounts[listId] || []; - const newAccountIds = [...existingAccountIds, accountId]; - const newMap: { [listId: string]: string[] } = {..._state.listsAccounts, [listId]: newAccountIds}; - return { - ..._state, - listsAccounts: newMap, - } - }), - on(ListActions.removeAccountFromList, (_state, {accountId, listId}) => { - const existingAccountIds = _state.listsAccounts[listId] || []; - const newAccountIds = existingAccountIds.filter(id => id !== accountId); - const newMap: { [listId: string]: string[] } = {..._state.listsAccounts, [listId]: newAccountIds}; - return { - ..._state, - listsAccounts: newMap, - } - }), - on(FiltersActions.setUsername, (_state, {username}) => { - return {..._state, filters: {..._state.filters, username}}; - }), - on(FiltersActions.setFreeText, (_state, {freeText}) => { - return {..._state, filters: {..._state.filters, freeText}}; - }), - on(FiltersActions.setLists, (_state, {lists}) => { - return {..._state, filters: {..._state.filters, lists}}; - }), - on(FiltersActions.setUnlisted, (_state, {unlisted}) => { - let newState = {..._state, filters: {..._state.filters, unlisted}}; - if (unlisted) { - newState = {...newState, filters: {...newState.filters, lists: []}}; - } - return newState; - }), - on(FiltersActions.clearFilters, _state => { - return {..._state, filters: initialState.filters}; - }), - ) -; + initialState, + on(MastodonApiActions.loadLists, _state => { + return {..._state, listsLoading: true}; + }), + on(MastodonApiActions.listsLoaded, (_state, {lists}) => { + return {..._state, listsLoading: false, listAccountsLoading: true, lists: lists,}; + }), + on(MastodonApiActions.accountIdsForListsLoaded, (_state, {mappings}) => { + return {..._state, listAccountsLoading: false, listsAccounts: mappings}; + }), + on(MastodonApiActions.followingsLoaded, (_state, {followings}) => { + return {..._state, followingsLoading: false, followings}; + }), + on(MastodonApiActions.followingsLoadedError, (_state, {error}) => { + console.error(error); + return {..._state}; + }), + on(ListActions.addAccountToList, (_state, {accountId, listId}) => { + const existingAccountIds = _state.listsAccounts[listId] || []; + const newAccountIds = [...existingAccountIds, accountId]; + const newMap: { [listId: string]: string[] } = {..._state.listsAccounts, [listId]: newAccountIds}; + return { + ..._state, + listsAccounts: newMap, + } + }), + on(ListActions.removeAccountFromList, (_state, {accountId, listId}) => { + const existingAccountIds = _state.listsAccounts[listId] || []; + const newAccountIds = existingAccountIds.filter(id => id !== accountId); + const newMap: { [listId: string]: string[] } = {..._state.listsAccounts, [listId]: newAccountIds}; + return { + ..._state, + listsAccounts: newMap, + } + }), + on(FiltersActions.setUsername, (_state, {username}) => { + return {..._state, filters: {..._state.filters, username}}; + }), + on(FiltersActions.setFreeText, (_state, {freeText}) => { + return {..._state, filters: {..._state.filters, freeText}}; + }), + on(FiltersActions.setLists, (_state, {lists}) => { + return {..._state, filters: {..._state.filters, lists}}; + }), + on(FiltersActions.setUnlisted, (_state, {unlisted}) => { + let newState = {..._state, filters: {..._state.filters, unlisted}}; + if (unlisted) { + newState = {...newState, filters: {...newState.filters, lists: []}}; + } + return newState; + }), + on(FiltersActions.clearFilters, _state => { + return {..._state, filters: initialState.filters}; + }), + on(ListActions.createListSuccess, (_state, {listId, title}) => { + const newListsArray = [..._state.lists, {id: listId, title: title, repliesPolicy: 'list', accounts: []}].sort((a, b) => a.title.localeCompare(b.title)); + return { + ..._state, + listsAccounts: {..._state.listsAccounts, [listId]: []}, + lists: newListsArray, + }; + }), + on(ListActions.updateListSuccess, (_state, {listId, newTitle}) => { + const listToUpdate = _state.lists.find(list => list.id === listId) as List; + const newListsArray = [..._state.lists.filter(list => list.id !== listId), {...listToUpdate, title: newTitle}].sort((a, b) => a.title.localeCompare(b.title)); + return { + ..._state, + lists: newListsArray, + }; + }), +); diff --git a/projects/mastolists/src/app/sync/sync/sync.component.html b/projects/mastolists/src/app/sync/sync/sync.component.html index 5ba9103..faf9042 100644 --- a/projects/mastolists/src/app/sync/sync/sync.component.html +++ b/projects/mastolists/src/app/sync/sync/sync.component.html @@ -40,8 +40,9 @@

Now you can: