feat: adds table view
feat: adds table view
This commit is contained in:
@@ -22,8 +22,8 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'followings',
|
path: 'list',
|
||||||
loadChildren: () => import('./followings/followings.module').then(m => m.FollowingsModule),
|
loadChildren: () => import('./followings-list/followings-list.module').then(m => m.FollowingsListModule),
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -31,6 +31,11 @@ const routes: Routes = [
|
|||||||
loadChildren: () => import('./followings-matrix/followings-matrix.module').then(m => m.FollowingsMatrixModule),
|
loadChildren: () => import('./followings-matrix/followings-matrix.module').then(m => m.FollowingsMatrixModule),
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'table',
|
||||||
|
loadChildren: () => import('./followings-table/followings-table.module').then(m => m.FollowingsTableModule),
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import {Component, isDevMode} from '@angular/core';
|
import {Component, isDevMode} from '@angular/core';
|
||||||
import {select, Store} from "@ngrx/store";
|
import {Store} from "@ngrx/store";
|
||||||
import {MastodonApiActions} from "./shared/state/store/actions";
|
import {MastodonApiActions} from "./shared/state/store/actions";
|
||||||
import {Observable, tap} from "rxjs";
|
|
||||||
import {selectLoadingPercentage} from "./shared/state/store/selectors";
|
|
||||||
import {PersistentStore} from "./shared/state/persistent/persistent-store.service";
|
import {PersistentStore} from "./shared/state/persistent/persistent-store.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -16,8 +14,9 @@ export class AppComponent {
|
|||||||
navigationItems = [
|
navigationItems = [
|
||||||
{title: 'Authorize', link: '/auth'},
|
{title: 'Authorize', link: '/auth'},
|
||||||
{title: 'Stats', link: '/sync'},
|
{title: 'Stats', link: '/sync'},
|
||||||
{title: 'List view', link: '/followings'},
|
{title: 'List view', link: '/list'},
|
||||||
{title: 'Matrix View', link: '/matrix'},
|
{title: 'Matrix View', link: '/matrix'},
|
||||||
|
{title: 'Table View', link: '/table'},
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor(private store: Store, private persistentStore: PersistentStore) {
|
constructor(private store: Store, private persistentStore: PersistentStore) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {RouterModule, Routes} from "@angular/router";
|
import {RouterModule, Routes} from "@angular/router";
|
||||||
import {NgModule} from "@angular/core";
|
import {NgModule} from "@angular/core";
|
||||||
import {FollowingsComponent} from "./followings/followings.component";
|
import {ListComponent} from "./list/list.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: FollowingsComponent
|
component: ListComponent
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -13,5 +13,5 @@ const routes: Routes = [
|
|||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
export class FollowingsRoutingModule {
|
export class FollowingsListRoutingModule {
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {FollowingsComponent} from './followings/followings.component';
|
import {ListComponent} from './list/list.component';
|
||||||
import {SharedModule} from '../shared/shared.module';
|
import {SharedModule} from '../shared/shared.module';
|
||||||
import {FollowingsRoutingModule} from './followings-routing.module';
|
import {FollowingsListRoutingModule} from './followings-list-routing.module';
|
||||||
import {NbListModule, NbToggleModule} from "@nebular/theme";
|
import {NbListModule, NbToggleModule} from "@nebular/theme";
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
FollowingsComponent
|
ListComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
FollowingsRoutingModule,
|
FollowingsListRoutingModule,
|
||||||
// Nebula
|
// Nebula
|
||||||
NbListModule,
|
NbListModule,
|
||||||
NbToggleModule,
|
NbToggleModule,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class FollowingsModule {
|
export class FollowingsListModule {
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { FollowingsComponent } from './followings.component';
|
import { ListComponent } from './list.component';
|
||||||
|
|
||||||
describe('FollowingsComponent', () => {
|
describe('FollowingsComponent', () => {
|
||||||
let component: FollowingsComponent;
|
let component: ListComponent;
|
||||||
let fixture: ComponentFixture<FollowingsComponent>;
|
let fixture: ComponentFixture<ListComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ FollowingsComponent ]
|
declarations: [ ListComponent ]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(FollowingsComponent);
|
fixture = TestBed.createComponent(ListComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -9,11 +9,11 @@ import {selectFilteredFollowingsWithLists, selectFollowings, selectFollowingsWit
|
|||||||
import {FiltersActions, ListActions, MastodonApiActions} from "../../shared/state/store/actions";
|
import {FiltersActions, ListActions, MastodonApiActions} from "../../shared/state/store/actions";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-followings',
|
selector: 'app-list',
|
||||||
templateUrl: './followings.component.html',
|
templateUrl: './list.component.html',
|
||||||
styleUrls: ['./followings.component.scss']
|
styleUrls: ['./list.component.scss']
|
||||||
})
|
})
|
||||||
export class FollowingsComponent {
|
export class ListComponent {
|
||||||
followings$: Observable<ReadonlyArray<Account>>;
|
followings$: Observable<ReadonlyArray<Account>>;
|
||||||
lists$: Observable<ReadonlyArray<List>>;
|
lists$: Observable<ReadonlyArray<List>>;
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
table {
|
table {
|
||||||
display: block;
|
display: block;
|
||||||
height: max(600px, calc(100vh - 300px));
|
height: max(600px, calc(100vh - 300px));
|
||||||
width: 1080px;
|
width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import {RouterModule, Routes} from "@angular/router";
|
||||||
|
import {NgModule} from "@angular/core";
|
||||||
|
import {TableComponent} from "./table/table.component";
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: TableComponent
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class FollowingsTableRoutingModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<nb-card>
|
||||||
|
<nb-card-header style="position: relative;">
|
||||||
|
<h1>Table View for Followings</h1>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<app-filters></app-filters>
|
||||||
|
</nb-card-header>
|
||||||
|
<nb-card-body>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>Notes</th>
|
||||||
|
<th>Fields</th>
|
||||||
|
<th>Lists</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let row of rows$ |async">
|
||||||
|
<td>
|
||||||
|
<a [href]="row.url">
|
||||||
|
<nb-user
|
||||||
|
size="medium"
|
||||||
|
shape="semi-round"
|
||||||
|
[name]="row.displayName"
|
||||||
|
[title]="row.username"
|
||||||
|
[picture]="row.avatar"
|
||||||
|
>
|
||||||
|
</nb-user>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td><span [innerHTML]="row.note"></span></td>
|
||||||
|
<td><span [innerHTML]="row.fields"></span></td>
|
||||||
|
<td>
|
||||||
|
<div *ngFor="let list of row.lists">
|
||||||
|
{{list.title}}
|
||||||
|
<nb-icon icon="person-remove-outline" (click)="removeAccountFromList(row.id, list.id)">
|
||||||
|
Remove Account from list
|
||||||
|
</nb-icon>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="actions">
|
||||||
|
<select class="list-select" #listSelect>
|
||||||
|
<option *ngFor="let list of lists$ | async" [value]="list.id">{{list.title}}</option>
|
||||||
|
</select>
|
||||||
|
<nb-icon icon="person-add-outline" (click)="addAccountToSelectedList(row.id, listSelect.value)">
|
||||||
|
Add Account to list
|
||||||
|
</nb-icon>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</nb-card-body>
|
||||||
|
</nb-card>
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
table {
|
||||||
|
display: block;
|
||||||
|
height: max(600px, calc(100vh - 300px));
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
th {
|
||||||
|
border: 1pt solid #101426;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border: 1pt solid #101426;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
select {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TableComponent } from './table.component';
|
||||||
|
|
||||||
|
describe('TableComponent', () => {
|
||||||
|
let component: TableComponent;
|
||||||
|
let fixture: ComponentFixture<TableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ TableComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(TableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import {Component} from '@angular/core';
|
||||||
|
import {map, Observable} from "rxjs";
|
||||||
|
import {Account} from "../../../../../mastodon-api/src/lib/interfaces/public/account";
|
||||||
|
import {List} from "../../../../../mastodon-api/src/lib/interfaces/public/list";
|
||||||
|
import {select, Store} from "@ngrx/store";
|
||||||
|
import {selectFilteredFollowingsWithLists, selectLists} from "../../shared/state/store/selectors";
|
||||||
|
import {ListActions} from "../../shared/state/store/actions";
|
||||||
|
|
||||||
|
interface DataGridRow {
|
||||||
|
id: string;
|
||||||
|
url: string;
|
||||||
|
displayName: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string;
|
||||||
|
note: string;
|
||||||
|
fields: string;
|
||||||
|
lists: List[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-table',
|
||||||
|
templateUrl: './table.component.html',
|
||||||
|
styleUrls: ['./table.component.scss']
|
||||||
|
})
|
||||||
|
export class TableComponent {
|
||||||
|
rows$: Observable<DataGridRow[]>;
|
||||||
|
lists$: Observable<ReadonlyArray<List>>;
|
||||||
|
|
||||||
|
constructor(private store: Store) {
|
||||||
|
this.rows$ = this.store
|
||||||
|
.pipe(
|
||||||
|
select(selectFilteredFollowingsWithLists),
|
||||||
|
map((accounts: ReadonlyArray<Account>) => {
|
||||||
|
return accounts.map((account) => {
|
||||||
|
return {
|
||||||
|
id: account.id,
|
||||||
|
username: `@${account.username}`,
|
||||||
|
displayName: account.displayName,
|
||||||
|
url: account.url,
|
||||||
|
avatar: account.avatar,
|
||||||
|
note: account.note,
|
||||||
|
fields: account.fields.map(field => `${field.name}: ${field.value}`).join('<br>'),
|
||||||
|
lists: account.lists,
|
||||||
|
} as DataGridRow;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.lists$ = this.store.pipe(select(selectLists));
|
||||||
|
}
|
||||||
|
|
||||||
|
addAccountToSelectedList(accountId: string, listId: string) {
|
||||||
|
this.store.dispatch(ListActions.addAccountToList({accountId, listId}));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAccountFromList(accountId: string, listId: string) {
|
||||||
|
this.store.dispatch(ListActions.removeAccountFromList({accountId, listId}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a [routerLink]="['/auth']">Authorize</a> with your instance</li>
|
<li><a [routerLink]="['/auth']">Authorize</a> with your instance</li>
|
||||||
<li><a [routerLink]="['/sync']">Sync</a> your lists and followings to local storage</li>
|
<li><a [routerLink]="['/sync']">Sync</a> your lists and followings to local storage</li>
|
||||||
<li>Select the <a [routerLink]="['/followings']">List view</a> to add and remove users from lists</li>
|
<li>Select the <a [routerLink]="['/list']">List view</a> to add and remove users from lists</li>
|
||||||
<li>... or use the experimental <a [routerLink]="['/matrix']">Matrix view</a></li>
|
<li>... or use the experimental <a [routerLink]="['/matrix']">Matrix view</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nb-card-body>
|
</nb-card-body>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
<div *ngIf="(lists$ | async) && (followings$ | async)">
|
<div *ngIf="(lists$ | async) && (followings$ | async)">
|
||||||
<p>Now you can:</p>
|
<p>Now you can:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Select the <a [routerLink]="['/followings']">List view</a> to add and remove users from lists</li>
|
<li>Select the <a [routerLink]="['/list']">List view</a> to add and remove users from lists</li>
|
||||||
<li>... or use the experimental <a [routerLink]="['/matrix']">Matrix view</a></li>
|
<li>... or use the experimental <a [routerLink]="['/matrix']">Matrix view</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ $nb-themes: nb-register-theme(
|
|||||||
(
|
(
|
||||||
font-family-primary: 'Inter',
|
font-family-primary: 'Inter',
|
||||||
font-family-secondary: 'Inter',
|
font-family-secondary: 'Inter',
|
||||||
layout-content-width: 1200px,
|
layout-content-width: 100vw,
|
||||||
),
|
),
|
||||||
dark,
|
dark,
|
||||||
dark
|
dark
|
||||||
|
|||||||
Reference in New Issue
Block a user