remove share-stats, cosmetic improvements

This commit is contained in:
Matt Hill
2022-02-09 11:55:12 -07:00
committed by Keagan McClelland
parent 5326de9b84
commit 20acd8efea
17 changed files with 125 additions and 276 deletions

View File

@@ -15,9 +15,10 @@
"build:setup-wizard": "ng run setup-wizard:build",
"build:ui": "ng run ui:build && tsc projects/ui/postprocess.ts && node projects/ui/postprocess.js && git log | head -n1 > dist/ui/git-hash.txt",
"build:all": "npm run build:deps && npm run build:diagnostic-ui && npm run build:setup-wizard && npm run build:ui",
"start:diagnostic-ui": "ionic serve --project diagnostic-ui",
"start:setup-wizard": "ionic serve --project setup-wizard",
"start:ui": "ionic serve --project ui"
"start:diagnostic-ui": "npm run copy-git-hash && ionic serve --project diagnostic-ui",
"start:setup-wizard": "npm run copy-git-hash && ionic serve --project setup-wizard",
"start:ui": "npm run copy-git-hash && ionic serve --project ui",
"copy-git-hash": "./update-git-hash.sh"
},
"dependencies": {
"@angular/animations": "^13.2.0",

View File

@@ -49,7 +49,7 @@
<ion-icon
*ngIf="eosService.updateAvailable$ | async"
color="success"
name="repeat"
name="rocket-outline"
></ion-icon>
</ng-container>
<ion-badge
@@ -162,7 +162,7 @@
<ion-icon name="reload"></ion-icon>
<ion-icon name="remove"></ion-icon>
<ion-icon name="remove-circle-outline"></ion-icon>
<ion-icon name="repeat"></ion-icon>
<ion-icon name="rocket-outline"></ion-icon>
<ion-icon name="save-outline"></ion-icon>
<ion-icon name="shield-checkmark-outline"></ion-icon>
<ion-icon name="storefront-outline"></ion-icon>

View File

@@ -13,6 +13,7 @@ import {
AlertController,
IonicSafeString,
LoadingController,
ModalController,
ToastController,
} from '@ionic/angular'
import { Emver } from './services/emver.service'
@@ -24,7 +25,6 @@ import {
ConnectionFailure,
ConnectionService,
} from './services/connection.service'
import { StartupAlertsService } from './services/startup-alerts.service'
import { ConfigService } from './services/config.service'
import { debounce, isEmptyObject } from './util/misc.util'
import { ErrorToastService } from './services/error-toast.service'
@@ -32,6 +32,7 @@ import { Subscription } from 'rxjs'
import { LocalStorageService } from './services/local-storage.service'
import { EOSService } from './services/eos.service'
import { MarketplaceService } from './pages/marketplace-routes/marketplace.service'
import { OSWelcomePage } from './modals/os-welcome/os-welcome.page'
@Component({
selector: 'app-root',
@@ -41,7 +42,7 @@ import { MarketplaceService } from './pages/marketplace-routes/marketplace.servi
export class AppComponent {
@HostListener('document:keydown.enter', ['$event'])
@debounce()
handleKeyboardEvent () {
handleKeyboardEvent() {
const elems = document.getElementsByClassName('enter-click')
const elem = elems[elems.length - 1] as HTMLButtonElement
if (!elem || elem.classList.contains('no-click') || elem.disabled) return
@@ -86,7 +87,7 @@ export class AppComponent {
},
]
constructor (
constructor(
private readonly storage: Storage,
private readonly authService: AuthService,
private readonly router: Router,
@@ -95,7 +96,7 @@ export class AppComponent {
private readonly loadingCtrl: LoadingController,
private readonly emver: Emver,
private readonly connectionService: ConnectionService,
private readonly startupAlertsService: StartupAlertsService,
private readonly modalCtrl: ModalController,
private readonly marketplaceService: MarketplaceService,
private readonly toastCtrl: ToastController,
private readonly errToast: ErrorToastService,
@@ -109,7 +110,7 @@ export class AppComponent {
this.init()
}
async init () {
async init() {
await this.storage.create()
await this.authService.init()
await this.localStorageService.init()
@@ -146,6 +147,8 @@ export class AppComponent {
.subscribe(data => {
// check for updates to EOS
this.checkForEosUpdate(data.ui)
// show eos welcome message
this.showEosWelcome(data.ui['ack-welcome'])
this.subscriptions = this.subscriptions.concat([
// watch status to present toast for updated state
@@ -158,8 +161,6 @@ export class AppComponent {
this.watchNotifications(),
// watch marketplace URL for changes
this.marketplaceService.init(),
// run startup alerts
this.startupAlertsService.runChecks(),
])
})
// UNVERIFIED
@@ -180,7 +181,7 @@ export class AppComponent {
})
}
async goToWebsite (): Promise<void> {
async goToWebsite(): Promise<void> {
let url: string
if (this.config.isTor()) {
url =
@@ -191,7 +192,7 @@ export class AppComponent {
window.open(url, '_blank', 'noreferrer')
}
async presentAlertLogout () {
async presentAlertLogout() {
const alert = await this.alertCtrl.create({
header: 'Caution',
message:
@@ -214,19 +215,37 @@ export class AppComponent {
await alert.present()
}
private async checkForEosUpdate (ui: UIData): Promise<void> {
private checkForEosUpdate(ui: UIData): void {
if (ui['auto-check-updates']) {
await this.eosService.getEOS()
this.eosService.getEOS()
}
}
private async showEosWelcome(ackVersion: string): Promise<void> {
if (!this.config.skipStartupAlerts && ackVersion !== this.config.version) {
const modal = await this.modalCtrl.create({
component: OSWelcomePage,
presentingElement: await this.modalCtrl.getTop(),
componentProps: {
version: this.config.version,
},
})
modal.onWillDismiss().then(() => {
this.embassyApi
.setDbValue({ pointer: '/ack-welcome', value: this.config.version })
.catch()
})
modal.present()
}
}
// should wipe cache independant of actual BE logout
private async logout () {
private async logout() {
this.embassyApi.logout({})
this.authService.setUnverified()
}
private watchConnection (): Subscription {
private watchConnection(): Subscription {
return this.connectionService
.watchFailure$()
.pipe(distinctUntilChanged(), debounceTime(500))
@@ -259,7 +278,7 @@ export class AppComponent {
})
}
private watchRouter (): Subscription {
private watchRouter(): Subscription {
return this.router.events
.pipe(filter((e: RoutesRecognized) => !!e.urlAfterRedirects))
.subscribe(e => {
@@ -270,7 +289,7 @@ export class AppComponent {
})
}
private watchStatus (): Subscription {
private watchStatus(): Subscription {
return this.patch.watch$('server-info', 'status').subscribe(status => {
if (status === ServerStatus.Updated && !this.updateToast) {
this.presentToastUpdated()
@@ -278,7 +297,7 @@ export class AppComponent {
})
}
private watchUpdateProgress (): Subscription {
private watchUpdateProgress(): Subscription {
return this.patch
.watch$('server-info', 'update-progress')
.subscribe(progress => {
@@ -286,7 +305,7 @@ export class AppComponent {
})
}
private watchVersion (): Subscription {
private watchVersion(): Subscription {
return this.patch.watch$('server-info', 'version').subscribe(version => {
if (this.emver.compare(this.config.version, version) !== 0) {
this.presentAlertRefreshNeeded()
@@ -294,7 +313,7 @@ export class AppComponent {
})
}
private watchNotifications (): Subscription {
private watchNotifications(): Subscription {
let previous: number
return this.patch
.watch$('server-info', 'unread-notification-count')
@@ -306,7 +325,7 @@ export class AppComponent {
})
}
private async presentAlertRefreshNeeded () {
private async presentAlertRefreshNeeded() {
const alert = await this.alertCtrl.create({
backdropDismiss: false,
header: 'Refresh Needed',
@@ -325,7 +344,7 @@ export class AppComponent {
await alert.present()
}
private async presentToastUpdated () {
private async presentToastUpdated() {
if (this.updateToast) return
this.updateToast = await this.toastCtrl.create({
@@ -355,7 +374,7 @@ export class AppComponent {
await this.updateToast.present()
}
private async presentToastNotifications () {
private async presentToastNotifications() {
if (this.notificationToast) return
this.notificationToast = await this.toastCtrl.create({
@@ -385,7 +404,7 @@ export class AppComponent {
await this.notificationToast.present()
}
private async presentToastOffline (
private async presentToastOffline(
message: string | IonicSafeString,
link?: string,
) {
@@ -426,7 +445,7 @@ export class AppComponent {
await this.offlineToast.present()
}
private async restart (): Promise<void> {
private async restart(): Promise<void> {
const loader = await this.loadingCtrl.create({
spinner: 'lines',
message: 'Restarting...',
@@ -443,7 +462,7 @@ export class AppComponent {
}
}
splitPaneVisible (e: any) {
splitPaneVisible(e: any) {
this.splitPane.sidebarOpen$.next(e.detail.visible)
}
}

View File

@@ -15,16 +15,6 @@
<ion-note slot="end">{{ patch.data.ui.name || defaultName }}</ion-note>
</ion-item>
<ion-item
button
(click)="serverConfig.presentAlert('share-stats', server['share-stats'])"
>
<ion-label>Auto Report Bugs</ion-label>
<ion-note slot="end"
>{{ server['share-stats'] ? 'Enabled' : 'Disabled' }}</ion-note
>
</ion-item>
<ion-item-divider>Marketplace</ion-item-divider>
<ion-item
button

View File

@@ -28,9 +28,9 @@ export class PreferencesPage {
private readonly loadingCtrl: LoadingController,
private readonly modalCtrl: ModalController,
private readonly api: ApiService,
public readonly serverConfig: ServerConfigService,
private readonly toastCtrl: ToastController,
private readonly localStorageService: LocalStorageService,
public readonly serverConfig: ServerConfigService,
public readonly patch: PatchDbService,
) {}

View File

@@ -59,19 +59,21 @@
</span>
</ng-container>
</p>
<!-- "Software Update" button only -->
<p *ngIf="button.title === 'Software Update'">
<ng-container
*ngIf="eosService.updateAvailable$ | async; else check"
>
<ion-text class="inline" color="success">
<ion-icon name="repeat"></ion-icon>
<ion-icon name="rocket-outline"></ion-icon>
Update Available
</ion-text>
</ng-container>
<ng-template #check>
<i>Check for updates</i>
<ion-text class="inline" color="dark">
<ion-icon name="refresh"></ion-icon>
Check for updates
</ion-text>
</ng-template>
</p>
</ion-label>

View File

@@ -204,7 +204,10 @@ export class ServerShowPage {
await loader.present()
try {
await this.eosService.getEOS()
const updateAvailable = await this.eosService.getEOS()
if (updateAvailable) {
this.updateEos()
}
} catch (e) {
this.errToast.present(e)
} finally {

View File

@@ -28,9 +28,6 @@ export module RR {
// server
export type SetShareStatsReq = WithExpire<{ value: boolean }> // server.config.share-stats
export type SetShareStatsRes = WithRevision<null>
export type GetServerLogsReq = {
cursor?: string
before_flag?: boolean

View File

@@ -51,12 +51,6 @@ export abstract class ApiService implements Source<DataModel>, Http<DataModel> {
// server
protected abstract setShareStatsRaw(
params: RR.SetShareStatsReq,
): Promise<RR.SetShareStatsRes>
setShareStats = (params: RR.SetShareStatsReq) =>
this.syncResponse(() => this.setShareStatsRaw(params))()
abstract getServerLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes>

View File

@@ -57,12 +57,6 @@ export class LiveApiService extends ApiService {
// server
async setShareStatsRaw(
params: RR.SetShareStatsReq,
): Promise<RR.SetShareStatsRes> {
return this.http.rpcRequest({ method: 'server.config.share-stats', params })
}
async getServerLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes> {

View File

@@ -25,22 +25,22 @@ export class MockApiService extends ApiService {
private readonly revertTime = 4000
sequence: number
constructor (private readonly bootstrapper: LocalStorageBootstrap) {
constructor(private readonly bootstrapper: LocalStorageBootstrap) {
super()
}
async getStatic (url: string): Promise<string> {
async getStatic(url: string): Promise<string> {
await pauseFor(2000)
return markdown
}
// db
async getRevisions (since: number): Promise<RR.GetRevisionsRes> {
async getRevisions(since: number): Promise<RR.GetRevisionsRes> {
return this.getDump()
}
async getDump (): Promise<RR.GetDumpRes> {
async getDump(): Promise<RR.GetDumpRes> {
const cache = await this.bootstrapper.init()
return {
id: cache.sequence,
@@ -49,7 +49,7 @@ export class MockApiService extends ApiService {
}
}
async setDbValueRaw (params: RR.SetDBValueReq): Promise<RR.SetDBValueRes> {
async setDbValueRaw(params: RR.SetDBValueReq): Promise<RR.SetDBValueRes> {
await pauseFor(2000)
const patch = [
{
@@ -63,7 +63,7 @@ export class MockApiService extends ApiService {
// auth
async login (params: RR.LoginReq): Promise<RR.loginRes> {
async login(params: RR.LoginReq): Promise<RR.loginRes> {
await pauseFor(2000)
setTimeout(() => {
@@ -73,39 +73,24 @@ export class MockApiService extends ApiService {
return null
}
async logout (params: RR.LogoutReq): Promise<RR.LogoutRes> {
async logout(params: RR.LogoutReq): Promise<RR.LogoutRes> {
await pauseFor(2000)
return null
}
async getSessions (params: RR.GetSessionsReq): Promise<RR.GetSessionsRes> {
async getSessions(params: RR.GetSessionsReq): Promise<RR.GetSessionsRes> {
await pauseFor(2000)
return Mock.Sessions
}
async killSessions (params: RR.KillSessionsReq): Promise<RR.KillSessionsRes> {
async killSessions(params: RR.KillSessionsReq): Promise<RR.KillSessionsRes> {
await pauseFor(2000)
return null
}
// server
async setShareStatsRaw (
params: RR.SetShareStatsReq,
): Promise<RR.SetShareStatsRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/server-info/share-stats',
value: params.value,
},
]
return this.withRevision(patch)
}
async getServerLogs (
async getServerLogs(
params: RR.GetServerLogsReq,
): Promise<RR.GetServerLogsRes> {
await pauseFor(2000)
@@ -127,21 +112,21 @@ export class MockApiService extends ApiService {
}
}
async getServerMetrics (
async getServerMetrics(
params: RR.GetServerMetricsReq,
): Promise<RR.GetServerMetricsRes> {
await pauseFor(2000)
return Mock.getServerMetrics()
}
async getPkgMetrics (
async getPkgMetrics(
params: RR.GetServerMetricsReq,
): Promise<RR.GetPackageMetricsRes> {
await pauseFor(2000)
return Mock.getAppMetrics()
}
async updateServerRaw (
async updateServerRaw(
params: RR.UpdateServerReq,
): Promise<RR.UpdateServerRes> {
await pauseFor(2000)
@@ -165,21 +150,21 @@ export class MockApiService extends ApiService {
return this.withRevision(patch, 'updating')
}
async restartServer (
async restartServer(
params: RR.RestartServerReq,
): Promise<RR.RestartServerRes> {
await pauseFor(2000)
return null
}
async shutdownServer (
async shutdownServer(
params: RR.ShutdownServerReq,
): Promise<RR.ShutdownServerRes> {
await pauseFor(2000)
return null
}
async systemRebuild (
async systemRebuild(
params: RR.RestartServerReq,
): Promise<RR.RestartServerRes> {
await pauseFor(2000)
@@ -188,7 +173,7 @@ export class MockApiService extends ApiService {
// marketplace URLs
async marketplaceProxy (path: string, params: {}, url: string): Promise<any> {
async marketplaceProxy(path: string, params: {}, url: string): Promise<any> {
await pauseFor(2000)
if (path === '/package/v0/info') {
@@ -211,7 +196,7 @@ export class MockApiService extends ApiService {
}
}
async getEos (
async getEos(
params: RR.GetMarketplaceEOSReq,
): Promise<RR.GetMarketplaceEOSRes> {
await pauseFor(2000)
@@ -226,7 +211,7 @@ export class MockApiService extends ApiService {
// notification
async getNotificationsRaw (
async getNotificationsRaw(
params: RR.GetNotificationsReq,
): Promise<RR.GetNotificationsRes> {
await pauseFor(2000)
@@ -241,14 +226,14 @@ export class MockApiService extends ApiService {
return this.withRevision(patch, Mock.Notifications)
}
async deleteNotification (
async deleteNotification(
params: RR.DeleteNotificationReq,
): Promise<RR.DeleteNotificationRes> {
await pauseFor(2000)
return null
}
async deleteAllNotifications (
async deleteAllNotifications(
params: RR.DeleteAllNotificationsReq,
): Promise<RR.DeleteAllNotificationsRes> {
await pauseFor(2000)
@@ -257,60 +242,60 @@ export class MockApiService extends ApiService {
// wifi
async getWifi (params: RR.GetWifiReq): Promise<RR.GetWifiRes> {
async getWifi(params: RR.GetWifiReq): Promise<RR.GetWifiRes> {
await pauseFor(2000)
return Mock.Wifi
}
async setWifiCountry (
async setWifiCountry(
params: RR.SetWifiCountryReq,
): Promise<RR.SetWifiCountryRes> {
await pauseFor(2000)
return null
}
async addWifi (params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
async addWifi(params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
await pauseFor(2000)
return null
}
async connectWifi (params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes> {
async connectWifi(params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes> {
await pauseFor(2000)
return null
}
async deleteWifi (params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> {
async deleteWifi(params: RR.DeleteWifiReq): Promise<RR.DeleteWifiRes> {
await pauseFor(2000)
return null
}
// ssh
async getSshKeys (params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes> {
async getSshKeys(params: RR.GetSSHKeysReq): Promise<RR.GetSSHKeysRes> {
await pauseFor(2000)
return Mock.SshKeys
}
async addSshKey (params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes> {
async addSshKey(params: RR.AddSSHKeyReq): Promise<RR.AddSSHKeyRes> {
await pauseFor(2000)
return Mock.SshKey
}
async deleteSshKey (params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> {
async deleteSshKey(params: RR.DeleteSSHKeyReq): Promise<RR.DeleteSSHKeyRes> {
await pauseFor(2000)
return null
}
// backup
async getBackupTargets (
async getBackupTargets(
params: RR.GetBackupTargetsReq,
): Promise<RR.GetBackupTargetsRes> {
await pauseFor(2000)
return Mock.BackupTargets
}
async addBackupTarget (
async addBackupTarget(
params: RR.AddBackupTargetReq,
): Promise<RR.AddBackupTargetRes> {
await pauseFor(2000)
@@ -327,7 +312,7 @@ export class MockApiService extends ApiService {
}
}
async updateBackupTarget (
async updateBackupTarget(
params: RR.UpdateBackupTargetReq,
): Promise<RR.UpdateBackupTargetRes> {
await pauseFor(2000)
@@ -342,21 +327,21 @@ export class MockApiService extends ApiService {
}
}
async removeBackupTarget (
async removeBackupTarget(
params: RR.RemoveBackupTargetReq,
): Promise<RR.RemoveBackupTargetRes> {
await pauseFor(2000)
return null
}
async getBackupInfo (
async getBackupInfo(
params: RR.GetBackupInfoReq,
): Promise<RR.GetBackupInfoRes> {
await pauseFor(2000)
return Mock.BackupInfo
}
async createBackupRaw (
async createBackupRaw(
params: RR.CreateBackupReq,
): Promise<RR.CreateBackupRes> {
await pauseFor(2000)
@@ -407,14 +392,14 @@ export class MockApiService extends ApiService {
// package
async getPackageProperties (
async getPackageProperties(
params: RR.GetPackagePropertiesReq,
): Promise<RR.GetPackagePropertiesRes<2>['data']> {
await pauseFor(2000)
return parsePropertiesPermissive(Mock.PackageProperties)
}
async getPackageLogs (
async getPackageLogs(
params: RR.GetPackageLogsReq,
): Promise<RR.GetPackageLogsRes> {
await pauseFor(2000)
@@ -436,7 +421,7 @@ export class MockApiService extends ApiService {
}
}
async installPackageRaw (
async installPackageRaw(
params: RR.InstallPackageReq,
): Promise<RR.InstallPackageRes> {
await pauseFor(2000)
@@ -471,14 +456,14 @@ export class MockApiService extends ApiService {
return this.withRevision(patch)
}
async dryUpdatePackage (
async dryUpdatePackage(
params: RR.DryUpdatePackageReq,
): Promise<RR.DryUpdatePackageRes> {
await pauseFor(2000)
return {}
}
async getPackageConfig (
async getPackageConfig(
params: RR.GetPackageConfigReq,
): Promise<RR.GetPackageConfigRes> {
await pauseFor(2000)
@@ -488,14 +473,14 @@ export class MockApiService extends ApiService {
}
}
async drySetPackageConfig (
async drySetPackageConfig(
params: RR.DrySetPackageConfigReq,
): Promise<RR.DrySetPackageConfigRes> {
await pauseFor(2000)
return {}
}
async setPackageConfigRaw (
async setPackageConfigRaw(
params: RR.SetPackageConfigReq,
): Promise<RR.SetPackageConfigRes> {
await pauseFor(2000)
@@ -509,7 +494,7 @@ export class MockApiService extends ApiService {
return this.withRevision(patch)
}
async restorePackagesRaw (
async restorePackagesRaw(
params: RR.RestorePackagesReq,
): Promise<RR.RestorePackagesRes> {
await pauseFor(2000)
@@ -545,14 +530,14 @@ export class MockApiService extends ApiService {
return this.withRevision(patch)
}
async executePackageAction (
async executePackageAction(
params: RR.ExecutePackageActionReq,
): Promise<RR.ExecutePackageActionRes> {
await pauseFor(2000)
return Mock.ActionResponse
}
async startPackageRaw (
async startPackageRaw(
params: RR.StartPackageReq,
): Promise<RR.StartPackageRes> {
const path = `/package-data/${params.id}/installed/status/main`
@@ -631,7 +616,7 @@ export class MockApiService extends ApiService {
return this.withRevision(originalPatch)
}
async dryStopPackage (
async dryStopPackage(
params: RR.DryStopPackageReq,
): Promise<RR.DryStopPackageRes> {
await pauseFor(2000)
@@ -645,7 +630,7 @@ export class MockApiService extends ApiService {
}
}
async stopPackageRaw (params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
async stopPackageRaw(params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
await pauseFor(2000)
const path = `/package-data/${params.id}/installed/status/main`
@@ -676,14 +661,14 @@ export class MockApiService extends ApiService {
return this.withRevision(patch)
}
async dryUninstallPackage (
async dryUninstallPackage(
params: RR.DryUninstallPackageReq,
): Promise<RR.DryUninstallPackageRes> {
await pauseFor(2000)
return {}
}
async uninstallPackageRaw (
async uninstallPackageRaw(
params: RR.UninstallPackageReq,
): Promise<RR.UninstallPackageRes> {
await pauseFor(2000)
@@ -709,7 +694,7 @@ export class MockApiService extends ApiService {
return this.withRevision(patch)
}
async deleteRecoveredPackageRaw (
async deleteRecoveredPackageRaw(
params: RR.DeleteRecoveredPackageReq,
): Promise<RR.DeleteRecoveredPackageRes> {
await pauseFor(2000)
@@ -722,7 +707,7 @@ export class MockApiService extends ApiService {
return this.withRevision(patch)
}
async dryConfigureDependency (
async dryConfigureDependency(
params: RR.DryConfigureDependencyReq,
): Promise<RR.DryConfigureDependencyRes> {
await pauseFor(2000)
@@ -733,7 +718,7 @@ export class MockApiService extends ApiService {
}
}
private async updateProgress (
private async updateProgress(
id: string,
initialProgress: InstallProgress,
): Promise<void> {
@@ -779,7 +764,7 @@ export class MockApiService extends ApiService {
}, 1000)
}
private async updateOSProgress (size: number) {
private async updateOSProgress(size: number) {
let downloaded = 0
while (downloaded < size) {
await pauseFor(250)
@@ -829,7 +814,7 @@ export class MockApiService extends ApiService {
}, 1000)
}
private async updateMock (patch: Operation[]): Promise<void> {
private async updateMock(patch: Operation[]): Promise<void> {
if (!this.sequence) {
const { sequence } = await this.bootstrapper.init()
this.sequence = sequence
@@ -842,7 +827,7 @@ export class MockApiService extends ApiService {
this.mockPatch$.next(revision)
}
private async withRevision<T> (
private async withRevision<T>(
patch: Operation[],
response: T = null,
): Promise<WithRevision<T>> {

View File

@@ -15,7 +15,6 @@ export const mockPatchData: DataModel = {
'auto-check-updates': true,
'pkg-order': [],
'ack-welcome': '1.0.0',
'ack-share-stats': false,
marketplace: undefined,
},
'server-info': {
@@ -25,9 +24,6 @@ export const mockPatchData: DataModel = {
status: ServerStatus.Running,
'lan-address': 'https://embassy-abcdefgh.local',
'tor-address': 'http://myveryownspecialtoraddress.onion',
'eos-marketplace': 'https://beta-registry-0-3.start9labs.com',
'package-marketplace': null,
'share-stats': false,
'unread-notification-count': 4,
'password-hash':
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',

View File

@@ -18,7 +18,7 @@ export class EOSService {
private readonly patch: PatchDbService,
) {}
async getEOS(): Promise<void> {
async getEOS(): Promise<boolean> {
this.eos = await this.api.getEos({
'eos-version-compat':
this.patch.getData()['server-info']['eos-version-compat'],
@@ -29,5 +29,6 @@ export class EOSService {
this.patch.data['server-info'].version,
) === 1
this.updateAvailable$.next(updateAvailable)
return updateAvailable
}
}

View File

@@ -12,7 +12,6 @@ export interface UIData {
'auto-check-updates': boolean
'pkg-order': string[]
'ack-welcome': string // EOS version
'ack-share-stats': boolean
marketplace: UIMarketplaceData
}
@@ -33,9 +32,6 @@ export interface ServerInfo {
'lan-address': URL
'tor-address': URL
status: ServerStatus
'eos-marketplace': URL
'package-marketplace': URL | null // uses EOS marketplace if null
'share-stats': boolean
'unread-notification-count': number
'update-progress'?: {
size: number

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { AlertInput, AlertButton, IonicSafeString } from '@ionic/core'
import { AlertInput, AlertButton } from '@ionic/core'
import { ApiService } from './api/embassy-api.service'
import { ConfigSpec } from '../pkg-config/config-types'
import { AlertController, LoadingController } from '@ionic/angular'
@@ -104,9 +104,6 @@ export class ServerConfigService {
value: enabled,
})
},
'share-stats': async (enabled: boolean) => {
return this.embassyApi.setShareStats({ value: enabled })
},
}
}
@@ -115,15 +112,7 @@ export const serverConfig: ConfigSpec = {
type: 'boolean',
name: 'Auto Check for Updates',
description:
'If enabled, EmbassyOS will automatically check for updates of itself and any installed services. Updating will still require your approval and action. Updates will never be performed automatically.',
'If enabled, EmbassyOS will automatically check for updates of itself. Updating will still require your approval and action. Updates will never be performed automatically.',
default: true,
},
'share-stats': {
type: 'boolean',
name: 'Report Bugs',
description: new IonicSafeString(
`If enabled, generic error codes will be anonymously transmitted over Tor to the Start9 team. This helps us identify and fix bugs quickly. <a href="https://docs.start9.com/user-manual/general/user-preferences/report-bugs.html" target="_blank" rel="noreferrer">Read more</a> `,
) as any,
default: false,
},
}

View File

@@ -1,125 +0,0 @@
import { Injectable } from '@angular/core'
import { ModalController } from '@ionic/angular'
import { WizardBaker } from '../components/install-wizard/prebaked-wizards'
import { OSWelcomePage } from '../modals/os-welcome/os-welcome.page'
import { ConfigService } from './config.service'
import { PatchDbService } from './patch-db/patch-db.service'
import { filter, take } from 'rxjs/operators'
import { isEmptyObject } from '../util/misc.util'
import { ApiService } from './api/embassy-api.service'
import { Subscription } from 'rxjs'
import { ServerConfigService } from './server-config.service'
@Injectable({
providedIn: 'root',
})
export class StartupAlertsService {
private checks: Check<any>[]
constructor(
private readonly config: ConfigService,
private readonly modalCtrl: ModalController,
private readonly api: ApiService,
private readonly wizardBaker: WizardBaker,
private readonly patch: PatchDbService,
private readonly serverConfig: ServerConfigService,
) {
const osWelcome: Check<boolean> = {
name: 'osWelcome',
shouldRun: () => this.shouldRunOsWelcome(),
display: () => this.displayOsWelcome(),
}
const shareStats: Check<boolean> = {
name: 'shareStats',
shouldRun: () => this.shouldRunShareStats(),
display: () => this.displayShareStats(),
}
this.checks = [osWelcome, shareStats]
}
// This takes our three checks and filters down to those that should run.
// Then, the reduce fires, quickly iterating through yielding a promise (previousDisplay) to the next element
// Each promise fires more or less concurrently, so each c.check(server) is run concurrently
// Then, since we await previousDisplay before c.display(res), each promise executing gets hung awaiting the display of the previous run
runChecks(): Subscription {
return this.patch
.watch$()
.pipe(
filter(data => !isEmptyObject(data)),
take(1),
)
.subscribe(async () => {
await this.checks
.filter(c => !this.config.skipStartupAlerts && c.shouldRun())
// returning true in the below block means to continue to next modal
// returning false means to skip all subsequent modals
.reduce(async (previousDisplay, c) => {
const displayRes = await previousDisplay
if (displayRes) return c.display()
}, Promise.resolve(true))
})
}
// ** should run **
private shouldRunOsWelcome(): boolean {
return this.patch.getData().ui['ack-welcome'] !== this.config.version
}
private shouldRunShareStats(): boolean {
return !this.patch.getData().ui['ack-share-stats']
}
private shouldRunOsUpdateCheck(): boolean {
return this.patch.getData().ui['auto-check-updates']
}
// ** display **
private async displayOsWelcome(): Promise<boolean> {
return new Promise(async resolve => {
const modal = await this.modalCtrl.create({
component: OSWelcomePage,
presentingElement: await this.modalCtrl.getTop(),
componentProps: {
version: this.config.version,
},
})
modal.onWillDismiss().then(() => {
this.api
.setDbValue({ pointer: '/ack-welcome', value: this.config.version })
.catch()
return resolve(true)
})
await modal.present()
})
}
private async displayShareStats(): Promise<boolean> {
return new Promise(async resolve => {
const alert = await this.serverConfig.presentAlert(
'share-stats',
this.patch.getData()['server-info']['share-stats'],
)
alert.onDidDismiss().then(() => {
this.api
.setDbValue({
pointer: '/ack-share-stats',
value: this.config.version,
})
.catch()
return resolve(true)
})
})
}
}
type Check<T> = {
// validates whether a check should run based on server properties
shouldRun: () => boolean
// display an alert based on the result of the check.
// return false if subsequent modals should not be displayed
display: () => Promise<boolean>
// for logging purposes
name: string
}

7
frontend/update-git-hash.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
cd "$(dirname "$0")"
TMP_FILE=$(mktemp)
jq ".gitHash = \"$(git rev-parse HEAD)\"" config.json > $TMP_FILE && mv $TMP_FILE config.json