Merge pull request #29 from NicolasConstant/feature_column-handling

Feature column handling
This commit is contained in:
Nicolas Constant 2019-02-10 18:22:32 -05:00 committed by GitHub
commit 01619fd5a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 439 additions and 76 deletions

45
package-lock.json generated
View File

@ -647,6 +647,51 @@
"tslib": "^1.9.0"
}
},
"@fortawesome/angular-fontawesome": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.3.0.tgz",
"integrity": "sha512-wXvyPI7GidoNiqeMz2re9iemUMFH4zBmuv94CfXlaanQ8+kMP/fYs/k69PLVN1KsebQY4kRA9GHmc1U1ndBkJg==",
"requires": {
"tslib": "^1.9.0"
}
},
"@fortawesome/fontawesome-common-types": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.13.tgz",
"integrity": "sha512-Qy1eMKylnbsCRXod8C4CfwqlNtLU7OkJiLsFJZlHY4rY9ky2VYUKNnRj0b+3lnGwA1dWNY+pI0XWbP/mrLCohw=="
},
"@fortawesome/fontawesome-svg-core": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.13.tgz",
"integrity": "sha512-1Q0grmELVTBDbk/Jo4dRCptkYMQjikPEcoPo+eZS+oLVowv1afP4177m4gvqMwdk3l+9KYoiMBJzfY2qDbs+Dg==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.13"
}
},
"@fortawesome/free-brands-svg-icons": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.7.0.tgz",
"integrity": "sha512-MA4FLVKfuamBlfkRznoAOHyCywGj0MToDyomvtpuJeB9BqNBPDQ/TpGTyRZ8TEyHqo8ejjjACXYjr2fIEB7FZA==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.13"
}
},
"@fortawesome/free-regular-svg-icons": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.7.0.tgz",
"integrity": "sha512-q6u0JTCr8T8RDRYytVcPg/RvPXv67fVcxSa/pdF59OAF5scM03QakX+W+x0vuaWvrpUwU0VK2AXJzSYnBUNpJA==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.13"
}
},
"@fortawesome/free-solid-svg-icons": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.7.0.tgz",
"integrity": "sha512-bWHNC9Mho8gJ/MFmnc+u6af0jXtdgNWdL6mxHE2lrPsSUGTBCr7AcYeTf+f0Q7JZDjIy/kW8FKpHcfuesScK5A==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.13"
}
},
"@ngtools/webpack": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-6.1.1.tgz",

View File

@ -5,15 +5,14 @@
"main": "main.js",
"scripts": {
"ng": "ng",
"start": "ng build && electron .",
"serve": "ng serve",
"start": "ng serve",
"build": "ng build --prod",
"test": "ng test",
"test-nowatch": "ng test --watch=false",
"lint": "ng lint",
"e2e": "ng e2e",
"electron": "ng build && electron .",
"electron-prod": "ng build --prod && electron ."
"electron": "ng build --prod && electron .",
"electron-debug": "ng build && electron ."
},
"private": true,
"dependencies": {
@ -26,6 +25,11 @@
"@angular/platform-browser": "^6.1.0",
"@angular/platform-browser-dynamic": "^6.1.0",
"@angular/router": "^6.1.0",
"@fortawesome/angular-fontawesome": "^0.3.0",
"@fortawesome/fontawesome-svg-core": "^1.2.13",
"@fortawesome/free-brands-svg-icons": "^5.7.0",
"@fortawesome/free-regular-svg-icons": "^5.7.0",
"@fortawesome/free-solid-svg-icons": "^5.7.0",
"@ngxs/storage-plugin": "^3.2.0",
"@ngxs/store": "^3.2.0",
"bootstrap": "^4.1.3",

View File

@ -10,6 +10,8 @@ import { NgxElectronModule } from "ngx-electron";
import { NgxsModule } from '@ngxs/store';
import { NgxsStoragePluginModule } from '@ngxs/storage-plugin';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { AppComponent } from "./app.component";
import { LeftSideBarComponent } from "./components/left-side-bar/left-side-bar.component";
import { StreamsMainDisplayComponent } from "./pages/streams-main-display/streams-main-display.component";
@ -42,6 +44,7 @@ import { StreamOverlayComponent } from './components/stream/stream-overlay/strea
import { DatabindedTextComponent } from './components/stream/status/databinded-text/databinded-text.component';
import { TimeAgoPipe } from './pipes/time-ago.pipe';
import { StreamStatusesComponent } from './components/stream/stream-statuses/stream-statuses.component';
import { StreamEditionComponent } from './components/stream/stream-edition/stream-edition.component';
const routes: Routes = [
{ path: "", redirectTo: "home", pathMatch: "full" },
@ -76,9 +79,11 @@ const routes: Routes = [
StreamOverlayComponent,
DatabindedTextComponent,
TimeAgoPipe,
StreamStatusesComponent
StreamStatusesComponent,
StreamEditionComponent
],
imports: [
FontAwesomeModule,
BrowserModule,
HttpModule,
HttpClientModule,

View File

@ -1,16 +1,16 @@
<div class="panel">
<h3 class="panel__title">Manage Account</h3>
<h3 class="panel__title">Manage Account</h3>
<div class="account-editor__display-avatar">
<img class="account-editor__avatar" src="{{account.avatar}}" title="{{ account.info.id }} " />
</div>
<div class="account-editor__display-avatar">
<img class="account-editor__avatar" src="{{account.avatar}}" title="{{ account.info.id }} " />
</div>
<h4 class="add-column__label">add column:</h4>
<h4 class="account__label">add column:</h4>
<a class="add-column__link" href *ngFor="let stream of availableStreams" (click)="addStream(stream)">
{{ stream.name }}
</a>
<!-- <a class="add-column__link" href>
<a class="account__link account__blue" href *ngFor="let stream of availableStreams" (click)="addStream(stream)">
{{ stream.name }}
</a>
<!-- <a class="add-column__link" href>
Global Timeline
</a>
<a class="add-column__link" href>
@ -20,4 +20,9 @@
Lists, Favs, Activitires, etc
</a> -->
<h4 class="account__label account__margin-top">remove account from sengi:</h4>
<a class="account__link account__red" href (click)="removeAccount()">
Delete
</a>
</div>

View File

@ -20,25 +20,48 @@
}
}
.add-column {
.account {
&__label {
// text-decoration: underline;
font-size: $small-font-size;
margin-left: 5px;
color: $font-color-secondary;
}
&__margin-top {
margin-top: 25px;
}
&__link {
text-decoration: none;
display: block; // width: calc(100% - 20px);
width: 100%; // height: 30px;
padding: 5px 10px; // border: solid 1px black;
background-color: $color-primary;
color: #fff;
&:not(:last-child) {
margin-bottom: 5px;
}
}
&__mid-link {
text-decoration: none;
display: block; // width: calc(100% - 20px);
width: 45%; // height: 30px;
padding: 5px 10px; // border: solid 1px black;
&:not(:last-child) {
margin-bottom: 5px;
}
}
&__blue {
background-color: $color-primary;
color: #fff;
&:hover {
background-color: lighten($color-primary, 15);
}
}
&__red {
$red-button-color: rgb(65, 3, 3);
background-color: $red-button-color;
color: #fff;
&:hover {
background-color: lighten($red-button-color, 15);
}
}
}

View File

@ -1,32 +1,43 @@
import { Component, OnInit, Input } from '@angular/core';
import { StreamElement, StreamTypeEnum, AddStream } from '../../../states/streams.state';
import { StreamElement, StreamTypeEnum, AddStream, RemoveAllStreams } from '../../../states/streams.state';
import { Store } from '@ngxs/store';
import { AccountsStateModel, AccountInfo } from '../../../states/accounts.state';
import { AccountsStateModel, AccountInfo, RemoveAccount } from '../../../states/accounts.state';
import { AccountWrapper } from '../../../models/account.models';
import { NavigationService } from '../../../services/navigation.service';
@Component({
selector: 'app-manage-account',
templateUrl: './manage-account.component.html',
styleUrls: ['./manage-account.component.scss']
selector: 'app-manage-account',
templateUrl: './manage-account.component.html',
styleUrls: ['./manage-account.component.scss']
})
export class ManageAccountComponent implements OnInit {
@Input() account: AccountWrapper;
@Input() account: AccountWrapper;
availableStreams: StreamElement[] = [];
availableStreams: StreamElement[] = [];
constructor(private readonly store: Store) { }
constructor(
private readonly store: Store,
private readonly navigationService: NavigationService) { }
ngOnInit() {
this.availableStreams.length = 0;
this.availableStreams.push(new StreamElement(StreamTypeEnum.global, 'Global Timeline', this.account.info.id, null, null));
this.availableStreams.push(new StreamElement(StreamTypeEnum.local, 'Local Timeline', this.account.info.id, null, null));
this.availableStreams.push(new StreamElement(StreamTypeEnum.personnal, 'Personnal Timeline', this.account.info.id, null, null));
}
addStream(stream: StreamElement): boolean {
if (stream) {
this.store.dispatch([new AddStream(stream)]);
ngOnInit() {
const instance = this.account.info.instance;
this.availableStreams.length = 0;
this.availableStreams.push(new StreamElement(StreamTypeEnum.global, 'Federated Timeline', this.account.info.id, null, null, `federate@${instance}`));
this.availableStreams.push(new StreamElement(StreamTypeEnum.local, 'Local Timeline', this.account.info.id, null, null, `local@${instance}`));
this.availableStreams.push(new StreamElement(StreamTypeEnum.personnal, 'Home', this.account.info.id, null, null, `home@${instance}`));
}
addStream(stream: StreamElement): boolean {
if (stream) {
this.store.dispatch([new AddStream(stream)]);
}
return false;
}
removeAccount(): boolean {
const accountId = this.account.info.id;
this.store.dispatch([new RemoveAllStreams(accountId), new RemoveAccount(accountId)]);
this.navigationService.closePanel();
return false;
}
return false;
}
}

View File

@ -33,7 +33,7 @@ export class LeftSideBarComponent implements OnInit, OnDestroy {
ngOnInit() {
this.accounts$.subscribe((accounts: AccountInfo[]) => {
if (accounts) {
//Update and Add
for (let acc of accounts) {
const previousAcc = this.accounts.find(x => x.info.id === acc.id)
if (previousAcc) {
@ -41,7 +41,6 @@ export class LeftSideBarComponent implements OnInit, OnDestroy {
} else {
const accWrapper = new AccountWrapper();
accWrapper.info = acc;
this.accounts.push(accWrapper);
this.mastodonService.retrieveAccountDetails(acc)
@ -49,10 +48,12 @@ export class LeftSideBarComponent implements OnInit, OnDestroy {
accWrapper.avatar = result.avatar;
});
}
}
//TODO: see when deleted
//Delete
const deletedAccounts = this.accounts.filter(x => accounts.findIndex(y => y.id === x.info.id) === -1);
for(let delAcc of deletedAccounts){
this.accounts = this.accounts.filter(x => x.info.id !== delAcc.info.id);
}
}
});

View File

@ -33,7 +33,7 @@ export class HashtagComponent implements OnInit {
event.stopPropagation();
const hashtag = this.hashtagElement.tag;
const newStream = new StreamElement(StreamTypeEnum.tag, `#${hashtag}`, this.hashtagElement.accountId, hashtag, null);
const newStream = new StreamElement(StreamTypeEnum.tag, `${hashtag}`, this.hashtagElement.accountId, hashtag, null, this.hashtagElement.displayableFullName);
this.store.dispatch([new AddStream(newStream)]);
return false;

View File

@ -1,6 +1,6 @@
<div class="reblog" *ngIf="reblog">
<a class="reblog__profile-link" href (click)="openAccount(status.account)">{{ status.account.display_name }} <img *ngIf="reblog" class="reblog__avatar"
src="{{ status.account.avatar }}" /></a> boosted
<a class="reblog__profile-link" href (click)="openAccount(status.account)">{{ status.account.display_name }} <img
*ngIf="reblog" class="reblog__avatar" src="{{ status.account.avatar }}" /></a> boosted
</div>
<div class="status">
@ -12,11 +12,15 @@
class="status__name--username">{{displayedStatus.account.acct}}</span>
</span>
</a>
<div class="status__created-at" title="{{ displayedStatus.created_at | date: 'full' }}">{{
status.created_at | timeAgo | async }}</div>
<div class="status__created-at" title="{{ displayedStatus.created_at | date: 'full' }}">
<a class="status__created-at--link" href="{{ displayedStatus.url }}" target="_blank">
{{ status.created_at | timeAgo | async }}
</a>
</div>
<!-- <div #content class="status__content" innerHTML="{{displayedStatus.content}}"></div> -->
<app-databinded-text class="status__content" [text]="displayedStatus.content" (accountSelected)="accountSelected($event)" (hashtagSelected)="hashtagSelected($event)" (textSelected)="textSelected()"></app-databinded-text>
<app-databinded-text class="status__content" [text]="displayedStatus.content" (accountSelected)="accountSelected($event)"
(hashtagSelected)="hashtagSelected($event)" (textSelected)="textSelected()"></app-databinded-text>
<app-attachements *ngIf="hasAttachments" class="attachments" [attachments]="displayedStatus.media_attachments"></app-attachements>

View File

@ -83,11 +83,19 @@
// margin: 0 !important;
// font-size: 0.85em;
// }
&__created-at {
color: $status-secondary-color;
&__created-at {
position: absolute;
top: 7px;
right: 5px;
&--link {
color: $status-secondary-color;
text-decoration: none;
&:hover {
color: lighten($status-secondary-color, 20);
}
}
}
}

View File

@ -0,0 +1,5 @@
<div class="stream-edition">
<button (click)="moveLeft()" class="stream-edition__button" title="move left"><fa-icon [icon]="faChevronLeft"></fa-icon></button>
<button (click)="moveRight()" class="stream-edition__button" title="move right"><fa-icon [icon]="faChevronRight"></fa-icon></button>
<button (click)="delete()" class="stream-edition__button stream-edition__button--delete" title="remove column"><fa-icon [icon]="faTimes"></fa-icon></button>
</div>

View File

@ -0,0 +1,21 @@
@import "variables";
@import "mixins";
.stream-edition {
width: 100%;
// min-height: 50px;
background-color: #222736;
&__button {
@include clearButton;
padding: 5px 10px 5px 10px;
margin: 3px 0;
&--delete{
float: right;
margin-right: 5px;
}
&:hover {
color: darken($font-color-primary, 20);
}
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { StreamEditionComponent } from './stream-edition.component';
xdescribe('StreamEditionComponent', () => {
let component: StreamEditionComponent;
let fixture: ComponentFixture<StreamEditionComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ StreamEditionComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StreamEditionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,38 @@
import { Component, OnInit, Input } from '@angular/core';
import { Store } from '@ngxs/store';
import { faChevronLeft, faChevronRight, faTimes } from "@fortawesome/free-solid-svg-icons";
import { StreamElement, RemoveStream, MoveStreamUp, MoveStreamDown } from '../../../states/streams.state';
@Component({
selector: 'app-stream-edition',
templateUrl: './stream-edition.component.html',
styleUrls: ['./stream-edition.component.scss']
})
export class StreamEditionComponent implements OnInit {
faChevronLeft = faChevronLeft;
faChevronRight = faChevronRight;
faTimes = faTimes;
@Input() streamElement: StreamElement;
constructor(private readonly store: Store) { }
ngOnInit() {
}
moveLeft(): boolean {
this.store.dispatch([new MoveStreamUp(this.streamElement.id)]);
return false;
}
moveRight(): boolean {
this.store.dispatch([new MoveStreamDown(this.streamElement.id)]);
return false;
}
delete(): boolean {
this.store.dispatch([new RemoveStream(this.streamElement.id)]);
return false;
}
}

View File

@ -116,7 +116,7 @@ export class StreamOverlayComponent implements OnInit {
}
const selectedAccount = this.toolsService.getSelectedAccounts()[0];
const hashTagElement = new StreamElement(StreamTypeEnum.tag, hashtag, selectedAccount.id, hashtag, null);
const hashTagElement = new StreamElement(StreamTypeEnum.tag, hashtag, selectedAccount.id, hashtag, null, `#${hashtag}@${selectedAccount.instance}`);
const newElement = new OverlayBrowsing(hashTagElement, null, null);
this.loadElement(newElement);
this.canGoForward = false;

View File

@ -1,17 +1,23 @@
<div class="stream-column">
<app-stream-overlay class="stream-overlay" *ngIf="overlayActive"
(closeOverlay)="closeOverlay()"
[browseAccountData]="overlayAccountToBrowse"
[browseHashtagData]="overlayHashtagToBrowse"
[browseThreadData]="overlayThreadToBrowse"></app-stream-overlay>
<app-stream-overlay class="stream-overlay" *ngIf="overlayActive" (closeOverlay)="closeOverlay()"
[browseAccountData]="overlayAccountToBrowse" [browseHashtagData]="overlayHashtagToBrowse" [browseThreadData]="overlayThreadToBrowse"></app-stream-overlay>
<div class="stream-column__stream-header">
<a href title="return to top" (click)="goToTop()">
<h1>{{ streamElement.name.toUpperCase() }}</h1>
<a class="stream-column__open-menu" href title="edit column" (click)="openEditionMenu()">
<fa-icon class="stream-column__open-menu--icon" [icon]="menuFaIcon"></fa-icon>
</a>
<a class="stream-column__stream-selector" href title="return to top" (click)="goToTop()">
<fa-icon class="stream-column__stream-selector--icon" [icon]="columnFaIcon"></fa-icon>
<h1 class="stream-column__stream-selector--title">{{ streamElement.name.toUpperCase() }}</h1>
</a>
</div>
<app-stream-statuses class="stream-statuses" [streamElement]="streamElement" [goToTop]="goToTopSubject.asObservable()" (browseAccountEvent)="browseAccount($event)" (browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-stream-statuses>
<app-stream-edition class="stream-edition" *ngIf="editionPanelIsOpen"
[streamElement]="streamElement"></app-stream-edition>
<app-stream-statuses class="stream-statuses" [streamElement]="streamElement" [goToTop]="goToTopSubject.asObservable()"
(browseAccountEvent)="browseAccount($event)" (browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-stream-statuses>
<!-- <div class="stream-toots flexcroll" #statusstream (scroll)="onScroll()">
<div class="stream-toots__status" *ngFor="let statusWrapper of statuses">
<app-status [statusWrapper]="statusWrapper" (browseAccount)="browseAccount($event)" (browseHashtag)="browseHashtag($event)"></app-status>

View File

@ -1,30 +1,63 @@
@import "variables";
@import "commons";
$stream-header-height: 40px;
.stream-edition {
width: $stream-column-width;
position: absolute;
z-index: 50;
}
.stream-column {
position: relative;
width: $stream-column-width;
height: calc(100%);
background-color: $column-color;
margin: 0 0 0 $stream-column-separator;
&__stream-header {
border-bottom: 1px solid #222736;
}
&__open-menu {
float: right;
display: block;
width: $stream-header-height - 10px;
height: $stream-header-height - 10px;
margin: 5px;
&:hover &--icon {
color: darken(whitesmoke, 30);
}
&--icon {
color: whitesmoke; // float: left;
position: relative;
top: 4px;
left: 8px;
}
}
&__stream-selector {
display: block;
width: calc(100%);
height: 30px;
height: $stream-header-height;
background-color: $column-header-background-color;
border-bottom: 1px solid black;
& h1 {
text-decoration: none; // &:hover {
// }
&--icon {
color: whitesmoke;
float: left;
position: relative;
left: 11px;
top: 9px;
}
&--title {
color: whitesmoke;
font-size: 0.8em;
font-weight: normal;
margin: 0;
padding: 8px 0 0 10px;
font-weight: normal; // margin: 0 0 0 25px;
padding: 14px 0 0 35px;
}
}
}
.stream-statuses {
display: block;
height: calc(100% - 30px);
height: calc(100% - #{$stream-header-height});
width: 320px;
}
@ -37,11 +70,9 @@
// border-width: 0 0 1px 0;
// }
// }
.stream-overlay {
position: absolute;
z-index: 50;
z-index: 100;
width: $stream-column-width;
height: calc(100%);
}

View File

@ -1,7 +1,8 @@
import { Component, OnInit, Input, ElementRef, ViewChild, HostListener } from "@angular/core";
import { Subject } from "rxjs";
import { faHome, faGlobe, faUser, faHashtag, faListUl, faBars, IconDefinition } from "@fortawesome/free-solid-svg-icons";
import { StreamElement } from "../../states/streams.state";
import { StreamElement, StreamTypeEnum } from "../../states/streams.state";
import { Status } from "../../services/models/mastodon.interfaces";
import { AccountInfo } from "../../states/accounts.state";
@ -11,6 +12,9 @@ import { AccountInfo } from "../../states/accounts.state";
styleUrls: ["./stream.component.scss"]
})
export class StreamComponent implements OnInit {
columnFaIcon: IconDefinition;
menuFaIcon = faBars;
overlayActive: boolean;
overlayAccountToBrowse: string;
overlayHashtagToBrowse: string;
@ -18,7 +22,34 @@ export class StreamComponent implements OnInit {
goToTopSubject: Subject<void> = new Subject<void>();
@Input() streamElement: StreamElement;
private _streamElement: StreamElement;
@Input('streamElement')
set streamElement(stream: StreamElement) {
switch (stream.type) {
case StreamTypeEnum.personnal:
this.columnFaIcon = faHome;
break;
case StreamTypeEnum.global:
this.columnFaIcon = faGlobe;
break;
case StreamTypeEnum.local:
this.columnFaIcon = faUser;
break;
case StreamTypeEnum.tag:
this.columnFaIcon = faHashtag;
break;
case StreamTypeEnum.list:
this.columnFaIcon = faListUl;
break;
}
this._streamElement = stream;
}
get streamElement(): StreamElement {
return this._streamElement;
}
constructor() { }
@ -57,6 +88,13 @@ export class StreamComponent implements OnInit {
this.overlayThreadToBrowse = null;
this.overlayActive = false;
}
editionPanelIsOpen: boolean;
openEditionMenu(): boolean {
console.log('opened menu');
this.editionPanelIsOpen = !this.editionPanelIsOpen;
return false;
}
}
export class StatusWrapper {

View File

@ -1,5 +1,5 @@
<div class="streams-selection-footer">
<a class="stream-selection" *ngFor="let str of streams; let i=index" href (click)="onColumnSelection(i)" >
<a class="stream-selection" *ngFor="let str of streams; let i=index" href (click)="onColumnSelection(i)" title="open {{str.displayableFullName}}">
<span class="stream-selection__column-reprensentation"></span>
</a>
</div>

View File

@ -33,7 +33,7 @@ export class StreamsMainDisplayComponent implements OnInit, OnDestroy {
private focusOnColumn(columnIndex: number): void {
if (columnIndex > -1) {
setTimeout(() => {
this.streamsElementRef.toArray()[columnIndex].nativeElement.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'start' });
this.streamsElementRef.toArray()[columnIndex].nativeElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
});
}
}

View File

@ -11,6 +11,11 @@ export class SelectAccount {
constructor(public account: AccountInfo, public multiselection: boolean = false) {}
}
export class RemoveAccount {
static readonly type = '[Accounts] Remove account';
constructor(public accountId: string) {}
}
export interface AccountsStateModel {
accounts: AccountInfo[];
}
@ -51,6 +56,15 @@ export class AccountsState {
accounts: copyAccounts
});
}
@Action(RemoveAccount)
RemoveAccount(ctx: StateContext<AccountsStateModel>, action: RemoveAccount){
const state = ctx.getState();
const filteredAccounts = state.accounts.filter(x => x.id !== action.accountId);
ctx.patchState({
accounts: filteredAccounts
});
}
}
export class AccountInfo {

View File

@ -5,6 +5,26 @@ export class AddStream {
constructor(public stream: StreamElement) {}
}
export class RemoveAllStreams {
static readonly type = '[Streams] Remove all streams';
constructor(public accountId :string) {}
}
export class RemoveStream {
static readonly type = '[Streams] Remove stream';
constructor(public streamId :string) {}
}
export class MoveStreamUp {
static readonly type = '[Streams] Move stream up';
constructor(public streamId :string) {}
}
export class MoveStreamDown {
static readonly type = '[Streams] Move stream down';
constructor(public streamId :string) {}
}
export interface StreamsStateModel {
streams: StreamElement[];
}
@ -23,15 +43,63 @@ export class StreamsState {
streams: [...state.streams, action.stream]
});
}
@Action(RemoveAllStreams)
RemoveAllStreams(ctx: StateContext<StreamsStateModel>, action: RemoveAllStreams){
const state = ctx.getState();
const filteredStreams = state.streams.filter(x => x.accountId !== action.accountId);
ctx.patchState({
streams: [...filteredStreams]
});
}
@Action(RemoveStream)
RemoveStream(ctx: StateContext<StreamsStateModel>, action: RemoveStream){
const state = ctx.getState();
const filteredStreams = state.streams.filter(x => x.id !== action.streamId);
ctx.patchState({
streams: [...filteredStreams]
});
}
@Action(MoveStreamUp)
MoveStreamUp(ctx: StateContext<StreamsStateModel>, action: MoveStreamUp){
const state = ctx.getState();
const sourceIndex = state.streams.findIndex(x => x.id === action.streamId);
if(sourceIndex === 0) return;
let streamsCopy = [...state.streams];
streamsCopy[sourceIndex - 1] = state.streams[sourceIndex];
streamsCopy[sourceIndex] = state.streams[sourceIndex - 1];
ctx.patchState({
streams: streamsCopy
});
}
@Action(MoveStreamDown)
MoveStreamDown(ctx: StateContext<StreamsStateModel>, action: MoveStreamDown){
const state = ctx.getState();
const sourceIndex = state.streams.findIndex(x => x.id === action.streamId);
if(sourceIndex === state.streams.length - 1) return;
let streamsCopy = [...state.streams];
streamsCopy[sourceIndex + 1] = state.streams[sourceIndex];
streamsCopy[sourceIndex] = state.streams[sourceIndex + 1];
ctx.patchState({
streams: streamsCopy
});
}
}
export class StreamElement {
public id: string;
constructor(
public type: StreamTypeEnum,
public name: string,
public accountId: string,
public tag: string,
public list: string) {
public list: string,
public displayableFullName: string) {
this.id = `${type}-${name}-${accountId}`;
}
}

View File

@ -4,4 +4,14 @@
display: table;
clear: both;
}
}
@mixin clearButton {
background: none;
color: inherit;
border: none;
padding: 0;
font: inherit;
cursor: pointer;
outline: inherit;
}

View File

@ -6,7 +6,7 @@ $color-primary: #141824;
$color-secondary: #090b10;
$column-color: #0f111a;
$column-header-background-color: black;
$column-header-background-color: #0c0c10;
@ -32,6 +32,7 @@ $stream-selector-height: 30px;
$stream-column-separator: 7px;
$stream-column-width: 320px;
$avatar-column-space: 70px;
//Bootstrap cuistomization
$enable-rounded: false;