Sengi-Windows-MacOS-Linux/src/app/components/common/timeline-base.ts

123 lines
4.2 KiB
TypeScript

import { OnInit, Input, OnDestroy, Output, EventEmitter, ElementRef, ViewChild } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { MastodonWrapperService } from '../../services/mastodon-wrapper.service';
import { AccountInfo } from '../../states/accounts.state';
import { StreamingWrapper } from '../../services/streaming.service';
import { NotificationService } from '../../services/notification.service';
import { ToolsService, OpenThreadEvent } from '../../services/tools.service';
import { StatusWrapper } from '../../models/common.model';
import { Status } from '../../services/models/mastodon.interfaces';
import { TimeLineModeEnum } from '../../states/settings.state';
import { BrowseBase } from './browse-base';
export abstract class TimelineBase extends BrowseBase {
isLoading = true;
protected maxReached = false;
protected lastCallReachedMax = false;
isThread = false;
displayError: string;
hasContentWarnings = false;
timelineLoadingMode: TimeLineModeEnum = TimeLineModeEnum.OnTop;
protected account: AccountInfo;
protected websocketStreaming: StreamingWrapper;
statuses: StatusWrapper[] = [];
bufferStream: Status[] = [];
protected bufferWasCleared: boolean;
numNewItems: number;
streamPositionnedAtTop: boolean = true;
protected isProcessingInfiniteScroll: boolean;
protected hideBoosts: boolean;
protected hideReplies: boolean;
protected hideBots: boolean;
@Input() goToTop: Observable<void>;
@Input() userLocked = true;
@ViewChild('statusstream') public statustream: ElementRef;
constructor(
protected readonly toolsService: ToolsService,
protected readonly notificationService: NotificationService,
protected readonly mastodonService: MastodonWrapperService) {
super();
}
abstract ngOnInit();
abstract ngOnDestroy();
protected abstract scrolledToTop();
protected abstract statusProcessOnGoToTop();
protected abstract getNextStatuses(): Promise<Status[]>;
onScroll() {
var element = this.statustream.nativeElement as HTMLElement;
const atBottom = element.scrollHeight <= element.clientHeight + element.scrollTop + 1000;
const atTop = element.scrollTop === 0;
this.streamPositionnedAtTop = false;
if (atBottom && !this.isProcessingInfiniteScroll) {
this.scrolledToBottom();
} else if (atTop) {
this.scrolledToTop();
}
}
private scrolledErrorOccured = false;
protected scrolledToBottom() {
if (this.isLoading || this.maxReached || this.scrolledErrorOccured) return;
this.isLoading = true;
this.isProcessingInfiniteScroll = true;
this.getNextStatuses()
.then((status: Status[]) => {
if (!status || status.length === 0 || this.maxReached) {
this.maxReached = true;
return;
}
if (status) {
for (const s of status) {
let cwPolicy = this.toolsService.checkContentWarning(s);
const wrapper = new StatusWrapper(cwPolicy.status, this.account, cwPolicy.applyCw, cwPolicy.hide);
this.statuses.push(wrapper);
}
}
if(this.lastCallReachedMax){
this.maxReached = true;
}
})
.catch((err: HttpErrorResponse) => {
this.scrolledErrorOccured = true;
setTimeout(() => {
this.scrolledErrorOccured = false;
}, 5000);
this.notificationService.notifyHttpError(err, this.account);
})
.then(() => {
this.isLoading = false;
this.isProcessingInfiniteScroll = false;
});
}
applyGoToTop(): boolean {
this.statusProcessOnGoToTop();
const stream = this.statustream.nativeElement as HTMLElement;
setTimeout(() => {
stream.scrollTo({
top: 0,
behavior: 'smooth'
});
}, 0);
return false;
}
}