Merge pull request #4 from NicolasConstant/topic-start-column-handling
Topic start column handling
This commit is contained in:
commit
6280409a40
|
@ -14,7 +14,7 @@ export class AppComponent implements OnInit, OnDestroy{
|
||||||
|
|
||||||
title = 'app';
|
title = 'app';
|
||||||
|
|
||||||
private floatingColumnActive: boolean;
|
floatingColumnActive: boolean;
|
||||||
private columnEditorSub: Subscription;
|
private columnEditorSub: Subscription;
|
||||||
|
|
||||||
constructor(private readonly navigationService: NavigationService) {
|
constructor(private readonly navigationService: NavigationService) {
|
||||||
|
|
|
@ -15,10 +15,8 @@ import { LeftSideBarComponent } from "./components/left-side-bar/left-side-bar.c
|
||||||
import { StreamsMainDisplayComponent } from "./pages/streams-main-display/streams-main-display.component";
|
import { StreamsMainDisplayComponent } from "./pages/streams-main-display/streams-main-display.component";
|
||||||
import { StreamComponent } from "./components/stream/stream.component";
|
import { StreamComponent } from "./components/stream/stream.component";
|
||||||
import { StreamsSelectionFooterComponent } from "./components/streams-selection-footer/streams-selection-footer.component";
|
import { StreamsSelectionFooterComponent } from "./components/streams-selection-footer/streams-selection-footer.component";
|
||||||
import { TootComponent } from "./components/toot/toot.component";
|
|
||||||
import { RegisterNewAccountComponent } from "./pages/register-new-account/register-new-account.component";
|
import { RegisterNewAccountComponent } from "./pages/register-new-account/register-new-account.component";
|
||||||
import { AuthService } from "./services/auth.service";
|
import { AuthService } from "./services/auth.service";
|
||||||
import { AccountsService } from "./services/accounts.service";
|
|
||||||
import { StreamingService } from "./services/streaming.service";
|
import { StreamingService } from "./services/streaming.service";
|
||||||
import { RegisteredAppsState } from "./states/registered-apps.state";
|
import { RegisteredAppsState } from "./states/registered-apps.state";
|
||||||
import { AccountsState } from "./states/accounts.state";
|
import { AccountsState } from "./states/accounts.state";
|
||||||
|
@ -28,6 +26,8 @@ import { FloatingColumnComponent } from './components/floating-column/floating-c
|
||||||
import { ColumnsEditorComponent } from './components/floating-column/columns-editor/columns-editor.component';
|
import { ColumnsEditorComponent } from './components/floating-column/columns-editor/columns-editor.component';
|
||||||
import { MessageEditorComponent } from './components/floating-column/message-editor/message-editor.component';
|
import { MessageEditorComponent } from './components/floating-column/message-editor/message-editor.component';
|
||||||
import { StreamsState } from "./states/streams.state";
|
import { StreamsState } from "./states/streams.state";
|
||||||
|
import { StatusComponent } from "./components/stream/status/status.component";
|
||||||
|
import { MastodonService } from "./services/mastodon.service";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "", redirectTo: "home", pathMatch: "full" },
|
{ path: "", redirectTo: "home", pathMatch: "full" },
|
||||||
|
@ -43,7 +43,7 @@ const routes: Routes = [
|
||||||
StreamsMainDisplayComponent,
|
StreamsMainDisplayComponent,
|
||||||
StreamComponent,
|
StreamComponent,
|
||||||
StreamsSelectionFooterComponent,
|
StreamsSelectionFooterComponent,
|
||||||
TootComponent,
|
StatusComponent,
|
||||||
RegisterNewAccountComponent,
|
RegisterNewAccountComponent,
|
||||||
AccountIconComponent,
|
AccountIconComponent,
|
||||||
FloatingColumnComponent,
|
FloatingColumnComponent,
|
||||||
|
@ -65,11 +65,7 @@ const routes: Routes = [
|
||||||
]),
|
]),
|
||||||
NgxsStoragePluginModule.forRoot()
|
NgxsStoragePluginModule.forRoot()
|
||||||
],
|
],
|
||||||
providers: [AuthService, NavigationService, AccountsService, StreamingService, { provide: APP_INITIALIZER, useFactory: settingsServiceFactory, deps: [AccountsService], multi: true }],
|
providers: [AuthService, NavigationService, MastodonService, StreamingService],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|
||||||
function settingsServiceFactory(service: AccountsService) {
|
|
||||||
return () => service.load();
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { Store } from "@ngxs/store";
|
||||||
|
|
||||||
import { Account } from "../../services/models/mastodon.interfaces";
|
import { Account } from "../../services/models/mastodon.interfaces";
|
||||||
import { AccountWrapper } from "../../models/account.models";
|
import { AccountWrapper } from "../../models/account.models";
|
||||||
import { AccountsService } from "../../services/accounts.service";
|
|
||||||
import { AccountsStateModel, AccountInfo } from "../../states/accounts.state";
|
import { AccountsStateModel, AccountInfo } from "../../states/accounts.state";
|
||||||
import { NavigationService } from "../../services/navigation.service";
|
import { NavigationService } from "../../services/navigation.service";
|
||||||
|
import { MastodonService } from "../../services/mastodon.service";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -23,7 +23,7 @@ export class LeftSideBarComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly navigationService: NavigationService,
|
private readonly navigationService: NavigationService,
|
||||||
private readonly accountsService: AccountsService,
|
private readonly mastodonService: MastodonService,
|
||||||
private readonly store: Store) {
|
private readonly store: Store) {
|
||||||
|
|
||||||
this.accounts$ = this.store.select(state => state.registeredaccounts.accounts);
|
this.accounts$ = this.store.select(state => state.registeredaccounts.accounts);
|
||||||
|
@ -42,7 +42,7 @@ export class LeftSideBarComponent implements OnInit, OnDestroy {
|
||||||
this.accounts.push(accWrapper);
|
this.accounts.push(accWrapper);
|
||||||
this.loadedAccounts[accWrapper.username] = acc;
|
this.loadedAccounts[accWrapper.username] = acc;
|
||||||
|
|
||||||
this.accountsService.retrieveAccountDetails(acc)
|
this.mastodonService.retrieveAccountDetails(acc)
|
||||||
.then((result: Account) => {
|
.then((result: Account) => {
|
||||||
accWrapper.avatar = result.avatar;
|
accWrapper.avatar = result.avatar;
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,7 +24,7 @@ export class AccountIconComponent implements OnInit {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
openMenu(event): boolean {
|
openMenu(): boolean {
|
||||||
this.openMenuNotify.emit(this.account);
|
this.openMenuNotify.emit(this.account);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<div class="toot">
|
||||||
|
<img class="toot__avatar" src="{{ status.account.avatar }}" />
|
||||||
|
<a href class="toot__profile-link"><span class="toot__fullname" innerHTML="{{status.account.display_name}}"></span> @<span id="toot-username" innerHTML="{{status.account.username}}"></span></a>
|
||||||
|
<div class="toot__content" innerHTML="{{status.content}}"></div>
|
||||||
|
</div>
|
|
@ -0,0 +1,47 @@
|
||||||
|
.toot {
|
||||||
|
border: solid #06070b;
|
||||||
|
border-width: 0 0 1px 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: calc(100%);
|
||||||
|
min-height: 70px;
|
||||||
|
overflow: hidden;
|
||||||
|
&__avatar {
|
||||||
|
margin: 10px 0 0 10px;
|
||||||
|
/* margin: 0; */
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
&__fullname {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
&__profile-link {
|
||||||
|
color: #353e64;
|
||||||
|
margin: 7px 0 0 70px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
&__content {
|
||||||
|
/*width: calc(100% - 50px);*/
|
||||||
|
margin: 10px 10px 10px 70px;
|
||||||
|
}
|
||||||
|
&__content p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #toot-avatar img {
|
||||||
|
// width: 50px;
|
||||||
|
// height: 50px;
|
||||||
|
// border-radius: 2px;
|
||||||
|
// margin: 0;
|
||||||
|
// }
|
||||||
|
/* #toot-username {
|
||||||
|
color: grey;
|
||||||
|
} */
|
||||||
|
|
||||||
|
/* ::ng-deep .invisible {
|
||||||
|
display: inline;
|
||||||
|
color: red;
|
||||||
|
} */
|
|
@ -1,20 +1,20 @@
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { TootComponent } from './toot.component';
|
import { StatusComponent } from './status.component';
|
||||||
|
|
||||||
describe('TootComponent', () => {
|
describe('StatusComponent', () => {
|
||||||
let component: TootComponent;
|
let component: StatusComponent;
|
||||||
let fixture: ComponentFixture<TootComponent>;
|
let fixture: ComponentFixture<StatusComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ TootComponent ]
|
declarations: [ StatusComponent ]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(TootComponent);
|
fixture = TestBed.createComponent(StatusComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Component, OnInit, Input } from "@angular/core";
|
||||||
|
import { Status } from "../../../services/models/mastodon.interfaces";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-status",
|
||||||
|
templateUrl: "./status.component.html",
|
||||||
|
styleUrls: ["./status.component.scss"]
|
||||||
|
})
|
||||||
|
export class StatusComponent implements OnInit {
|
||||||
|
@Input() status: Status;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
<div class="stream-column">
|
<div class="stream-column">
|
||||||
<div class="stream-column__stream-header">
|
<div class="stream-column__stream-header">
|
||||||
<a href title="return to top" (click)="goToTop()"><h1>{{ stream.streamName.toUpperCase() }}</h1></a>
|
<a href title="return to top" (click)="goToTop()"><h1>{{ streamElement.name.toUpperCase() }}</h1></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="stream-toots" data-simplebar>
|
<div class="stream-toots flexcroll" #statusstream> <!-- data-simplebar -->
|
||||||
<div *ngFor="let toot of toots">
|
<div *ngFor="let status of statuses">
|
||||||
<app-toot [toot]="toot"></app-toot>
|
<app-status [status]="status" ></app-status>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -29,3 +29,22 @@
|
||||||
width: 320px;
|
width: 320px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flexcroll {
|
||||||
|
scrollbar-face-color: #08090d;
|
||||||
|
scrollbar-shadow-color: #08090d;
|
||||||
|
scrollbar-highlight-color: #08090d;
|
||||||
|
scrollbar-3dlight-color: #08090d;
|
||||||
|
scrollbar-darkshadow-color: #08090d;
|
||||||
|
scrollbar-track-color: #08090d;
|
||||||
|
scrollbar-arrow-color: #08090d;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 7px;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
-webkit-border-radius: 0px;
|
||||||
|
border-radius: 0px;
|
||||||
|
background: #08090d;
|
||||||
|
-webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,11 @@
|
||||||
import { Component, OnInit, Input } from "@angular/core";
|
import { Component, OnInit, Input, ElementRef, ViewChild } from "@angular/core";
|
||||||
import { Stream, TootWrapper } from "../../models/stream.models";
|
|
||||||
import { AccountWrapper } from "../../models/account.models";
|
import { AccountWrapper } from "../../models/account.models";
|
||||||
|
import { StreamElement, StreamTypeEnum } from "../../states/streams.state";
|
||||||
|
import { StreamingService, StreamingWrapper, EventEnum, StatusUpdate } from "../../services/streaming.service";
|
||||||
|
import { Store } from "@ngxs/store";
|
||||||
|
import { AccountInfo } from "../../states/accounts.state";
|
||||||
|
import { Status } from "../../services/models/mastodon.interfaces";
|
||||||
|
import { MastodonService } from "../../services/mastodon.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-stream",
|
selector: "app-stream",
|
||||||
|
@ -8,49 +13,72 @@ import { AccountWrapper } from "../../models/account.models";
|
||||||
styleUrls: ["./stream.component.scss"]
|
styleUrls: ["./stream.component.scss"]
|
||||||
})
|
})
|
||||||
export class StreamComponent implements OnInit {
|
export class StreamComponent implements OnInit {
|
||||||
private _stream: Stream;
|
private _streamElement: StreamElement;
|
||||||
|
private account: AccountInfo;
|
||||||
|
private websocketStreaming: StreamingWrapper;
|
||||||
|
|
||||||
|
statuses: Status[] = [];
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set stream(stream: Stream) {
|
set streamElement(streamElement: StreamElement) {
|
||||||
this._stream = stream;
|
this._streamElement = streamElement;
|
||||||
this._stream.statuses.subscribe((toots: TootWrapper[]) => {
|
|
||||||
for (let t of toots) {
|
const splitedUserName = streamElement.username.split('@');
|
||||||
this.toots.push(t);
|
const user = splitedUserName[0];
|
||||||
|
const instance = splitedUserName[1];
|
||||||
|
this.account = this.getRegisteredAccounts().find(x => x.username == user && x.instance == instance);
|
||||||
|
|
||||||
|
this.retrieveToots(); //TODO change this for WebSockets
|
||||||
|
this.launchWebsocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
get streamElement(): StreamElement {
|
||||||
|
return this._streamElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly store: Store,
|
||||||
|
private readonly streamingService: StreamingService,
|
||||||
|
private readonly mastodonService: MastodonService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild('statusstream') public statustream: ElementRef;
|
||||||
|
goToTop(): boolean {
|
||||||
|
const stream = this.statustream.nativeElement as HTMLElement;
|
||||||
|
stream.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRegisteredAccounts(): AccountInfo[] {
|
||||||
|
var regAccounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
|
||||||
|
return regAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private retrieveToots(): void {
|
||||||
|
this.mastodonService.getTimeline(this.account, this._streamElement.type)
|
||||||
|
.then((results: Status[]) => {
|
||||||
|
for (const s of results) {
|
||||||
|
this.statuses.push(s);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get stream(): Stream {
|
private launchWebsocket(): void {
|
||||||
return this._stream;
|
this.websocketStreaming = this.streamingService.getStreaming(this.account, this._streamElement.type);
|
||||||
|
this.websocketStreaming.statusUpdateSubjet.subscribe((update: StatusUpdate) => {
|
||||||
|
if (update) {
|
||||||
|
if (update.type === EventEnum.update) {
|
||||||
|
if (!this.statuses.find(x => x.id == update.status.id)) {
|
||||||
|
this.statuses.unshift(update.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
toots: TootWrapper[] = [];
|
|
||||||
|
|
||||||
constructor(){
|
|
||||||
// var simplebar = new SimpleBar(document.querySelector('#mam-stream-toots'), { autoHide: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
//Stubs
|
|
||||||
//const newStream = new Stream();
|
|
||||||
//newStream.streamName = "Stream Name";
|
|
||||||
//this.stream = newStream;
|
|
||||||
|
|
||||||
//const acc1 = new AccountWrapper();
|
|
||||||
//acc1.username = "@mastodon.social@Gargron";
|
|
||||||
//acc1.avatar = "https://files.mastodon.social/accounts/avatars/000/000/001/original/4df197532c6b768c.png";
|
|
||||||
|
|
||||||
//for (let i = 0; i < 20; i++) {
|
|
||||||
// const newToot = new TootWrapper();
|
|
||||||
// newToot.account = acc1;
|
|
||||||
// newToot.content = "Lorem Elsass ipsum tristique semper elit jetz gehts los lacus habitant Hans sagittis baeckeoffe condimentum id, salu bredele ch'ai libero, ftomi! hop Pfourtz ! id munster auctor, Miss Dahlias rhoncus Yo dû. Salu bissame turpis ante amet non sed gal Spätzle Gal !";
|
|
||||||
// this.toots.push(newToot);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
goToTop(): boolean {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -26,9 +26,7 @@ export class StreamsSelectionFooterComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onColumnSelection(index: number): boolean {
|
onColumnSelection(index: number): boolean {
|
||||||
console.warn(`column selected: ${index}`);
|
|
||||||
this.navigationService.columnSelected(index);
|
this.navigationService.columnSelected(index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<div id="toot">
|
|
||||||
<div id="toot-avatar">
|
|
||||||
<img src="{{ toot.account.avatar }}" />
|
|
||||||
</div>
|
|
||||||
<a href id="toot-profile-link"><span id="toot-fullname" innerHTML="{{toot.account.display_name}}"></span> @<span id="toot-username" innerHTML="{{toot.account.username}}"></span></a>
|
|
||||||
<div id="toot-content" innerHTML="{{toot.content}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,54 +0,0 @@
|
||||||
#toot {
|
|
||||||
border: solid #06070b;
|
|
||||||
border-width: 0 0 1px 0;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: calc(100%);
|
|
||||||
min-height: 70px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#toot-avatar {
|
|
||||||
margin: 10px 0 0 10px;
|
|
||||||
/* margin: 0; */
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
float: left;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#toot-avatar img {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#toot-fullname {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* #toot-username {
|
|
||||||
color: grey;
|
|
||||||
} */
|
|
||||||
|
|
||||||
#toot-profile-link {
|
|
||||||
color: #353e64;
|
|
||||||
margin: 7px 0 0 70px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#toot-content {
|
|
||||||
/*width: calc(100% - 50px);*/
|
|
||||||
margin: 10px 10px 10px 70px ;
|
|
||||||
}
|
|
||||||
|
|
||||||
#toot-content p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.85em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::ng-deep .invisible {
|
|
||||||
display: inline;
|
|
||||||
color: red;
|
|
||||||
} */
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { Component, OnInit, Input } from "@angular/core";
|
|
||||||
import { TootWrapper } from "../../models/stream.models";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-toot",
|
|
||||||
templateUrl: "./toot.component.html",
|
|
||||||
styleUrls: ["./toot.component.scss"]
|
|
||||||
})
|
|
||||||
export class TootComponent implements OnInit {
|
|
||||||
@Input() toot: TootWrapper;
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
import { Http, Headers, Response } from "@angular/http";
|
|
||||||
import { BehaviorSubject } from "rxjs";
|
|
||||||
|
|
||||||
import { AccountWrapper } from "./account.models";
|
|
||||||
// import { LocalAccount } from "../services/accounts.service";
|
|
||||||
import { ApiRoutes } from "../services/models/api.settings";
|
|
||||||
import { Account, Status } from "../services/models/mastodon.interfaces";
|
|
||||||
import { StreamingService, StreamingWrapper } from "../services/streaming.service";
|
|
||||||
import { StreamTypeEnum } from "../states/streams.state";
|
|
||||||
|
|
||||||
export class Stream {
|
|
||||||
private apiRoutes = new ApiRoutes();
|
|
||||||
|
|
||||||
statuses = new BehaviorSubject<TootWrapper[]>([]);
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly httpService: Http,
|
|
||||||
public streamName: string,
|
|
||||||
private readonly type: StreamTypeEnum) {
|
|
||||||
|
|
||||||
this.retrieveToots(); //TODO change this for WebSockets
|
|
||||||
}
|
|
||||||
|
|
||||||
private test: StreamingWrapper;
|
|
||||||
private retrieveToots(): void {
|
|
||||||
// //TEST
|
|
||||||
// const service = new StreamingService();
|
|
||||||
// this.test = service.getStreaming(this.account.mastodonInstance, this.account.tokenData.access_token);
|
|
||||||
// //END TEST
|
|
||||||
|
|
||||||
const route = this.getTimelineRoute();
|
|
||||||
|
|
||||||
const header = new Headers();
|
|
||||||
// header.append("Authorization", `Bearer ${this.account.tokenData.access_token}`);
|
|
||||||
|
|
||||||
// this.httpService.get(this.account.mastodonInstance + route, { headers: header }).toPromise()
|
|
||||||
// .then((res: Response) => {
|
|
||||||
// const statuses = (res.json() as Status[])
|
|
||||||
// .map((status: Status) => {
|
|
||||||
// return new TootWrapper(status);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// this.statuses.next(statuses);
|
|
||||||
// });
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private getTimelineRoute(): string {
|
|
||||||
switch (this.type) {
|
|
||||||
case StreamTypeEnum.personnal:
|
|
||||||
return this.apiRoutes.getHomeTimeline;
|
|
||||||
case StreamTypeEnum.local:
|
|
||||||
return this.apiRoutes.getPublicTimeline + `?Local=true`;
|
|
||||||
case StreamTypeEnum.global:
|
|
||||||
return this.apiRoutes.getPublicTimeline + `?Local=false`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// export enum StreamTypeEnum {
|
|
||||||
// Home,
|
|
||||||
// Public,
|
|
||||||
// Local
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
export class TootWrapper {
|
|
||||||
constructor(status: Status) {
|
|
||||||
this.account = new AccountWrapper();
|
|
||||||
this.account.username = status.account.username;
|
|
||||||
this.account.display_name = status.account.display_name;
|
|
||||||
this.account.avatar = status.account.avatar;
|
|
||||||
|
|
||||||
this.content = status.content;
|
|
||||||
}
|
|
||||||
|
|
||||||
account: AccountWrapper; //TODO change to Account
|
|
||||||
content: string;
|
|
||||||
}
|
|
|
@ -5,9 +5,9 @@ import { Observable } from "rxjs";
|
||||||
|
|
||||||
import { AuthService } from "../../services/auth.service";
|
import { AuthService } from "../../services/auth.service";
|
||||||
import { TokenData, AppData } from "../../services/models/mastodon.interfaces";
|
import { TokenData, AppData } from "../../services/models/mastodon.interfaces";
|
||||||
import { AccountsService } from "../../services/accounts.service";
|
|
||||||
import { AddRegisteredApp, RegisteredAppsState, RegisteredAppsStateModel, AppInfo } from "../../states/registered-apps.state";
|
import { AddRegisteredApp, RegisteredAppsState, RegisteredAppsStateModel, AppInfo } from "../../states/registered-apps.state";
|
||||||
import { AccountInfo, AddAccount } from "../../states/accounts.state";
|
import { AccountInfo, AddAccount } from "../../states/accounts.state";
|
||||||
|
import { MastodonService } from "../../services/mastodon.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-register-new-account",
|
selector: "app-register-new-account",
|
||||||
|
@ -23,7 +23,6 @@ export class RegisterNewAccountComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly authService: AuthService,
|
private readonly authService: AuthService,
|
||||||
private readonly accountsService: AccountsService,
|
|
||||||
private readonly store: Store,
|
private readonly store: Store,
|
||||||
private readonly activatedRoute: ActivatedRoute) {
|
private readonly activatedRoute: ActivatedRoute) {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="main-display flexcroll">
|
<div class="main-display flexcroll">
|
||||||
<div class="main-display__stream-column" *ngFor="let s of streams">
|
<div class="main-display__stream-column" *ngFor="let s of streamElements$ | async">
|
||||||
<app-stream [stream]="s" #stream></app-stream>
|
<app-stream [streamElement]="s" #stream></app-stream>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import { Component, OnInit, OnDestroy, ViewChild, QueryList, ViewChildren, ElementRef } from "@angular/core";
|
import { Component, OnInit, OnDestroy, QueryList, ViewChildren, ElementRef } from "@angular/core";
|
||||||
|
|
||||||
import { Stream } from "../../models/stream.models";
|
|
||||||
import { Observable, Subscription } from "rxjs";
|
import { Observable, Subscription } from "rxjs";
|
||||||
import { StreamElement } from "../../states/streams.state";
|
import { Select } from "@ngxs/store";
|
||||||
import { Store } from "@ngxs/store";
|
|
||||||
import { Http } from "@angular/http";
|
|
||||||
import { NavigationService } from "../../services/navigation.service";
|
|
||||||
|
|
||||||
|
import { StreamElement } from "../../states/streams.state";
|
||||||
|
import { NavigationService } from "../../services/navigation.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-streams-main-display",
|
selector: "app-streams-main-display",
|
||||||
|
@ -15,51 +12,29 @@ import { NavigationService } from "../../services/navigation.service";
|
||||||
})
|
})
|
||||||
export class StreamsMainDisplayComponent implements OnInit, OnDestroy {
|
export class StreamsMainDisplayComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
streams: Stream[] = [];
|
@Select(state => state.streamsstatemodel.streams) streamElements$: Observable<StreamElement[]>;
|
||||||
|
|
||||||
private streams$: Observable<StreamElement[]>;
|
|
||||||
private streamsStateSub: Subscription;
|
|
||||||
private columnSelectedSub: Subscription;
|
private columnSelectedSub: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly navigationService: NavigationService,
|
private readonly navigationService: NavigationService) {
|
||||||
private readonly http: Http,
|
|
||||||
private readonly store: Store) {
|
|
||||||
this.streams$ = this.store.select(state => state.streamsstatemodel.streams);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.streamsStateSub = this.streams$.subscribe((streams: StreamElement[]) => {
|
|
||||||
this.streams.length = 0;
|
|
||||||
for (const stream of streams) {
|
|
||||||
const newStream = new Stream(this.http, stream.name, stream.type);
|
|
||||||
this.streams.push(newStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.columnSelectedSub = this.navigationService.columnSelectedSubject.subscribe((columnIndex: number) => {
|
this.columnSelectedSub = this.navigationService.columnSelectedSubject.subscribe((columnIndex: number) => {
|
||||||
this.focusOnColumn(columnIndex);
|
this.focusOnColumn(columnIndex);
|
||||||
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.streamsStateSub.unsubscribe();
|
|
||||||
this.columnSelectedSub.unsubscribe();
|
this.columnSelectedSub.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewChildren('stream', { read: ElementRef }) public streamsElementRef: QueryList<ElementRef>;;
|
@ViewChildren('stream', { read: ElementRef }) public streamsElementRef: QueryList<ElementRef>;;
|
||||||
private focusOnColumn(columnIndex: number): void {
|
private focusOnColumn(columnIndex: number): void {
|
||||||
console.warn(`col selected: ${columnIndex}`);
|
|
||||||
|
|
||||||
if (columnIndex > -1) {
|
if (columnIndex > -1) {
|
||||||
console.warn(this.streamsElementRef);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.streamsElementRef.toArray()[columnIndex].nativeElement.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'start' });
|
this.streamsElementRef.toArray()[columnIndex].nativeElement.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'start' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { Injectable } from "@angular/core";
|
|
||||||
import { Http, Headers, Response } from "@angular/http";
|
|
||||||
import { Subject, BehaviorSubject } from "rxjs";
|
|
||||||
|
|
||||||
import { TokenData, Account } from "./models/mastodon.interfaces";
|
|
||||||
import { ApiRoutes } from "./models/api.settings";
|
|
||||||
import { AccountInfo } from "../states/accounts.state";
|
|
||||||
import { HttpClient, HttpHeaders } from "@angular/common/http";
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class AccountsService {
|
|
||||||
|
|
||||||
private apiRoutes = new ApiRoutes();
|
|
||||||
|
|
||||||
constructor(private readonly httpClient: HttpClient) {}
|
|
||||||
|
|
||||||
retrieveAccountDetails(account: AccountInfo): Promise<Account> {
|
|
||||||
const headers = new HttpHeaders({'Authorization':`Bearer ${account.token.access_token}`});
|
|
||||||
// const headers = new HttpHeaders({'Bearer':`${account.token}`});
|
|
||||||
return this.httpClient.get<Account>('https://' + account.instance + this.apiRoutes.getCurrentAccount, {headers: headers}).toPromise();
|
|
||||||
}
|
|
||||||
|
|
||||||
load(): any {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MastodonService } from './mastodon.service';
|
||||||
|
|
||||||
|
describe('MastodonService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [MastodonService]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([MastodonService], (service: MastodonService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
|
@ -0,0 +1,70 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpHeaders, HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
import { ApiRoutes } from './models/api.settings';
|
||||||
|
import { Account, Status } from "./models/mastodon.interfaces";
|
||||||
|
import { AccountInfo } from '../states/accounts.state';
|
||||||
|
import { StreamTypeEnum } from '../states/streams.state';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MastodonService {
|
||||||
|
|
||||||
|
private apiRoutes = new ApiRoutes();
|
||||||
|
|
||||||
|
constructor(private readonly httpClient: HttpClient) { }
|
||||||
|
|
||||||
|
retrieveAccountDetails(account: AccountInfo): Promise<Account> {
|
||||||
|
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
|
||||||
|
return this.httpClient.get<Account>('https://' + account.instance + this.apiRoutes.getCurrentAccount, { headers: headers }).toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
getTimeline(account: AccountInfo, type: StreamTypeEnum, max_id: string = null, since_id: string = null, limit: number = 20): Promise<Status[]> {
|
||||||
|
const route = `https://${account.instance}${this.getTimelineRoute(type, max_id, since_id, limit)}`;
|
||||||
|
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
|
||||||
|
return this.httpClient.get<Status[]>(route, { headers: headers }).toPromise()
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTimelineRoute(type: StreamTypeEnum, max_id: string, since_id: string, limit: number): string {
|
||||||
|
let route: string;
|
||||||
|
switch (type) {
|
||||||
|
case StreamTypeEnum.personnal:
|
||||||
|
route = this.apiRoutes.getHomeTimeline;
|
||||||
|
break;
|
||||||
|
case StreamTypeEnum.local:
|
||||||
|
route = this.apiRoutes.getPublicTimeline + `?local=true&`;
|
||||||
|
break;
|
||||||
|
case StreamTypeEnum.global:
|
||||||
|
route = this.apiRoutes.getPublicTimeline + `?local=false&`;
|
||||||
|
break;
|
||||||
|
case StreamTypeEnum.directmessages:
|
||||||
|
route = this.apiRoutes.getDirectTimeline;
|
||||||
|
break;
|
||||||
|
case StreamTypeEnum.tag:
|
||||||
|
route = this.apiRoutes.getTagTimeline.replace('{0}', 'TODO');
|
||||||
|
break;
|
||||||
|
case StreamTypeEnum.list:
|
||||||
|
route = this.apiRoutes.getListTimeline.replace('{0}', 'TODO');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('StreamTypeEnum not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!route.includes('?')) route = route + '?';
|
||||||
|
if (max_id) route = route + `max_id=${max_id}&`;
|
||||||
|
if (since_id) route = route + `since_id=${since_id}&`;
|
||||||
|
if (limit) route = route + `limit=${limit}&`;
|
||||||
|
|
||||||
|
return this.trimChar(this.trimChar(route, '?'), '&');
|
||||||
|
}
|
||||||
|
|
||||||
|
private escapeRegExp(strToEscape) {
|
||||||
|
// Escape special characters for use in a regular expression
|
||||||
|
return strToEscape.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||||
|
};
|
||||||
|
|
||||||
|
private trimChar(origString, charToTrim) {
|
||||||
|
charToTrim = this.escapeRegExp(charToTrim);
|
||||||
|
var regEx = new RegExp("^[" + charToTrim + "]+|[" + charToTrim + "]+$", "g");
|
||||||
|
return origString.replace(regEx, "");
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,47 +1,51 @@
|
||||||
|
|
||||||
export class ApiRoutes {
|
export class ApiRoutes {
|
||||||
createApp = "/api/v1/apps";
|
createApp = '/api/v1/apps';
|
||||||
getToken = "/oauth/token";
|
getToken = '/oauth/token';
|
||||||
getAccount = "/api/v1/accounts/{0}";
|
getAccount = '/api/v1/accounts/{0}';
|
||||||
getCurrentAccount = "/api/v1/accounts/verify_credentials";
|
getCurrentAccount = '/api/v1/accounts/verify_credentials';
|
||||||
getAccountFollowers = "/api/v1/accounts/{0}/followers";
|
getAccountFollowers = '/api/v1/accounts/{0}/followers';
|
||||||
getAccountFollowing = "/api/v1/accounts/{0}/following";
|
getAccountFollowing = '/api/v1/accounts/{0}/following';
|
||||||
getAccountStatuses = "/api/v1/accounts/{0}/statuses";
|
getAccountStatuses = '/api/v1/accounts/{0}/statuses';
|
||||||
follow = "/api/v1/accounts/{0}/follow";
|
follow = '/api/v1/accounts/{0}/follow';
|
||||||
unfollow = "/api/v1/accounts/{0}/unfollow";
|
unfollow = '/api/v1/accounts/{0}/unfollow';
|
||||||
block = "/api/v1/accounts/{0}/block";
|
block = '/api/v1/accounts/{0}/block';
|
||||||
unblock = "/api/v1/accounts/{0}/unblock";
|
unblock = '/api/v1/accounts/{0}/unblock';
|
||||||
mute = "/api/v1/accounts/{0}/mute";
|
mute = '/api/v1/accounts/{0}/mute';
|
||||||
unmute = "/api/v1/accounts/{0}/unmute";
|
unmute = '/api/v1/accounts/{0}/unmute';
|
||||||
getAccountRelationships = "/api/v1/accounts/relationships";
|
getAccountRelationships = '/api/v1/accounts/relationships';
|
||||||
searchForAccounts = "/api/v1/accounts/search";
|
searchForAccounts = '/api/v1/accounts/search';
|
||||||
getBlocks = "/api/v1/blocks";
|
getBlocks = '/api/v1/blocks';
|
||||||
getFavourites = "/api/v1/favourites";
|
getFavourites = '/api/v1/favourites';
|
||||||
getFollowRequests = "/api/v1/follow_requests";
|
getFollowRequests = '/api/v1/follow_requests';
|
||||||
authorizeFollowRequest = "/api/v1/follow_requests/authorize";
|
authorizeFollowRequest = '/api/v1/follow_requests/authorize';
|
||||||
rejectFollowRequest = "/api/v1/follow_requests/reject";
|
rejectFollowRequest = '/api/v1/follow_requests/reject';
|
||||||
followRemote = "/api/v1/follows";
|
followRemote = '/api/v1/follows';
|
||||||
getInstance = "/api/v1/instance";
|
getInstance = '/api/v1/instance';
|
||||||
uploadMediaAttachment = "/api/v1/media";
|
uploadMediaAttachment = '/api/v1/media';
|
||||||
getMutes = "/api/v1/mutes";
|
getMutes = '/api/v1/mutes';
|
||||||
getNotifications = "/api/v1/notifications";
|
getNotifications = '/api/v1/notifications';
|
||||||
getSingleNotifications = "/api/v1/notifications/{0}";
|
getSingleNotifications = '/api/v1/notifications/{0}';
|
||||||
clearNotifications = "/api/v1/notifications/clear";
|
clearNotifications = '/api/v1/notifications/clear';
|
||||||
getReports = "/api/v1/reports";
|
getReports = '/api/v1/reports';
|
||||||
reportUser = "/api/v1/reports";
|
reportUser = '/api/v1/reports';
|
||||||
search = "/api/v1/search";
|
search = '/api/v1/search';
|
||||||
getStatus = "/api/v1/statuses/{0}";
|
getStatus = '/api/v1/statuses/{0}';
|
||||||
getStatusContext = "/api/v1/statuses/{0}/context";
|
getStatusContext = '/api/v1/statuses/{0}/context';
|
||||||
getStatusCard = "/api/v1/statuses/{0}/card";
|
getStatusCard = '/api/v1/statuses/{0}/card';
|
||||||
getStatusRebloggedBy = "/api/v1/statuses/{0}/reblogged_by";
|
getStatusRebloggedBy = '/api/v1/statuses/{0}/reblogged_by';
|
||||||
getStatusFavouritedBy = "/api/v1/statuses/{0}/favourited_by";
|
getStatusFavouritedBy = '/api/v1/statuses/{0}/favourited_by';
|
||||||
postNewStatus = "/api/v1/statuses";
|
postNewStatus = '/api/v1/statuses';
|
||||||
deleteStatus = "/api/v1/statuses/{0}";
|
deleteStatus = '/api/v1/statuses/{0}';
|
||||||
reblogStatus = "/api/v1/statuses/{0}/reblog";
|
reblogStatus = '/api/v1/statuses/{0}/reblog';
|
||||||
unreblogStatus = "/api/v1/statuses/{0}/unreblog";
|
unreblogStatus = '/api/v1/statuses/{0}/unreblog';
|
||||||
favouritingStatus = "/api/v1/statuses/{0}/favourite";
|
favouritingStatus = '/api/v1/statuses/{0}/favourite';
|
||||||
unfavouritingStatus = "/api/v1/statuses/{0}/unfavourite";
|
unfavouritingStatus = '/api/v1/statuses/{0}/unfavourite';
|
||||||
getHomeTimeline = "/api/v1/timelines/home";
|
getHomeTimeline = '/api/v1/timelines/home';
|
||||||
getPublicTimeline = "/api/v1/timelines/public";
|
getPublicTimeline = '/api/v1/timelines/public';
|
||||||
getHastagTimeline = "/api/v1/timelines/tag/{0}";
|
getHastagTimeline = '/api/v1/timelines/tag/{0}';
|
||||||
|
getDirectTimeline = '/api/v1/timelines/direct';
|
||||||
|
getTagTimeline = '/api/v1/timelines/tag/{0}';
|
||||||
|
getListTimeline = '/api/v1/timelines/list/{0}';
|
||||||
|
getStreaming = '/api/v1/streaming?access_token={0}&stream={1}';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,126 @@
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
|
import { Status } from "./models/mastodon.interfaces";
|
||||||
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
import { ApiRoutes } from "./models/api.settings";
|
||||||
|
import { StreamTypeEnum } from "../states/streams.state";
|
||||||
|
import { MastodonService } from "./mastodon.service";
|
||||||
|
import { AccountInfo } from "../states/accounts.state";
|
||||||
|
import { stat } from "fs";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StreamingService {
|
export class StreamingService {
|
||||||
|
constructor(
|
||||||
|
private readonly mastodonService: MastodonService) { }
|
||||||
|
|
||||||
constructor() { }
|
getStreaming(accountInfo: AccountInfo, streamType: StreamTypeEnum): StreamingWrapper {
|
||||||
|
return new StreamingWrapper(this.mastodonService, accountInfo, streamType);
|
||||||
//TODO restructure this to handle real domain objects
|
|
||||||
getStreaming(mastodonInstance: string, accessToken: string): StreamingWrapper {
|
|
||||||
return new StreamingWrapper(mastodonInstance.replace("https://", "wss://") + `/api/v1/streaming//?access_token=${accessToken}&stream=public`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StreamingWrapper {
|
export class StreamingWrapper {
|
||||||
|
statusUpdateSubjet = new BehaviorSubject<StatusUpdate>(null);
|
||||||
|
eventSource: WebSocket;
|
||||||
|
private apiRoutes = new ApiRoutes();
|
||||||
|
|
||||||
constructor(private readonly domain: string) {
|
constructor(
|
||||||
const eventSource = new WebSocket(domain);
|
private readonly mastodonService: MastodonService,
|
||||||
eventSource.onmessage = x => console.warn(JSON.parse(x.data));
|
private readonly accountInfo: AccountInfo,
|
||||||
eventSource.onerror = x => console.error(x);
|
private readonly streamType: StreamTypeEnum) {
|
||||||
eventSource.onopen = x => console.log(x);
|
|
||||||
eventSource.onclose = x => console.log(x);
|
const request = this.getRequest(streamType);
|
||||||
|
const route = `wss://${accountInfo.instance}${this.apiRoutes.getStreaming}`.replace('{0}', accountInfo.token.access_token).replace('{1}', request);
|
||||||
|
this.start(route);
|
||||||
|
}
|
||||||
|
|
||||||
|
private start(route: string) {
|
||||||
|
this.eventSource = new WebSocket(route);
|
||||||
|
this.eventSource.onmessage = x => this.statusParsing(<WebSocketEvent>JSON.parse(x.data));
|
||||||
|
this.eventSource.onerror = x => this.webSocketGotError(x);
|
||||||
|
this.eventSource.onopen = x => console.log(x);
|
||||||
|
this.eventSource.onclose = x => this.webSocketClosed(route, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private errorClosing: boolean;
|
||||||
|
private webSocketGotError(x: Event) {
|
||||||
|
this.errorClosing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private since_id: string;
|
||||||
|
private webSocketClosed(domain, x: Event) {
|
||||||
|
console.log(x);
|
||||||
|
|
||||||
|
if (this.errorClosing) {
|
||||||
|
this.mastodonService.getTimeline(this.accountInfo, this.streamType, null, this.since_id)
|
||||||
|
.then((status: Status[]) => {
|
||||||
|
// status = status.sort((n1, n2) => { return (<number>n1.id) < (<number>n2.id); });
|
||||||
|
status = status.sort((a, b) => a.id.localeCompare(b.id));
|
||||||
|
for (const s of status) {
|
||||||
|
const update = new StatusUpdate();
|
||||||
|
update.status = s;
|
||||||
|
update.type = EventEnum.update;
|
||||||
|
this.since_id = update.status.id;
|
||||||
|
this.statusUpdateSubjet.next(update);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
setTimeout(() => { this.start(domain) }, 20 * 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.errorClosing = false;
|
||||||
|
} else {
|
||||||
|
setTimeout(() => { this.start(domain) }, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private statusParsing(event: WebSocketEvent) {
|
||||||
|
const newUpdate = new StatusUpdate();
|
||||||
|
|
||||||
|
switch (event.event) {
|
||||||
|
case 'update':
|
||||||
|
newUpdate.type = EventEnum.update;
|
||||||
|
newUpdate.status = <Status>JSON.parse(event.payload);
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
newUpdate.type = EventEnum.delete;
|
||||||
|
newUpdate.messageId = event.payload;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
newUpdate.type = EventEnum.unknow;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.statusUpdateSubjet.next(newUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRequest(type: StreamTypeEnum): string {
|
||||||
|
switch (type) {
|
||||||
|
case StreamTypeEnum.global:
|
||||||
|
return 'public';
|
||||||
|
case StreamTypeEnum.local:
|
||||||
|
return 'public:local';
|
||||||
|
case StreamTypeEnum.personnal:
|
||||||
|
return 'user';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WebSocketEvent {
|
||||||
|
event: string;
|
||||||
|
payload: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StatusUpdate {
|
||||||
|
type: EventEnum;
|
||||||
|
status: Status;
|
||||||
|
messageId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum EventEnum {
|
||||||
|
unknow = 0,
|
||||||
|
update = 1,
|
||||||
|
delete = 2
|
||||||
|
}
|
||||||
|
|
|
@ -40,5 +40,6 @@ export class StreamElement {
|
||||||
activity = 5,
|
activity = 5,
|
||||||
list = 6,
|
list = 6,
|
||||||
directmessages = 7,
|
directmessages = 7,
|
||||||
}
|
tag = 8,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue