diff --git a/src/app/components/floating-column/manage-account/favorites/favorites.component.ts b/src/app/components/floating-column/manage-account/favorites/favorites.component.ts index 9ff75dbe..02802d76 100644 --- a/src/app/components/floating-column/manage-account/favorites/favorites.component.ts +++ b/src/app/components/floating-column/manage-account/favorites/favorites.component.ts @@ -1,11 +1,12 @@ -import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core'; +import { Component, OnInit, Output, EventEmitter, Input, ViewChild, ElementRef } from '@angular/core'; import { StatusWrapper } from '../../../../models/common.model'; import { OpenThreadEvent } from '../../../../services/tools.service'; import { AccountWrapper } from '../../../../models/account.models'; -import { MastodonService } from '../../../../services/mastodon.service'; +import { MastodonService, FavoriteResult } from '../../../../services/mastodon.service'; import { Status } from '../../../../services/models/mastodon.interfaces'; import { NotificationService } from '../../../../services/notification.service'; +import { resetCompiledComponents } from '@angular/core/src/render3/jit/module'; @Component({ selector: 'app-favorites', @@ -15,7 +16,7 @@ import { NotificationService } from '../../../../services/notification.service'; export class FavoritesComponent implements OnInit { statuses: StatusWrapper[] = []; displayError: string; - isLoading = true; + isLoading = true; isThread = false; hasContentWarnings = false; @@ -23,24 +24,43 @@ export class FavoritesComponent implements OnInit { @Output() browseHashtagEvent = new EventEmitter(); @Output() browseThreadEvent = new EventEmitter(); - @Input() account: AccountWrapper; + private maxReached = false; + private maxId: string; + private _account: AccountWrapper; + + @Input('account') + set account(acc: AccountWrapper) { + console.warn('account'); + this._account = acc; + this.getFavorites(); + } + get account(): AccountWrapper { + return this._account; + } + + @ViewChild('statusstream') public statustream: ElementRef; constructor( private readonly notificationService: NotificationService, private readonly mastodonService: MastodonService) { } ngOnInit() { - this.getFavorites(); } - private getFavorites(){ + private reset(){ this.isLoading = true; this.statuses.length = 0; + this.maxReached = false; + this.maxId = null; + } + + private getFavorites() { + this.reset(); this.mastodonService.getFavorites(this.account.info) - .then((statuses: Status[]) => { - - for (const s of statuses) { + .then((result: FavoriteResult) => { + this.maxId = result.max_id; + for (const s of result.favorites) { const wrapper = new StatusWrapper(s, this.account.info); this.statuses.push(wrapper); } @@ -54,4 +74,51 @@ export class FavoritesComponent implements OnInit { } + onScroll() { + var element = this.statustream.nativeElement as HTMLElement; + const atBottom = element.scrollHeight <= element.clientHeight + element.scrollTop + 1000; + + if (atBottom) { + this.scrolledToBottom(); + } + } + + + private scrolledToBottom() { + if (this.isLoading || this.maxReached) return; + + this.isLoading = true; + this.mastodonService.getFavorites(this.account.info, this.maxId) + .then((result: FavoriteResult) => { + const statuses = result.favorites; + if (statuses.length === 0 || !this.maxId) { + this.maxReached = true; + return; + } + + this.maxId = result.max_id; + for (const s of statuses) { + const wrapper = new StatusWrapper(s, this.account.info); + this.statuses.push(wrapper); + } + }) + .catch(err => { + this.notificationService.notifyHttpError(err); + }) + .then(() => { + this.isLoading = false; + }); + } + + browseAccount(accountName: string): void { + this.browseAccountEvent.next(accountName); + } + + browseHashtag(hashtag: string): void { + this.browseHashtagEvent.next(hashtag); + } + + browseThread(openThreadEvent: OpenThreadEvent): void { + this.browseThreadEvent.next(openThreadEvent); + } } diff --git a/src/app/services/mastodon.service.ts b/src/app/services/mastodon.service.ts index bdb5526d..fbce99af 100644 --- a/src/app/services/mastodon.service.ts +++ b/src/app/services/mastodon.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { HttpHeaders, HttpClient } from '@angular/common/http'; +import { HttpHeaders, HttpClient, HttpResponse } from '@angular/common/http'; import { ApiRoutes } from './models/api.settings'; import { Account, Status, Results, Context, Relationship, Instance, Attachment } from "./models/mastodon.interfaces"; @@ -7,7 +7,7 @@ import { AccountInfo } from '../states/accounts.state'; import { StreamTypeEnum } from '../states/streams.state'; @Injectable() -export class MastodonService { +export class MastodonService { private apiRoutes = new ApiRoutes(); constructor(private readonly httpClient: HttpClient) { } @@ -133,12 +133,27 @@ export class MastodonService { const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); return this.httpClient.get(route, { headers: headers }).toPromise(); } - - getFavorites(account: AccountInfo): Promise { - const route = `https://${account.instance}${this.apiRoutes.getFavourites}`; + + getFavorites(account: AccountInfo, maxId: string = null): Promise { //, minId: string = null + let route = `https://${account.instance}${this.apiRoutes.getFavourites}`; //?limit=${limit} + + if (maxId) route += `?max_id=${maxId}`; + //if (minId) route += `&min_id=${minId}`; + const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); - return this.httpClient.get(route, { headers: headers }).toPromise(); - } + return this.httpClient.get(route, { headers: headers, observe: "response" }).toPromise() + .then((res: HttpResponse) => { + const link = res.headers.get('Link'); + let lastId = null; + if(link){ + const maxId = link.split('max_id=')[1]; + if(maxId){ + lastId = maxId.split('>;')[0]; + } + } + return new FavoriteResult(lastId, res.body); + }); + } searchAccount(account: AccountInfo, query: string, limit: number = 40, following: boolean = false): Promise { const route = `https://${account.instance}${this.apiRoutes.searchForAccounts}?q=${query}&limit=${limit}&following=${following}`; @@ -168,7 +183,7 @@ export class MastodonService { const route = `https://${account.instance}${this.apiRoutes.unfavouritingStatus}`.replace('{0}', status.id); const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); return this.httpClient.post(route, null, { headers: headers }).toPromise() - } + } getRelationships(account: AccountInfo, accountsToRetrieve: Account[]): Promise { let params = `?${this.formatArray(accountsToRetrieve.map(x => x.id.toString()), 'id')}`; @@ -206,7 +221,7 @@ export class MastodonService { } //TODO: add focus support - updateMediaAttachment(account: AccountInfo, mediaId: string, description: string): Promise { + updateMediaAttachment(account: AccountInfo, mediaId: string, description: string): Promise { let input = new FormData(); input.append('description', description); const route = `https://${account.instance}${this.apiRoutes.updateMediaAttachment.replace('{0}', mediaId)}`; @@ -239,4 +254,10 @@ class StatusData { sensitive: boolean; spoiler_text: string; visibility: string; +} + +export class FavoriteResult { + constructor( + public max_id: string, + public favorites: Status[]) {} } \ No newline at end of file