This commit is contained in:
Drew Ansbacher
2021-08-25 13:00:02 -06:00
committed by Aiden McClelland
parent 37a6df0815
commit 95c95a9fe4
12 changed files with 183 additions and 138 deletions

View File

@@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { BadgeMenuComponent } from './badge-menu.component'
import { IonicModule } from '@ionic/angular'
import { RouterModule } from '@angular/router'
import { SharingModule } from 'src/app/modules/sharing.module'
@NgModule({
@@ -12,7 +11,6 @@ import { SharingModule } from 'src/app/modules/sharing.module'
imports: [
CommonModule,
IonicModule,
RouterModule.forChild([]),
SharingModule,
],
exports: [BadgeMenuComponent],

View File

@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { IonicModule } from '@ionic/angular'
import { LogsPage } from './logs.page'
import { SharingModule } from 'src/app/modules/sharing.module'
@NgModule({
declarations: [LogsPage],
imports: [
CommonModule,
IonicModule,
SharingModule,
],
exports: [LogsPage],
})
export class LogsPageModule { }

View File

@@ -0,0 +1,42 @@
<ion-content
[scrollEvents]="true"
(ionScroll)="scrollEvent($event)"
style="height: 100%;"
id="ion-content"
class="ion-padding"
>
<ion-infinite-scroll id="scroller" *ngIf="!loading && needInfinite" position="top" threshold="0" (ionInfinite)="loadData($event)">
<ion-infinite-scroll-content
loadingSpinner="bubbles">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
<text-spinner *ngIf="loading" text="Loading Logs"></text-spinner>
<div id="container">
<div id="template" style="white-space: pre-line;"></div>
</div>
<div id="button-div" *ngIf="!loading" style="width: 100%; text-align: center;">
<ion-button *ngIf="!loadingMore" (click)="loadMore()" strong color="dark">
Load More
<ion-icon slot="end" name="refresh"></ion-icon>
</ion-button>
<ion-spinner *ngIf="loadingMore" name="lines" color="warning"></ion-spinner>
</div>
<div
[ngStyle]="{
'position': 'fixed',
'bottom': '50px',
'right': isOnBottom ? '-50px' : '30px',
'background-color': 'var(--ion-color-medium)',
'border-radius': '100%',
'transition': 'right 0.4s ease-out'
}"
>
<ion-button style="width: 50px; height: 50px; --padding-start: 0px; --padding-end: 0px; --border-radius: 100%;" fill="clear" (click)="scrollToBottom()">
<ion-icon style="font-size: 60px; --ionicon-stroke-width: 8px;" name="chevron-down-circle-outline"></ion-icon>
</ion-button>
</div>
</ion-content>

View File

@@ -0,0 +1,95 @@
import { Component, Input, ViewChild } from '@angular/core'
import { IonContent } from '@ionic/angular'
import { ErrorToastService } from 'src/app/services/error-toast.service'
import { Log } from 'src/app/services/api/api.types'
@Component({
selector: 'logs',
templateUrl: './logs.page.html',
styleUrls: ['./logs.page.scss'],
})
export class LogsPage {
@ViewChild(IonContent) private content: IonContent
@Input() fetchLogs: (params: { before?: string, after?: string, limit: number }) => Promise<Log[]>
loading = true
loadingMore = false
logs: string
needInfinite = true
before: string
after: string
limit = 200
scrollToBottomButton = false
isOnBottom = true
constructor (
private readonly errToast: ErrorToastService,
) { }
ngOnInit () {
this.getLogs()
}
async fetch (isBefore: boolean = true) {
const logs = await this.fetchLogs({
before: isBefore ? this.before : undefined,
after: !isBefore ? this.after : undefined,
limit: this.limit,
})
this.before = logs[0]?.timestamp
this.after = logs[logs.length - 1]?.timestamp
this.loading = false
return logs
}
async getLogs () {
try {
// get logs
const logs = await this.fetch()
this.before = logs[0].timestamp
const container = document.getElementById('container')
const beforeContainerHeight = container.scrollHeight
const newLogs = document.getElementById('template').cloneNode(true) as HTMLElement
newLogs.innerHTML = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n') + (logs.length ? '\n\n' : '')
container.prepend(newLogs)
const afterContainerHeight = container.scrollHeight
// scroll down
scrollBy(0, afterContainerHeight - beforeContainerHeight)
this.content.scrollToPoint(0, afterContainerHeight - beforeContainerHeight)
if (logs.length < this.limit) {
this.needInfinite = false
}
} catch (e) {
this.errToast.present(e)
}
}
async loadMore () {
this.loadingMore = true
const logs = await this.fetch(false)
const container = document.getElementById('container')
const newLogs = document.getElementById('template').cloneNode(true) as HTMLElement
newLogs.innerHTML = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n') + (logs.length ? '\n\n' : '')
container.append(newLogs)
this.loadingMore = false
this.scrollEvent()
}
scrollEvent () {
const buttonDiv = document.getElementById('button-div')
this.isOnBottom = buttonDiv.getBoundingClientRect().top < window.innerHeight
}
scrollToBottom () {
this.content.scrollToBottom(500)
}
async loadData (e: any): Promise<void> {
await this.getLogs()
e.target.complete()
}
}

View File

@@ -4,6 +4,7 @@ import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { AppLogsPage } from './app-logs.page'
import { SharingModule } from 'src/app/modules/sharing.module'
import { LogsPageModule } from 'src/app/components/logs/logs.module'
const routes: Routes = [
{
@@ -18,6 +19,7 @@ const routes: Routes = [
IonicModule,
RouterModule.forChild(routes),
SharingModule,
LogsPageModule,
],
declarations: [AppLogsPage],
})

View File

@@ -4,24 +4,9 @@
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>Logs</ion-title>
<ion-buttons slot="end">
<ion-button (click)="getLogs()">
<ion-icon slot="start" name="refresh-outline"></ion-icon>
Refresh
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content id="ion-content" class="ion-padding">
<ion-infinite-scroll id="scroller" *ngIf="!loading && needInfinite" position="top" threshold="0" (ionInfinite)="loadData($event)">
<ion-infinite-scroll-content
loadingSpinner="bubbles">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
<text-spinner *ngIf="loading" text="Loading Logs"></text-spinner>
<div id="container"><div id="template" style="white-space: pre-line;"></div></div>
</ion-content>
<div style="height: 100%">
<logs [fetchLogs]="fetchFetchLogs()"></logs>
</div>

View File

@@ -1,8 +1,5 @@
import { Component, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { Component } from '@angular/core'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { IonContent } from '@ionic/angular'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
selector: 'app-logs',
@@ -10,68 +7,24 @@ import { ErrorToastService } from 'src/app/services/error-toast.service'
styleUrls: ['./app-logs.page.scss'],
})
export class AppLogsPage {
@ViewChild(IonContent, { static: false }) private content: IonContent
pkgId: string
loading = true
needInfinite = true
before: string
constructor (
private readonly route: ActivatedRoute,
private readonly errToast: ErrorToastService,
private readonly embassyApi: ApiService,
) { }
async ngOnInit () {
this.pkgId = this.route.snapshot.paramMap.get('pkgId')
this.getLogs()
}
async refresh () {
this.before = undefined
this.loading = true
const container = document.getElementById('container')
const newLogs = document.getElementById('template').cloneNode(true) as HTMLElement
while (container.firstChild) { container.removeChild(container.firstChild) }
container.append(newLogs)
this.getLogs()
}
async getLogs () {
const limit = 200
try {
// get logs
const logs = await this.embassyApi.getPackageLogs({
id: this.pkgId,
before: this.before,
limit,
fetchFetchLogs (): Function {
return async (params: { after?: string, before?: string, limit: number }) => {
const pkgId = this.pkgId
return this.embassyApi.getPackageLogs({
id: pkgId,
after: params.after,
before: params.before,
limit: params.limit,
})
this.before = logs[0].timestamp
const container = document.getElementById('container')
const beforeContainerHeight = container.scrollHeight
const newLogs = document.getElementById('template').cloneNode(true) as HTMLElement
newLogs.innerHTML = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n') + (logs.length ? '\n\n' : '')
container.prepend(newLogs)
const afterContainerHeight = container.scrollHeight
// scroll down
scrollBy(0, afterContainerHeight - beforeContainerHeight)
this.content.scrollToPoint(0, afterContainerHeight - beforeContainerHeight)
if (logs.length < limit) {
this.needInfinite = false
}
} catch (e) {
this.errToast.present(e)
} finally {
this.loading = false
}
}
async loadData (e: any): Promise<void> {
await this.getLogs()
e.target.complete()
}
}

View File

@@ -4,6 +4,7 @@ import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { ServerLogsPage } from './server-logs.page'
import { SharingModule } from 'src/app/modules/sharing.module'
import { LogsPageModule } from 'src/app/components/logs/logs.module'
const routes: Routes = [
{
@@ -18,6 +19,7 @@ const routes: Routes = [
IonicModule,
RouterModule.forChild(routes),
SharingModule,
LogsPageModule,
],
declarations: [ServerLogsPage],
})

View File

@@ -4,24 +4,9 @@
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>Logs</ion-title>
<ion-buttons slot="end">
<ion-button (click)="getLogs()">
<ion-icon slot="start" name="refresh-outline"></ion-icon>
Refresh
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content id="ion-content" class="ion-padding">
<ion-infinite-scroll id="scroller" *ngIf="firstTimeLoaded && needInfinite" position="top" threshold="0" (ionInfinite)="loadData($event)">
<ion-infinite-scroll-content
loadingSpinner="bubbles">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
<text-spinner *ngIf="!firstTimeLoaded" text="Loading Logs"></text-spinner>
<div id="container"><div id="template" style="white-space: pre-line;"></div></div>
</ion-content>
<div style="height: 100%">
<logs [fetchLogs]="fetchFetchLogs()"></logs>
</div>

View File

@@ -1,7 +1,5 @@
import { Component, ViewChild } from '@angular/core'
import { Component } from '@angular/core'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { IonContent } from '@ionic/angular'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@Component({
selector: 'server-logs',
@@ -9,53 +7,22 @@ import { ErrorToastService } from 'src/app/services/error-toast.service'
styleUrls: ['./server-logs.page.scss'],
})
export class ServerLogsPage {
@ViewChild(IonContent, { static: false }) private content: IonContent
pkgId: string
loading = true
logs: string
needInfinite = true
firstTimeLoaded = false
before: string
constructor (
private readonly errToast: ErrorToastService,
private readonly embassyApi: ApiService,
) { }
ngOnInit () {
this.getLogs()
}
async getLogs () {
const limit = 200
try {
// get logs
const logs = await this.embassyApi.getServerLogs({ before: this.before, limit })
this.before = logs[0].timestamp
this.firstTimeLoaded = true
const container = document.getElementById('container')
const beforeContainerHeight = container.scrollHeight
const newLogs = document.getElementById('template').cloneNode(true) as HTMLElement
newLogs.innerHTML = logs.map(l => `${l.timestamp} ${l.log}`).join('\n\n') + (logs.length ? '\n\n' : '')
container.prepend(newLogs)
const afterContainerHeight = container.scrollHeight
// scroll down
scrollBy(0, afterContainerHeight - beforeContainerHeight)
this.content.scrollToPoint(0, afterContainerHeight - beforeContainerHeight)
if (logs.length < limit) {
this.needInfinite = false
}
} catch (e) {
this.errToast.present(e)
fetchFetchLogs (): Function {
return async (params: { before?: string, after?: string, limit: number }) => {
return this.embassyApi.getServerLogs({
after: params.after,
before: params.before,
limit: params.limit,
})
}
}
async loadData (e: any): Promise<void> {
await this.getLogs()
e.target.complete()
}
}

View File

@@ -27,7 +27,7 @@ export module RR {
export type SetShareStatsReq = WithExpire<{ value: any }> // server.config.share-stats
export type SetShareStatsRes = WithRevision<null>
export type GetServerLogsReq = { before?: string, limit?: number } // server.logs
export type GetServerLogsReq = { before?: string, after?: string, limit?: number } // server.logs
export type GetServerLogsRes = Log[]
export type GetServerMetricsReq = { } // server.metrics
@@ -137,7 +137,7 @@ export module RR {
export type GetPackagePropertiesReq = { id: string } // package.properties
export type GetPackagePropertiesRes<T extends number> = PackagePropertiesVersioned<T>
export type GetPackageLogsReq = { id: string, limit?: number, before?: string } // package.logs
export type GetPackageLogsReq = { id: string, limit?: number, before?: string, after?: string } // package.logs
export type GetPackageLogsRes = Log[]
export type GetPackageMetricsReq = { id: string } // package.metrics