diff --git a/.eslintrc.json b/.eslintrc.json
index 0c3f077cf..6eb370fcb 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -24,14 +24,23 @@
"plugin:@typescript-eslint/recommended"
],
"rules": {
+ "@typescript-eslint/semi":"error",
"@typescript-eslint/no-namespace": "warn",
"@typescript-eslint/no-empty-function": "warn",
"@typescript-eslint/no-useless-constructor": "off",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/quotes": ["error", "single"],
"@typescript-eslint/no-unused-expressions": "error",
- "@typescript-eslint/member-ordering": "warn",
+ "@typescript-eslint/member-ordering": "off",
"@typescript-eslint/no-shadow": "error",
+ "@typescript-eslint/no-unused-vars": [
+ "warn",
+ {
+ "argsIgnorePattern": "^_",
+ "varsIgnorePattern": "^_",
+ "caughtErrorsIgnorePattern": "^_"
+ }
+ ],
"@angular-eslint/component-selector": [
"error",
{
diff --git a/package-lock.json b/package-lock.json
index 673ae650c..73cf4479b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -44,7 +44,6 @@
"otpauth": "^9.1.1",
"rrule": "^2.7.2",
"rxjs": "^7.8.0",
- "rxjs-compat": "^6.6.7",
"tinymce": "^6.8.3",
"ts-md5": "^1.3.1",
"zone.js": "^0.13.3"
@@ -20500,12 +20499,6 @@
"tslib": "^2.1.0"
}
},
- "node_modules/rxjs-compat": {
- "version": "6.6.7",
- "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.6.7.tgz",
- "integrity": "sha512-szN4fK+TqBPOFBcBcsR0g2cmTTUF/vaFEOZNuSdfU8/pGFnNmmn2u8SystYXG1QMrjOPBc6XTKHMVfENDf6hHw==",
- "license": "Apache-2.0"
- },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
diff --git a/package.json b/package.json
index 04a3a6d52..5a2cf1c08 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,6 @@
"otpauth": "^9.1.1",
"rrule": "^2.7.2",
"rxjs": "^7.8.0",
- "rxjs-compat": "^6.6.7",
"tinymce": "^6.8.3",
"ts-md5": "^1.3.1",
"zone.js": "^0.13.3"
diff --git a/src/app/account-app/account-receipt.component.ts b/src/app/account-app/account-receipt.component.ts
index 0fd2b8451..0449436a2 100644
--- a/src/app/account-app/account-receipt.component.ts
+++ b/src/app/account-app/account-receipt.component.ts
@@ -20,7 +20,7 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { RunboxMe, RunboxWebmailAPI } from '../rmmapi/rbwebmail';
-import { AsyncSubject } from 'rxjs';
+import { AsyncSubject, firstValueFrom } from 'rxjs';
import { take } from 'rxjs/operators';
@Component({
@@ -45,12 +45,12 @@ export class AccountReceiptComponent implements OnInit {
}
async ngOnInit() {
- this.me = await this.rmmapi.me.toPromise();
+ this.me = await firstValueFrom(this.rmmapi.me);
- const params = await this.route.params.pipe(take(1)).toPromise();
+ const params = await firstValueFrom(this.route.params.pipe(take(1)));
const receiptID = params.id;
- this.receipt = await this.rmmapi.getReceipt(receiptID).toPromise();
+ this.receipt = await firstValueFrom(this.rmmapi.getReceipt(receiptID));
this.receipt.time = this.receipt.time.replace('T', ' ');
if (this.receipt.method === 'giro') {
diff --git a/src/app/account-app/account-renewals.component.ts b/src/app/account-app/account-renewals.component.ts
index d3d0110ea..cad14cff2 100644
--- a/src/app/account-app/account-renewals.component.ts
+++ b/src/app/account-app/account-renewals.component.ts
@@ -17,7 +17,7 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { MatLegacySnackBar as MatSnackBar } from '@angular/material/legacy-snack-bar';
@@ -232,7 +232,7 @@ Warning: You are close to your quotas for this product
`,
})
-export class AccountRenewalsRenewNowButtonComponent {
+export class AccountRenewalsRenewNowButtonComponent implements OnInit {
@Input() p: ActiveProduct;
@Input() usage: DataUsageInterface;
@Output() clicked: EventEmitter = new EventEmitter();
diff --git a/src/app/account-app/cart.service.spec.ts b/src/app/account-app/cart.service.spec.ts
index 1389d2699..fe705f4fb 100644
--- a/src/app/account-app/cart.service.spec.ts
+++ b/src/app/account-app/cart.service.spec.ts
@@ -21,7 +21,7 @@ import { CartService } from './cart.service';
import { ProductOrder } from './product-order';
import { StorageService } from '../storage.service';
import { RunboxWebmailAPI } from '../rmmapi/rbwebmail';
-import { of } from 'rxjs';
+import { firstValueFrom, of } from 'rxjs';
import { take } from 'rxjs/operators';
import { Decimal } from 'decimal.js-light';
@@ -49,7 +49,7 @@ describe('CartService', () => {
const order = new ProductOrder(401,'subscription', new Decimal(3), 402);
await cart.add(order);
- console.log(await cart.items.pipe(take(1)).toPromise());
+ console.log(await firstValueFrom(cart.items));
expect(await cart.contains(401, 402)).toBe(true, 'cart contains the renewal product');
expect(await cart.contains(401)).toBe(false, 'cart does not contain the new product');
});
diff --git a/src/app/account-app/cart.service.ts b/src/app/account-app/cart.service.ts
index afd1c2f23..8230390ff 100644
--- a/src/app/account-app/cart.service.ts
+++ b/src/app/account-app/cart.service.ts
@@ -18,7 +18,7 @@
// ---------- END RUNBOX LICENSE ----------
import { Injectable } from '@angular/core';
-import { ReplaySubject } from 'rxjs';
+import { firstValueFrom, ReplaySubject } from 'rxjs';
import { take } from 'rxjs/operators';
import { ProductOrder } from './product-order';
@@ -46,7 +46,7 @@ export class CartService {
}
async add(p: ProductOrder): Promise {
- const items = await this.items.pipe(take(1)).toPromise();
+ const items = await firstValueFrom(this.items.pipe(take(1)));
for (const i of items) {
// Cannot order multiples of subscription products
@@ -70,7 +70,7 @@ export class CartService {
}
async contains(pid: number, apid?: number): Promise {
- const items = await this.items.pipe(take(1)).toPromise();
+ const items = await firstValueFrom(this.items.pipe(take(1)));
for (const p of items) {
if (p.pid === pid && p.apid === apid) {
return true;
@@ -80,7 +80,7 @@ export class CartService {
}
async remove(order: ProductOrder): Promise {
- const items = await this.items.pipe(take(1)).toPromise();
+ const items = await firstValueFrom(this.items.pipe(take(1)));
// check if it's enough to just reduce the quantity on existing product
for (const i of items) {
if (i.isSameProduct(order)) {
diff --git a/src/app/account-app/credit-cards/stripe-add-card-dialog.component.ts b/src/app/account-app/credit-cards/stripe-add-card-dialog.component.ts
index 1cd98ee22..fbe56a54c 100644
--- a/src/app/account-app/credit-cards/stripe-add-card-dialog.component.ts
+++ b/src/app/account-app/credit-cards/stripe-add-card-dialog.component.ts
@@ -64,8 +64,8 @@ export class StripeAddCardDialogComponent implements AfterViewInit {
}
async ngAfterViewInit() {
- await stripeLoader.toPromise();
- const stripePubkey = await this.paymentsservice.stripePubkey.toPromise();
+ await firstValueFrom(stripeLoader);
+ const stripePubkey = await firstValueFrom(this.paymentsservice.stripePubkey);
const customerSession = await firstValueFrom(this.paymentsservice.customerSession());
this.stripe = Stripe(stripePubkey);
diff --git a/src/app/account-app/no-products-for-subaccounts.guard.ts b/src/app/account-app/no-products-for-subaccounts.guard.ts
index 0e54f849c..d155f854f 100644
--- a/src/app/account-app/no-products-for-subaccounts.guard.ts
+++ b/src/app/account-app/no-products-for-subaccounts.guard.ts
@@ -19,7 +19,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
-import { Observable } from 'rxjs';
+import { firstValueFrom, Observable } from 'rxjs';
import { RunboxWebmailAPI } from '../rmmapi/rbwebmail';
import { RMMAuthGuardService } from '../rmmapi/rmmauthguard.service';
@@ -54,7 +54,7 @@ export class NoProductsForSubaccountsGuard {
(success) => {
if (typeof(success) === 'boolean' && success) {
if (restricted) {
- return this.rmmapi.me.toPromise().then(me => {
+ return firstValueFrom(this.rmmapi.me).then(me => {
if (me.owner) {
return this.router.parseUrl('/account/not-for-subaccounts');
} else {
diff --git a/src/app/account-app/shopping-cart.component.ts b/src/app/account-app/shopping-cart.component.ts
index 7349cb8fe..a31b8c06e 100644
--- a/src/app/account-app/shopping-cart.component.ts
+++ b/src/app/account-app/shopping-cart.component.ts
@@ -20,7 +20,7 @@
import { Component, OnInit } from '@angular/core';
import { MatDialog as MatDialog, MatDialogRef as MatDialogRef } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
-import { Subject } from 'rxjs';
+import { firstValueFrom, Subject } from 'rxjs';
import { CartService } from './cart.service';
import { BitpayPaymentDialogComponent } from './bitpay-payment-dialog.component';
@@ -177,7 +177,7 @@ export class ShoppingCartComponent implements OnInit {
// maybe there is a more elegant way to do this, but it's concise and works :)
const cartItems = JSON.parse(JSON.stringify(items));
- let products = await this.paymentsservice.products.toPromise();
+ let products = await firstValueFrom(this.paymentsservice.products);
// Check if all the products in the cart had their details in the paymentservice.
// This may not be true if they're coming from the URL for instance,
@@ -192,7 +192,7 @@ export class ShoppingCartComponent implements OnInit {
}
const neededPids = Array.from(neededPidsSet.values());
if (neededPids.length > 0) {
- const extras = await this.rmmapi.getProducts(neededPids).toPromise();
+ const extras = await firstValueFrom(this.rmmapi.getProducts(neededPids));
if (extras.length !== neededPids.length) {
console.warn(`Failed to load products ${neededPids.join(',')} (got: ${JSON.stringify(extras)})`);
}
@@ -213,7 +213,7 @@ export class ShoppingCartComponent implements OnInit {
}
async checkIfLegal(items: CartItem[]) {
- const me = await this.rmmapi.me.toPromise();
+ const me = await firstValueFrom(this.rmmapi.me);
this.orderError = undefined; // unless we find something else :)
diff --git a/src/app/account-app/stripe-payment-dialog.component.ts b/src/app/account-app/stripe-payment-dialog.component.ts
index 9046f46fa..75bbd3c69 100644
--- a/src/app/account-app/stripe-payment-dialog.component.ts
+++ b/src/app/account-app/stripe-payment-dialog.component.ts
@@ -87,7 +87,7 @@ export class StripePaymentDialogComponent implements AfterViewInit {
}
async ngAfterViewInit() {
- await stripeLoader.toPromise();
+ await firstValueFrom(stripeLoader);
const stripePubkey = await firstValueFrom(this.paymentsservice.stripePubkey);
const customerSession = await firstValueFrom(this.paymentsservice.customerSession());
@@ -173,7 +173,7 @@ export class StripePaymentDialogComponent implements AfterViewInit {
this.state = 'failure';
return;
}
-
+
}
handleConfirmationToken(cId: string) {
diff --git a/src/app/account-details/account-settings.component.ts b/src/app/account-details/account-settings.component.ts
index 7097ecd60..422e837cc 100644
--- a/src/app/account-details/account-settings.component.ts
+++ b/src/app/account-details/account-settings.component.ts
@@ -17,7 +17,7 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
import { AsyncSubject } from 'rxjs';
import { share, timeout } from 'rxjs/operators';
import { RMM } from '../rmm';
@@ -28,7 +28,7 @@ import { AccountSettingsInterface } from '../rmm/account-settings';
templateUrl: './account-settings.component.html',
styleUrls: ['account-details.component.scss'],
})
-export class AccountSettingsComponent {
+export class AccountSettingsComponent implements OnInit {
displayedColumns: string[] = ['description', 'status'];
settings = new AsyncSubject();
settingsArray = [];
diff --git a/src/app/account-details/personal-details.component.ts b/src/app/account-details/personal-details.component.ts
index 24b2b4a94..75416d191 100644
--- a/src/app/account-details/personal-details.component.ts
+++ b/src/app/account-details/personal-details.component.ts
@@ -18,10 +18,10 @@
// ---------- END RUNBOX LICENSE ----------
import { HttpClient, HttpResponse } from '@angular/common/http';
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
-import { Subject } from 'rxjs';
+import { firstValueFrom, Subject } from 'rxjs';
import { RMM } from '../rmm';
import { map } from 'rxjs/operators';
import { AccountDetailsInterface } from '../rmm/account-details';
@@ -39,7 +39,7 @@ interface CountryAndTimezone {
templateUrl: './personal-details.component.html',
styleUrls: ['account-details.component.scss'],
})
-export class PersonalDetailsComponent {
+export class PersonalDetailsComponent implements OnInit {
hide = true;
myControl = new UntypedFormControl();
countriesAndTimezones: CountryAndTimezone[] = [];
@@ -109,10 +109,9 @@ export class PersonalDetailsComponent {
}
loadTimezones() {
- this.http
+ firstValueFrom(this.http
.get('/rest/v1/timezones')
- .pipe(map((res: HttpResponse) => res['result']))
- .toPromise()
+ .pipe(map((res: HttpResponse) => res['result'])))
.then((data) => this.timezones = data.timezones);
}
@@ -126,10 +125,9 @@ export class PersonalDetailsComponent {
}
private loadSelectFields() {
- this.http
+ firstValueFrom(this.http
.get('/rest/v1/account/details')
- .pipe(map((res: HttpResponse) => res['result']))
- .toPromise()
+ .pipe(map((res: HttpResponse) => res['result'])))
.then((data) => {
this.selectedCountry = data.country;
this.selectedTimezone = data.timezone;
diff --git a/src/app/account-details/storage-data.component.ts b/src/app/account-details/storage-data.component.ts
index 5dfb34dfb..abcad5a5c 100644
--- a/src/app/account-details/storage-data.component.ts
+++ b/src/app/account-details/storage-data.component.ts
@@ -17,7 +17,7 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
import { AsyncSubject } from 'rxjs';
import { RMM } from '../rmm';
@@ -34,7 +34,7 @@ export interface DataUsage {
templateUrl: './storage-data.component.html',
styleUrls: ['account-details.component.scss'],
})
-export class StorageDataComponent {
+export class StorageDataComponent implements OnInit {
dataUsage = new AsyncSubject();
displayedColumns: string[] = ['type', 'quota', 'usage', 'percentage_used'];
diff --git a/src/app/account-security/sessions.component.ts b/src/app/account-security/sessions.component.ts
index ccc26f8c0..a95d29f94 100644
--- a/src/app/account-security/sessions.component.ts
+++ b/src/app/account-security/sessions.component.ts
@@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import { Component, Output, EventEmitter, ViewChild } from '@angular/core';
+import { Component, Output, EventEmitter, ViewChild, OnInit } from '@angular/core';
import { MatLegacyPaginator as MatPaginator } from '@angular/material/legacy-paginator';
import { MatLegacySnackBar as MatSnackBar } from '@angular/material/legacy-snack-bar';
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
@@ -28,7 +28,7 @@ import { RMM } from '../rmm';
styleUrls: ['account.security.component.scss'],
templateUrl: 'sessions.component.html',
})
-export class SessionsComponent {
+export class SessionsComponent implements OnInit {
panelOpenState = false;
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator;
@Output() Close: EventEmitter = new EventEmitter();
diff --git a/src/app/aliases/aliases.lister.spec.ts b/src/app/aliases/aliases.lister.spec.ts
index 6effe5bf4..8a3ffb893 100644
--- a/src/app/aliases/aliases.lister.spec.ts
+++ b/src/app/aliases/aliases.lister.spec.ts
@@ -70,7 +70,7 @@ describe('AliasesListerComponent', () => {
return of({
status: 'error',
result: 'unimplemented'
- })
+ });
}
}
} },
@@ -94,11 +94,11 @@ describe('AliasesListerComponent', () => {
});
fixture = TestBed.createComponent(AliasesListerComponent);
component = fixture.componentInstance;
- })
+ });
it('loads aliases through RMM', () => {
expect(component.aliases).toEqual(ALIASES);
- })
+ });
it('sets the default email to current user email', () => {
expect(component.defaultEmail).toBe(DEFAULT_EMAIL);
diff --git a/src/app/aliases/aliases.lister.ts b/src/app/aliases/aliases.lister.ts
index a8781079d..847a1fe0e 100644
--- a/src/app/aliases/aliases.lister.ts
+++ b/src/app/aliases/aliases.lister.ts
@@ -58,7 +58,7 @@ export class AliasesListerComponent {
if (result !== undefined) {
this.aliases.push(result);
}
- })
+ });
}
edit (item: object) {
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index b5a152f8f..055e4c6ae 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -47,7 +47,7 @@ import { map, take, skip, mergeMap, filter, tap, throttleTime, debounceTime, dis
import { WebSocketSearchService } from './websocketsearch/websocketsearch.service';
import { WebSocketSearchMailList } from './websocketsearch/websocketsearchmaillist';
-import { from, Observable } from 'rxjs';
+import { from, Observable, lastValueFrom } from 'rxjs';
import { xapianLoadedSubject } from './xapian/xapianwebloader';
import { SwPush } from '@angular/service-worker';
import { exportKeysFromJWK } from './webpush/vapid.tools';
@@ -396,7 +396,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis
&& prev.every((f, index) =>
objectEqualWithKeys(f, curr[index], [
'folderId', 'totalMessages', 'newMessages', 'folderName'
- ]))
+ ]));
}))
.pipe(map((folders: FolderListEntry[]) => folders.filter(f => f.folderPath.indexOf('Drafts') !== 0))
);
@@ -616,11 +616,11 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis
async emptySpam(spamFolderName) {
console.log('found spam folder with name', spamFolderName);
- const messageLists = await this.rmmapi.listAllMessages(
+ const messageLists = await lastValueFrom(this.rmmapi.listAllMessages(
0, 0, 0,
RunboxWebmailAPI.LIST_ALL_MESSAGES_CHUNK_SIZE,
true, spamFolderName
- ).toPromise();
+ ));
const messageIds = messageLists.map(msg => msg.id);
this.messageActionsHandler.updateMessages({
@@ -929,7 +929,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis
}
public rowSelected(rowIndex: number, columnIndex: number, multiSelect?: boolean) {
- const isSelect = (columnIndex === 0) || multiSelect
+ const isSelect = (columnIndex === 0) || multiSelect;
if ((this.selectedFolder === this.messagelistservice.templateFolderName) && !isSelect) {
this.draftDeskService.newTemplateDraft(
@@ -1452,14 +1452,14 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis
}
async enableNotification() {
- console.log('request notification permission')
+ console.log('request notification permission');
try {
- const result = await Notification.requestPermission()
- console.log('Notification.permission', Notification.permission)
+ const result = await Notification.requestPermission();
+ console.log('Notification.permission', Notification.permission);
if (result === 'granted') this.subscribeToNotifications();
} catch (error) {
- console.error(error)
+ console.error(error);
}
}
}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index bced66edb..72427892d 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -202,7 +202,7 @@ const routes: Routes = [
{ provide: HTTP_INTERCEPTORS, useClass: RMMHttpInterceptorService, multi: true },
{ provide: ErrorHandler, useClass: SentryErrorHandler },
{ provide: SwRegistrationOptions,
- useFactory: () => ({ registrationStrategy: 'registerWithDelay:5000' }) }
+ useFactory: () => ({ registrationStrategy: 'registerWhenStable:30000' }) }
],
bootstrap: [MainContainerComponent]
})
diff --git a/src/app/changelog/changelog.component.scss b/src/app/changelog/changelog.component.scss
index 33643b198..ac11df38c 100644
--- a/src/app/changelog/changelog.component.scss
+++ b/src/app/changelog/changelog.component.scss
@@ -6,7 +6,8 @@ h4 {
margin: 0.75em 0;
}
-.mat-list-base[dense] .mat-list-item, .mat-list-base[dense] .mat-list-option {
+.mat-list-base[dense] .mat-list-item,
+.mat-list-base[dense] .mat-list-option {
height: 30px;
}
diff --git a/src/app/common/preferences.service.ts b/src/app/common/preferences.service.ts
index 7ed303938..b1a358a11 100644
--- a/src/app/common/preferences.service.ts
+++ b/src/app/common/preferences.service.ts
@@ -18,7 +18,7 @@
// ---------- END RUNBOX LICENSE ----------
import { Injectable } from '@angular/core';
-import { ReplaySubject } from 'rxjs';
+import { firstValueFrom, ReplaySubject } from 'rxjs';
import { take } from 'rxjs/operators';
import { StorageService } from '../storage.service';
@@ -151,7 +151,7 @@ export class PreferencesService {
};
}
- const allPrefs = await this.preferences.pipe(take(1)).toPromise();
+ const allPrefs = await firstValueFrom(this.preferences.pipe(take(1)));
Object.keys(prefsdata[DefaultPrefGroups.Global]['entries']).forEach((key) => {
allPrefs.set(`${DefaultPrefGroups.Global}:${key}`, prefsdata[DefaultPrefGroups.Global]['entries'][key]);
});
@@ -179,7 +179,7 @@ export class PreferencesService {
}
private async uploadPreferenceData(level: string) {
- const prefs = await this.preferences.pipe(take(1)).toPromise();
+ const prefs = await firstValueFrom(this.preferences.pipe(take(1)));
const entriesObj = {};
prefs.forEach((value, key) => {
// for (const [key, value] of prefs) {
@@ -219,7 +219,7 @@ export class PreferencesService {
if (this.loadedOldStyle) {
return;
}
- let prefs = await this.preferences.pipe(take(1)).toPromise();
+ let prefs = await firstValueFrom(this.preferences.pipe(take(1)));
if (!prefs) {
// Already set / imported
prefs = new Map();
@@ -235,7 +235,7 @@ export class PreferencesService {
const showImagesDecisionKey = 'rmm7showimagesdecision';
const resizerPercentageKey = 'rmm7resizerpercentage';
- const uid = await this.storage.uid.toPromise();
+ const uid = await firstValueFrom(this.storage.uid);
if (level === DefaultPrefGroups.Global) {
if (localStorage.getItem('rmm7experimentalFeatureEnabled') !== null) {
diff --git a/src/app/common/util.spec.ts b/src/app/common/util.spec.ts
index b50d532ea..3fa34121f 100644
--- a/src/app/common/util.spec.ts
+++ b/src/app/common/util.spec.ts
@@ -104,53 +104,53 @@ describe('withKeys', () => {
});
it('can fetch nested items', () => {
- expect(withKeys(o, ['c'])).toEqual({c: [3, 4]})
+ expect(withKeys(o, ['c'])).toEqual({c: [3, 4]});
});
it('returns an empty object if all keys dont exist', () => {
- expect(withKeys(o, ['x', 'y', 'z'])).toEqual({})
+ expect(withKeys(o, ['x', 'y', 'z'])).toEqual({});
});
it('returns an empty object if no keys are provided', () => {
expect(withKeys(o, [])).toEqual({});
});
-})
+});
describe('objectEqualWithKeys', () => {
const o = {a: 1, b: 2, c: [3, 4], d: {e: 5}};
// since they are both empty objects
it('matches when no keys are specified', () => {
- expect(objectEqualWithKeys(o, o, [])).toBeTrue()
- expect(objectEqualWithKeys(o, {}, [])).toBeTrue()
- expect(objectEqualWithKeys({}, {}, [])).toBeTrue()
+ expect(objectEqualWithKeys(o, o, [])).toBeTrue();
+ expect(objectEqualWithKeys(o, {}, [])).toBeTrue();
+ expect(objectEqualWithKeys({}, {}, [])).toBeTrue();
});
it('matches when provided keys that exist', () => {
- expect(objectEqualWithKeys(o, o, ['a', 'b'])).toBeTrue()
- expect(objectEqualWithKeys(o, o, ['a', 'b', 'c', 'd'])).toBeTrue()
+ expect(objectEqualWithKeys(o, o, ['a', 'b'])).toBeTrue();
+ expect(objectEqualWithKeys(o, o, ['a', 'b', 'c', 'd'])).toBeTrue();
});
// again, they are both empty objects
it('matches when provided keys that dont exist', () => {
- expect(objectEqualWithKeys(o, o, ['f', 'g', 'h'])).toBeTrue()
+ expect(objectEqualWithKeys(o, o, ['f', 'g', 'h'])).toBeTrue();
});
it('matches only by keys, ignoring other values', () => {
- expect(objectEqualWithKeys(o, {a: 1, b: 2, c: 32}, ['a', 'b'])).toBeTrue()
+ expect(objectEqualWithKeys(o, {a: 1, b: 2, c: 32}, ['a', 'b'])).toBeTrue();
});
it('matches nested objects and arrays', () => {
- expect(objectEqualWithKeys(o, {a: 25, c: [3, 4], d: {e: 5}}, ['c', 'd'])).toBeTrue()
+ expect(objectEqualWithKeys(o, {a: 25, c: [3, 4], d: {e: 5}}, ['c', 'd'])).toBeTrue();
});
it('fails to match if values are different', () => {
- expect(objectEqualWithKeys(o, {a: 222, b: 123}, ['a', 'b'])).toBeFalse()
- expect(objectEqualWithKeys(o, {c: [4, 5]}, ['c'])).toBeFalse()
- expect(objectEqualWithKeys(o, {d: {e: 6}}, ['d'])).toBeFalse()
+ expect(objectEqualWithKeys(o, {a: 222, b: 123}, ['a', 'b'])).toBeFalse();
+ expect(objectEqualWithKeys(o, {c: [4, 5]}, ['c'])).toBeFalse();
+ expect(objectEqualWithKeys(o, {d: {e: 6}}, ['d'])).toBeFalse();
});
it('fails to match if comparison object is empty', () => {
- expect(objectEqualWithKeys(o, {}, ['a', 'b'])).toBeFalse()
+ expect(objectEqualWithKeys(o, {}, ['a', 'b'])).toBeFalse();
});
});
\ No newline at end of file
diff --git a/src/app/common/util.ts b/src/app/common/util.ts
index fd94fe97f..3c8f045ff 100644
--- a/src/app/common/util.ts
+++ b/src/app/common/util.ts
@@ -73,7 +73,7 @@ export function withKeys(o: object, keys: any[]): object {
return keys.reduce((acc, k) => {
if (k in o) { acc[k] = o[k]; }
return acc;
- }, {})
+ }, {});
}
/**
diff --git a/src/app/compose/compose.component.ts b/src/app/compose/compose.component.ts
index 7d52848c8..e1aa9ecf2 100644
--- a/src/app/compose/compose.component.ts
+++ b/src/app/compose/compose.component.ts
@@ -46,7 +46,7 @@ declare const MailParser;
const LOCAL_STORAGE_SHOW_POPULAR_RECIPIENTS = 'showPopularRecipients';
const LOCAL_STORAGE_DEFAULT_HTML_COMPOSE = 'composeInHTMLByDefault';
-const DOWNLOAD_DRAFT_URL = '/ajax/download_draft_attachment?filename='
+const DOWNLOAD_DRAFT_URL = '/ajax/download_draft_attachment?filename=';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
@@ -216,7 +216,7 @@ export class ComposeComponent implements AfterViewInit, OnDestroy, OnInit {
// have autosave until it was saved as a draft.
if (this.model.tid) return;
- this.submit(false)
+ this.submit(false);
});
this.formGroup.controls.from.valueChanges
@@ -471,8 +471,8 @@ export class ComposeComponent implements AfterViewInit, OnDestroy, OnInit {
public loadDraft(msgObj) {
if (msgObj.errors) {
- this.snackBar.open(msgObj.errors[0], 'Ok')
- throw msgObj
+ this.snackBar.open(msgObj.errors[0], 'Ok');
+ throw msgObj;
}
const model = new DraftFormModel();
@@ -869,7 +869,7 @@ export class ComposeComponent implements AfterViewInit, OnDestroy, OnInit {
}
public saveTemplate() {
- this.isTemplate = true
+ this.isTemplate = true;
this.submit(false);
}
diff --git a/src/app/compose/draftdesk.service.ts b/src/app/compose/draftdesk.service.ts
index 0c1d02731..82e6f452b 100644
--- a/src/app/compose/draftdesk.service.ts
+++ b/src/app/compose/draftdesk.service.ts
@@ -26,7 +26,7 @@ import { MailAddressInfo } from '../common/mailaddressinfo';
import { MessageListService } from '../rmmapi/messagelist.service';
import { MessageTableRowTool} from '../messagetable/messagetablerow';
import { Identity, ProfileService } from '../profiles/profile.service';
-import { from, of, BehaviorSubject } from 'rxjs';
+import { from, of, BehaviorSubject, firstValueFrom } from 'rxjs';
import { map, mergeMap, bufferCount, take, distinctUntilChanged } from 'rxjs/operators';
import moment from 'moment';
@@ -264,7 +264,7 @@ export class DraftDeskService {
&& prev.every((f, index) =>
objectEqualWithKeys(f, curr[index], [
'folderId', 'totalMessages', 'newMessages'
- ]))
+ ]));
}))
.subscribe((folders) => {
this.refreshDrafts();
@@ -317,8 +317,8 @@ export class DraftDeskService {
this.rmmapi.getMessageContents(messageId).subscribe((contents) => {
const res: any = Object.assign({}, contents);
- const {subject} = res.headers
- let { to } = res.headers
+ const {subject} = res.headers;
+ let { to } = res.headers;
if (to) {
to = new MailAddressInfo(to.value.name, to.value.address).nameAndAddress;
@@ -329,14 +329,14 @@ export class DraftDeskService {
this.mainIdentity(),
to,
subject
- )
+ );
draftFormModel.tid = messageId;
draftFormModel.msg_body = contents.text.text;
draftFormModel.html = contents.text.html;
return this.newDraft(draftFormModel);
- })
+ });
}
public async newBugReport(
@@ -353,9 +353,9 @@ export class DraftDeskService {
'"Runbox 7 Bug Reports" ',
'Runbox 7 Bug Report'
);
- const template = await this.http.get('assets/templates/bug_report.txt',
- {responseType: 'text'}).toPromise();
- const me = await this.rmmapi.me.toPromise();
+ const template = await firstValueFrom(this.http.get('assets/templates/bug_report.txt',
+ {responseType: 'text'}));
+ const me = await firstValueFrom(this.rmmapi.me);
let body = `${template}`
;
@@ -382,8 +382,8 @@ export class DraftDeskService {
}
public async newVideoCallInvite(to: string, url: URL) {
- const template = await this.http.get('assets/templates/video_call.txt',
- {responseType: 'text'}).toPromise();
+ const template = await firstValueFrom(this.http.get('assets/templates/video_call.txt',
+ {responseType: 'text'}));
const draftObj = DraftFormModel.create(
-1,
this.mainIdentity(),
diff --git a/src/app/compose/recipients.service.spec.ts b/src/app/compose/recipients.service.spec.ts
index ab2c353ba..cace40396 100644
--- a/src/app/compose/recipients.service.spec.ts
+++ b/src/app/compose/recipients.service.spec.ts
@@ -1,18 +1,18 @@
// --------- BEGIN RUNBOX LICENSE ---------
// Copyright (C) 2016-2020 Runbox Solutions AS (runbox.com).
-//
+//
// This file is part of Runbox 7.
-//
+//
// Runbox 7 is free software: You can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
-//
+//
// Runbox 7 is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
-//
+//
// You should have received a copy of the GNU General Public License
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
@@ -22,7 +22,7 @@ import { RecipientsService } from './recipients.service';
import { ContactsService } from '../contacts-app/contacts.service';
import { SearchService, XAPIAN_GLASS_WR } from '../xapian/searchservice';
import { StorageService } from '../storage.service';
-import { AsyncSubject, of, Subject } from 'rxjs';
+import { AsyncSubject, of, Subject, firstValueFrom, lastValueFrom } from 'rxjs';
import { take } from 'rxjs/operators';
import { Contact } from '../contacts-app/contact';
import { RunboxWebmailAPI } from '../rmmapi/rbwebmail';
@@ -109,7 +109,7 @@ describe('RecipientsService', () => {
const recipientsService = TestBed.inject(RecipientsService);
// Take 2 as searchindex+contacts service are separate updates
- const recipients = await recipientsService.recipients.pipe(take(2)).toPromise();
+ const recipients = await lastValueFrom(recipientsService.recipients.pipe(take(2)));
console.log(recipients);
expect(window['termlistresult'].length).toBe(5);
@@ -136,7 +136,7 @@ describe('RecipientsService', () => {
};
const recipientsService: RecipientsService = TestBed.inject(RecipientsService);
- const suggested = await recipientsService.recentlyUsed.pipe(take(1)).toPromise();
+ const suggested = await firstValueFrom(recipientsService.recentlyUsed);
expect(suggested.length).toBe(2);
});
diff --git a/src/app/compose/recipients.service.ts b/src/app/compose/recipients.service.ts
index 436e34aad..f0886a1cf 100644
--- a/src/app/compose/recipients.service.ts
+++ b/src/app/compose/recipients.service.ts
@@ -120,8 +120,8 @@ export class RecipientsService {
).then((updateGroups) => {
this.updateRecipients(updateGroups);
}).catch((error) => {
- console.error(error)
- return this.recipients.next([])
+ console.error(error);
+ return this.recipients.next([]);
});
});
@@ -198,8 +198,8 @@ export class RecipientsService {
}
}
).catch((error) => {
- console.error(error)
- return null
+ console.error(error);
+ return null;
})
);
} else if (m.email) {
diff --git a/src/app/contacts-app/contact-details/contact-details.component.ts b/src/app/contacts-app/contact-details/contact-details.component.ts
index b342d67d1..f3621b0b7 100644
--- a/src/app/contacts-app/contact-details/contact-details.component.ts
+++ b/src/app/contacts-app/contact-details/contact-details.component.ts
@@ -31,6 +31,7 @@ import { MobileQueryService } from '../../mobile-query.service';
import { ContactPickerDialogComponent } from '../contact-picker-dialog.component';
import { AppSettings } from '../../app-settings';
import { DraftDeskService } from '../../compose/draftdesk.service';
+import { firstValueFrom } from 'rxjs';
import { RunboxWebmailAPI } from '../../rmmapi/rbwebmail';
import { OnscreenComponent } from '../../onscreen/onscreen.component';
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
@@ -329,7 +330,7 @@ export class ContactDetailsComponent {
}
async askForMoreMembers(): Promise {
- let contacts = await this.contactsservice.contactsSubject.pipe(take(1)).toPromise();
+ let contacts = await firstValueFrom(this.contactsservice.contactsSubject.pipe(take(1)));
contacts = contacts.filter(c => {
if (c.kind !== ContactKind.INVIDIDUAL) {
return false;
@@ -408,14 +409,14 @@ export class ContactDetailsComponent {
}
async newVideoCall(email: string) {
- const name = await this.dialog.open(SimpleInputDialog, {
+ const name = await firstValueFrom(this.dialog.open(SimpleInputDialog, {
data: new SimpleInputDialogParams(
'Meeting invitation',
'Pick a name for your meeting',
'Meeting name (optional)',
)
- }).afterClosed().toPromise();
- const me = await this.rmmapi.me.toPromise();
+ }).afterClosed());
+ const me = await firstValueFrom(this.rmmapi.me);
const meetingCode = OnscreenComponent.generateMeetingName(name, me.uid.toString());
const meetingUrl = new URL(this.location.prepareExternalUrl(`/onscreen/${meetingCode}`), window.location.origin);
this.draftDeskService.newVideoCallInvite(email, meetingUrl).then(() => {
diff --git a/src/app/contacts-app/contact-list.component.ts b/src/app/contacts-app/contact-list.component.ts
index 53e02aed7..393988461 100644
--- a/src/app/contacts-app/contact-list.component.ts
+++ b/src/app/contacts-app/contact-list.component.ts
@@ -17,7 +17,7 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import { Component, Input, EventEmitter, Output } from '@angular/core';
+import { Component, Input, EventEmitter, Output, OnChanges } from '@angular/core';
import { MatSidenav } from '@angular/material/sidenav';
import { Contact } from './contact';
@@ -28,7 +28,7 @@ type SelectionEvent = Set;
styleUrls: ['contacts-app.component.scss'],
templateUrl: './contact-list.component.html'
})
-export class ContactListComponent {
+export class ContactListComponent implements OnChanges {
@Input() contacts: Contact[];
@Input() categories: string[] = [];
diff --git a/src/app/contacts-app/contacts-app.component.ts b/src/app/contacts-app/contacts-app.component.ts
index d55a4054b..7e7ba2d10 100644
--- a/src/app/contacts-app/contacts-app.component.ts
+++ b/src/app/contacts-app/contacts-app.component.ts
@@ -258,7 +258,7 @@ export class ContactsAppComponent {
try {
contacts = Contact.fromVcf(vcf);
} catch (e) {
- console.error(e)
+ console.error(e);
if (warning) {
// we predicted this:
this.showError('Only .vcf contacts files are supported, this does not look like one');
diff --git a/src/app/folder/folderlist.component.spec.ts b/src/app/folder/folderlist.component.spec.ts
index ecaf28f44..c7d05637d 100644
--- a/src/app/folder/folderlist.component.spec.ts
+++ b/src/app/folder/folderlist.component.spec.ts
@@ -20,7 +20,7 @@
import { FolderListComponent, DropPosition, CreateFolderEvent, MoveFolderEvent } from './folderlist.component';
import { RunboxWebmailAPI } from '../rmmapi/rbwebmail';
import { FolderListEntry } from '../common/folderlistentry';
-import { BehaviorSubject, of } from 'rxjs';
+import { BehaviorSubject, firstValueFrom, of } from 'rxjs';
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { TestBed } from '@angular/core/testing';
import { take } from 'rxjs/operators';
@@ -76,19 +76,19 @@ describe('FolderListComponent', () => {
console.log('move folder with id 6 above 5');
await comp.folderReorderingDrop(6, 5, DropPosition.ABOVE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 2, 3, 4, 6, 5, 7]);
expect(rearrangedFolders.map(f => f.folderLevel)).toEqual([0, 0, 1, 2, 2, 2, 0]);
console.log('move folder with id 6 above 5 - should not cause any changes');
await comp.folderReorderingDrop(6, 5, DropPosition.ABOVE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 2, 3, 4, 6, 5, 7]);
expect(rearrangedFolders.map(f => f.folderLevel)).toEqual([0, 0, 1, 2, 2, 2, 0]);
console.log('move folder with id 6 below 5');
comp.folderReorderingDrop(6, 5, DropPosition.BELOW);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(moveEvent.order).toEqual([1, 2, 3, 4, 5, 6, 7]);
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 2, 3, 4, 5, 6, 7]);
@@ -96,7 +96,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 5 below 7');
comp.folderReorderingDrop(5, 7, DropPosition.BELOW);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(moveEvent.order).toEqual([1, 2, 3, 4, 6, 7, 5]);
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 2, 3, 4, 6, 7, 5]);
@@ -105,7 +105,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 5 inside 7');
comp.folderReorderingDrop(5, 7, DropPosition.INSIDE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 2, 3, 4, 6, 7, 5]);
expect(rearrangedFolders[6].folderPath).toBe('folder3.subsubfolder2');
@@ -113,7 +113,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 7 above 1');
comp.folderReorderingDrop(7, 1, DropPosition.ABOVE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([7, 5, 1, 2, 3, 4, 6]);
expect(rearrangedFolders[0].folderPath).toBe('folder3');
@@ -122,7 +122,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 7 below 1');
comp.folderReorderingDrop(7, 1, DropPosition.BELOW);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 7, 5, 2, 3, 4, 6]);
expect(rearrangedFolders[1].folderPath).toBe('folder3');
@@ -131,7 +131,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 4 above 7');
comp.folderReorderingDrop(4, 7, DropPosition.ABOVE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 4, 7, 5, 2, 3, 6]);
expect(rearrangedFolders[1].folderPath).toBe('subsubfolder');
@@ -141,7 +141,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 5 below 7');
comp.folderReorderingDrop(5, 7, DropPosition.BELOW);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders[2].folderPath).toBe('folder3');
expect(rearrangedFolders[3].folderPath).toBe('subsubfolder2');
@@ -150,7 +150,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 6 below 3');
comp.folderReorderingDrop(6, 3, DropPosition.BELOW);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 4, 7, 5, 2, 3, 6]);
@@ -158,7 +158,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 6 inside 7');
comp.folderReorderingDrop(6, 7, DropPosition.INSIDE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 4, 7, 6, 5, 2, 3]);
@@ -166,7 +166,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 3 above 7');
comp.folderReorderingDrop(3, 7, DropPosition.ABOVE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 4, 3, 7, 6, 5, 2]);
@@ -174,7 +174,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 6 above 4');
comp.folderReorderingDrop(6, 4, DropPosition.ABOVE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 6, 4, 3, 7, 5, 2]);
@@ -182,7 +182,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 6 inside 4');
comp.folderReorderingDrop(6, 4, DropPosition.INSIDE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 4, 6, 3, 7, 5, 2]);
@@ -190,7 +190,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 3 inside 5');
comp.folderReorderingDrop(3, 5, DropPosition.INSIDE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 4, 6, 7, 5, 3, 2]);
@@ -198,7 +198,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 4 above 5');
comp.folderReorderingDrop(4, 5, DropPosition.ABOVE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 7, 4, 6, 5, 3, 2]);
@@ -206,7 +206,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 6 below 7');
comp.folderReorderingDrop(6, 7, DropPosition.BELOW);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 7, 6, 4, 5, 3, 2]);
@@ -214,7 +214,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 2 inside 3');
comp.folderReorderingDrop(2, 3, DropPosition.INSIDE);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 7, 6, 4, 5, 3, 2]);
@@ -222,7 +222,7 @@ describe('FolderListComponent', () => {
console.log('move folder with id 4 below 3');
comp.folderReorderingDrop(4, 3, DropPosition.BELOW);
- rearrangedFolders = await comp.folders.pipe(take(1)).toPromise();
+ rearrangedFolders = await firstValueFrom(comp.folders);
console.log(rearrangedFolders.map(f => f.folderId));
expect(rearrangedFolders.map(f => f.folderId)).toEqual([1, 7, 6, 5, 3, 2, 4]);
@@ -244,7 +244,7 @@ describe('FolderListComponent', () => {
foldersSubject.next([...folders, new FolderListEntry(3, 50, 40, 'user', ev.name, 'folder2', 0)])
);
comp.addFolder();
- const newListOfFolders = await comp.folders.pipe(take(1)).toPromise();
+ const newListOfFolders = await firstValueFrom(comp.folders);
console.log(newListOfFolders);
diff --git a/src/app/folder/folderlist.component.ts b/src/app/folder/folderlist.component.ts
index e0524080e..c6a96fc9e 100644
--- a/src/app/folder/folderlist.component.ts
+++ b/src/app/folder/folderlist.component.ts
@@ -25,7 +25,7 @@ import { FolderListEntry } from '../common/folderlistentry';
import { FolderMessageCountMap } from '../rmmapi/messagelist.service';
import { SimpleInputDialog, SimpleInputDialogParams } from '../dialog/simpleinput.dialog';
-import { Observable } from 'rxjs';
+import { Observable, firstValueFrom } from 'rxjs';
import { first, map, filter, take } from 'rxjs/operators';
import { FlatTreeControl } from '@angular/cdk/tree';
import {ExtendedKeyboardEvent, Hotkey, HotkeysService} from 'angular2-hotkeys';
@@ -117,7 +117,7 @@ export class FolderListComponent implements OnChanges {
}
} catch (e) {
/* we don't care why it failed, it just means that we'll show all folders as collapsed */
- console.error(e)
+ console.error(e);
}
this.treeControl = new FlatTreeControl(this._getLevel, this._isExpandable);
@@ -255,8 +255,8 @@ export class FolderListComponent implements OnChanges {
}
onFolderClick($event, folder) {
- $event.preventDefault()
- this.selectFolder(folder)
+ $event.preventDefault();
+ this.selectFolder(folder);
}
selectFolder(folder: string): void {
@@ -366,7 +366,7 @@ export class FolderListComponent implements OnChanges {
return;
}
- const folders = await this.folders.pipe(take(1)).toPromise();
+ const folders = await firstValueFrom(this.folders.pipe(take(1)));
let sourceIndex = folders.findIndex(fld => fld.folderId === sourceFolderId);
let destinationIndex = folders.findIndex(folder => folder.folderId === destinationFolderId);
diff --git a/src/app/http/progress.service.spec.ts b/src/app/http/progress.service.spec.ts
index 8bb91f129..4576c8eb4 100644
--- a/src/app/http/progress.service.spec.ts
+++ b/src/app/http/progress.service.spec.ts
@@ -1,18 +1,18 @@
// --------- BEGIN RUNBOX LICENSE ---------
// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com).
-//
+//
// This file is part of Runbox 7.
-//
+//
// Runbox 7 is free software: You can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
-//
+//
// Runbox 7 is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
-//
+//
// You should have received a copy of the GNU General Public License
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
@@ -27,6 +27,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { RMMHttpInterceptorService } from '../rmmapi/rmmhttpinterceptor.service';
import { ProgressService } from './progress.service';
import { RouterTestingModule } from '@angular/router/testing';
+import { firstValueFrom } from 'rxjs';
import { filter, take } from 'rxjs/operators';
import { RMMAuthGuardService } from '../rmmapi/rmmauthguard.service';
import { RMMOfflineService } from '../rmmapi/rmmoffline.service';
@@ -72,14 +73,12 @@ describe('ProgressService', () => {
const last_on_req = httpMock.expectOne('/rest/v1/last_on');
last_on_req.flush(200);
- const me = await rmmapiservice.me.toPromise();
+ const me = await firstValueFrom(rmmapiservice.me);
expect(me.last_name).toBe('testuser');
expect(httpProgressSeen).toBeTruthy();
- const hasCurrentHttpActivity = await progressService.httpRequestInProgress.pipe(
- take(1)
- ).toPromise();
+ const hasCurrentHttpActivity = await firstValueFrom(progressService.httpRequestInProgress);
expect(hasCurrentHttpActivity).toBeFalsy();
expect(rmmapiservice.last_on_interval).toBeTruthy();
diff --git a/src/app/http/progress.service.ts b/src/app/http/progress.service.ts
index 647df750b..4c5882bf6 100644
--- a/src/app/http/progress.service.ts
+++ b/src/app/http/progress.service.ts
@@ -1,26 +1,30 @@
// --------- BEGIN RUNBOX LICENSE ---------
// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com).
-//
+//
// This file is part of Runbox 7.
-//
+//
// Runbox 7 is free software: You can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
-//
+//
// Runbox 7 is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
-//
+//
// You should have received a copy of the GNU General Public License
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
import {Injectable} from '@angular/core';
-import {Subject, BehaviorSubject} from 'rxjs';
+import {Subject, BehaviorSubject, Observable} from 'rxjs';
+import {delay} from 'rxjs/operators';
@Injectable()
export class ProgressService {
httpRequestInProgress: Subject = new BehaviorSubject(false);
+
+ // Delayed version to avoid ExpressionChangedAfterItHasBeenCheckedError in templates
+ httpRequestInProgress$: Observable = this.httpRequestInProgress.pipe(delay(0));
}
diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts
index d73e32a74..60e5c0e46 100644
--- a/src/app/login/login.component.ts
+++ b/src/app/login/login.component.ts
@@ -93,7 +93,7 @@ export class LoginComponent implements OnInit {
}
public onSubmit(ngForm: NgForm) {
- const { value } = ngForm
+ const { value } = ngForm;
this.login_errors_reset();
diff --git a/src/app/mailviewer/avatar-bar.component.ts b/src/app/mailviewer/avatar-bar.component.ts
index 24bb623d7..76b23948a 100644
--- a/src/app/mailviewer/avatar-bar.component.ts
+++ b/src/app/mailviewer/avatar-bar.component.ts
@@ -17,8 +17,8 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import { Component, Input, OnInit } from '@angular/core';
-import { ReplaySubject} from 'rxjs';
+import { Component, Input, OnInit, OnChanges } from '@angular/core';
+import { firstValueFrom, ReplaySubject} from 'rxjs';
import { take } from 'rxjs/operators';
import { ProfileService } from '../profiles/profile.service';
import { ContactsService } from '../contacts-app/contacts.service';
@@ -53,7 +53,7 @@ import { PreferencesService } from '../common/preferences.service';
`,
})
-export class AvatarBarComponent implements OnInit {
+export class AvatarBarComponent implements OnInit, OnChanges {
@Input() email: {
from: string[],
to: string[],
@@ -82,7 +82,7 @@ export class AvatarBarComponent implements OnInit {
async ngOnChanges() {
this.emails = []; // so that we don't display the old, wrong ones while we're loading new ones
- const own = await this.ownAddresses.pipe(take(1)).toPromise();
+ const own = await firstValueFrom(this.ownAddresses.pipe(take(1)));
const emails: string[] = [].concat.apply(
[], [this.email.from, this.email.to, this.email.cc, this.email.bcc]
diff --git a/src/app/mailviewer/singlemailviewer.component.ts b/src/app/mailviewer/singlemailviewer.component.ts
index 0c632d961..1bb81b032 100644
--- a/src/app/mailviewer/singlemailviewer.component.ts
+++ b/src/app/mailviewer/singlemailviewer.component.ts
@@ -1,18 +1,18 @@
// --------- BEGIN RUNBOX LICENSE ---------
// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com).
-//
+//
// This file is part of Runbox 7.
-//
+//
// Runbox 7 is free software: You can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
-//
+//
// Runbox 7 is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
-//
+//
// You should have received a copy of the GNU General Public License
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
@@ -40,7 +40,7 @@ import { MobileQueryService } from '../mobile-query.service';
import { HorizResizerDirective } from '../directives/horizresizer.directive';
import { MessageContents, RunboxWebmailAPI } from '../rmmapi/rbwebmail';
-import { of } from 'rxjs';
+import { firstValueFrom, of } from 'rxjs';
import { Router } from '@angular/router';
import { MessageListService } from '../rmmapi/messagelist.service';
import { loadLocalMailParser } from './mailparser';
@@ -564,7 +564,7 @@ export class SingleMailViewerComponent implements OnInit, DoCheck, AfterViewInit
res.date.setMinutes(res.date.getMinutes() - res.date.getTimezoneOffset());
res.sanitized_html = this.expandAttachmentData(res.attachments, res.sanitized_html);
- res.visible_attachment_count = res.attachments.filter((att) => !att.internal).length;
+ res.visible_attachment_count = res.attachments.filter((att) => !att.internal).length;
res.sanitized_html_without_images = this.expandAttachmentData(res.attachments, res.sanitized_html_without_images);
@@ -689,7 +689,7 @@ export class SingleMailViewerComponent implements OnInit, DoCheck, AfterViewInit
}
print() {
- // Can't access print view inside iFrame, so we need to
+ // Can't access print view inside iFrame, so we need to
// temporary hide buttons while the view is rendering
const messageContents = document.getElementById('messageContents');
const buttons = messageContents.getElementsByTagName('button');
@@ -732,7 +732,7 @@ export class SingleMailViewerComponent implements OnInit, DoCheck, AfterViewInit
/**
* EXPERIMENTAL, decrypt attachment (encrypted.asc) by sending it to pgpapp.no
- * @param attachmentIndex
+ * @param attachmentIndex
*/
public decryptAttachment(attachmentIndex: number) {
this.http.get('/rest/v1/email/' + this.messageId + '/attachment/' + attachmentIndex,
@@ -746,7 +746,7 @@ export class SingleMailViewerComponent implements OnInit, DoCheck, AfterViewInit
window.removeEventListener('message', pgpapplistener);
pgpapp.close();
- const parseMail = await loadLocalMailParser().toPromise();
+ const parseMail = await firstValueFrom(loadLocalMailParser());
const parsed = await parseMail(msg.data.decryptedContent);
this.mailObj.text = parsed.text;
this.mailObj.subject = parsed.subject;
diff --git a/src/app/menu/headertoolbar.component.ts b/src/app/menu/headertoolbar.component.ts
index 2a6964181..ae584a1f3 100644
--- a/src/app/menu/headertoolbar.component.ts
+++ b/src/app/menu/headertoolbar.component.ts
@@ -17,7 +17,7 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
import { RunboxWebmailAPI } from '../rmmapi/rbwebmail';
import { RMMOfflineService } from '../rmmapi/rmmoffline.service';
import { Router } from '@angular/router';
@@ -29,7 +29,7 @@ import { RunboxMe } from '../rmmapi/rbwebmail';
selector: 'rmm-headertoolbar',
templateUrl: 'headertoolbar.component.html'
})
-export class HeaderToolbarComponent {
+export class HeaderToolbarComponent implements OnInit {
rmm6tooltip = 'This area isn\'t upgraded to Runbox 7 yet and will open in a new tab';
user_is_trial = false;
diff --git a/src/app/onscreen/onscreen.component.ts b/src/app/onscreen/onscreen.component.ts
index a9d5d5eaf..8997dc4c8 100644
--- a/src/app/onscreen/onscreen.component.ts
+++ b/src/app/onscreen/onscreen.component.ts
@@ -20,7 +20,7 @@
import { Component, OnDestroy } from '@angular/core';
import { Location } from '@angular/common';
import { MobileQueryService } from '../mobile-query.service';
-import { AsyncSubject } from 'rxjs';
+import { AsyncSubject, firstValueFrom } from 'rxjs';
import { RunboxWebmailAPI, RunboxMe } from '../rmmapi/rbwebmail';
import { ContactsService } from '../contacts-app/contacts.service';
import { ActivatedRoute } from '@angular/router';
@@ -115,7 +115,7 @@ export class OnscreenComponent implements OnDestroy {
}
async createMeeting() {
- await jitsiLoader.toPromise();
+ await firstValueFrom(jitsiLoader);
const name = await this.encodeMeetingName(this.form.meetingName);
this.joinMeeting(name).then(() => {
@@ -132,7 +132,7 @@ export class OnscreenComponent implements OnDestroy {
}
async joinMeeting(code: string) {
- await jitsiLoader.toPromise();
+ await firstValueFrom(jitsiLoader);
this.jitsiAPI = new JitsiMeetExternalAPI('video.runbox.com', {
roomName: code,
@@ -188,7 +188,7 @@ export class OnscreenComponent implements OnDestroy {
}
async encodeMeetingName(name: string): Promise {
- const me = await this.me.toPromise();
+ const me = await firstValueFrom(this.me);
return Promise.resolve(OnscreenComponent.generateMeetingName(name, me.uid.toString()));
}
diff --git a/src/app/profiles/profile.service.spec.ts b/src/app/profiles/profile.service.spec.ts
index de80daf6c..5be6dcaad 100644
--- a/src/app/profiles/profile.service.spec.ts
+++ b/src/app/profiles/profile.service.spec.ts
@@ -157,36 +157,36 @@ describe('ProfileService', () => {
}).compileComponents();
}));
- beforeEach(() => { service = service = TestBed.inject(ProfileService) });
+ beforeEach(() => { service = service = TestBed.inject(ProfileService); });
it('loads valid profile subsets', (done) => {
service.validProfiles.subscribe(profiles => {
expect(profiles.length).toBe(4);
done();
});
- })
+ });
it('loads all profiles', (done) => {
service.profiles.subscribe(profiles => {
expect(profiles.length).toBe(PROFILES.length);
done();
});
- })
+ });
it('loads alias profile subsets', (done) => {
service.aliases.subscribe(profiles => {
expect(profiles.length).toBe(PROFILES.filter(p => p.reference_type === 'aliases').length);
done();
});
- })
+ });
it('loads non alias profile subsets', (done) => {
service.otherProfiles.subscribe(profiles => {
expect(profiles.length).toBe(2);
done();
});
- })
+ });
it('loads a compose profile', () => {
expect(service.composeProfile).toBeDefined();
expect(service.composeProfile.email).toEqual('a2@example.com');
- })
+ });
it('adds a new profile on create', (done) => {
service.create({ name: 'New Profile Name',
diff --git a/src/app/rmm/account-security.ts b/src/app/rmm/account-security.ts
index 748072da8..33a505202 100644
--- a/src/app/rmm/account-security.ts
+++ b/src/app/rmm/account-security.ts
@@ -80,7 +80,7 @@ export class AccountSecurity {
+ ' \\____\\/ \\_____/\\ \n'
+ ' \\____\\/ \n';
return txt;
- }
+ };
txt_footer: any = () => {
const dt = new Date();
@@ -93,7 +93,7 @@ export class AccountSecurity {
' \n'
+ ' https://runbox.com Generated by Runbox at ' + dt_str + ' \n';
return txt;
- }
+ };
check_password(password): Observable {
this.is_busy = true;
diff --git a/src/app/rmm6/rmm6.module.ts b/src/app/rmm6/rmm6.module.ts
index ccbb4ffb6..52abc3aa8 100644
--- a/src/app/rmm6/rmm6.module.ts
+++ b/src/app/rmm6/rmm6.module.ts
@@ -17,7 +17,7 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import { NgModule, ApplicationRef, ComponentFactoryResolver, Injector, NgZone } from '@angular/core';
+import { NgModule, ApplicationRef, ComponentFactoryResolver, Injector, NgZone, DoBootstrap } from '@angular/core';
import { RMM6AngularGateway } from './rmm6angulargateway';
import { MailViewerModule } from '../mailviewer/mailviewer.module';
import { DomainRegisterModule } from '../domainregister/domainregister.module';
@@ -78,7 +78,7 @@ import { SearchExpressionBuilderModule } from '../xapian/search-expression-build
],
providers: [ProgressService]
})
-export class RMM6Module {
+export class RMM6Module implements DoBootstrap {
rmmAngularGW: RMM6AngularGateway;
diff --git a/src/app/rmmapi/rblocale.ts b/src/app/rmmapi/rblocale.ts
index cdbcdb0c5..738eb3c0f 100644
--- a/src/app/rmmapi/rblocale.ts
+++ b/src/app/rmmapi/rblocale.ts
@@ -45,7 +45,7 @@ export class RunboxLocale {
try {
translated = window['getLocale'](key.split('.'));
} catch (ex) {
- console.error(ex)
+ console.error(ex);
console.log('locale translations not found');
}
return translated;
diff --git a/src/app/saved-searches/saved-searches.service.ts b/src/app/saved-searches/saved-searches.service.ts
index 21763d5a6..7c58e14d4 100644
--- a/src/app/saved-searches/saved-searches.service.ts
+++ b/src/app/saved-searches/saved-searches.service.ts
@@ -18,7 +18,7 @@
// ---------- END RUNBOX LICENSE ----------
import { Injectable } from '@angular/core';
-import { ReplaySubject } from 'rxjs';
+import { firstValueFrom, ReplaySubject } from 'rxjs';
import { take } from 'rxjs/operators';
import { StorageService } from '../storage.service';
@@ -83,7 +83,7 @@ export class SavedSearchesService {
private async uploadSeachData() {
const data: SavedSearchStorage = {
version: this.version,
- entries: await this.searches.pipe(take(1)).toPromise(),
+ entries: await firstValueFrom(this.searches.pipe(take(1))),
};
this.rmmapi.setSavedSearches(data).subscribe(
newData => this.applySyncedData(newData)
diff --git a/src/app/sentry-error-handler.ts b/src/app/sentry-error-handler.ts
index d005c9f4b..af3d56711 100644
--- a/src/app/sentry-error-handler.ts
+++ b/src/app/sentry-error-handler.ts
@@ -22,6 +22,7 @@ import * as Sentry from '@sentry/browser';
import './sentry';
import { RMMAuthGuardService } from './rmmapi/rmmauthguard.service';
+import { firstValueFrom } from 'rxjs';
import { RunboxWebmailAPI } from './rmmapi/rbwebmail';
@Injectable()
@@ -40,10 +41,10 @@ export class SentryErrorHandler implements ErrorHandler {
return;
}
const authguard = this.injector.get(RMMAuthGuardService);
- const isLoggedIn = await authguard.isLoggedIn().toPromise();
+ const isLoggedIn = await firstValueFrom(authguard.isLoggedIn());
if (isLoggedIn) {
const rmmapi = this.injector.get(RunboxWebmailAPI);
- const me = await rmmapi.me.toPromise();
+ const me = await firstValueFrom(rmmapi.me);
Sentry.setUser({
uid: me.uid,
username: me.username,
diff --git a/src/app/sentry.ts b/src/app/sentry.ts
index d0980ca07..93677fad9 100644
--- a/src/app/sentry.ts
+++ b/src/app/sentry.ts
@@ -17,7 +17,7 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import { environment } from '../environments/environment'
+import { environment } from '../environments/environment';
import * as Sentry from '@sentry/browser';
Sentry.init({
diff --git a/src/app/start/startdesk.component.ts b/src/app/start/startdesk.component.ts
index 26cf8c34f..53faca1dd 100644
--- a/src/app/start/startdesk.component.ts
+++ b/src/app/start/startdesk.component.ts
@@ -27,7 +27,7 @@ import { Contact } from '../contacts-app/contact';
import { SearchService, SearchIndexDocumentData } from '../xapian/searchservice';
import { isValidEmail } from '../compose/emailvalidator';
import { filter, take } from 'rxjs/operators';
-import { ReplaySubject } from 'rxjs';
+import { firstValueFrom, ReplaySubject } from 'rxjs';
import { ProfileService } from '../profiles/profile.service';
import { UsageReportsService } from '../common/usage-reports.service';
@@ -267,7 +267,7 @@ export class StartDeskComponent implements OnInit {
private async extractMailingLists(messages: SearchIndexDocumentData[]): Promise> {
const possibleMailingLists = new Map();
- const ownAddresses = await this.ownAddresses.pipe(take(1)).toPromise();
+ const ownAddresses = await firstValueFrom(this.ownAddresses.pipe(take(1)));
for (const message of messages) {
if (!message.recipients.find(r => ownAddresses.has(r.toLowerCase()))) {
diff --git a/src/app/storage.service.ts b/src/app/storage.service.ts
index 3af77f90f..31e57e397 100644
--- a/src/app/storage.service.ts
+++ b/src/app/storage.service.ts
@@ -19,7 +19,7 @@
import { RunboxWebmailAPI } from './rmmapi/rbwebmail';
import { Injectable } from '@angular/core';
-import { AsyncSubject, ReplaySubject } from 'rxjs';
+import { AsyncSubject, firstValueFrom, ReplaySubject } from 'rxjs';
@Injectable()
export class StorageService {
@@ -36,7 +36,7 @@ export class StorageService {
}
private async userKey(key: string): Promise {
- const uid = await this.uid.toPromise();
+ const uid = await firstValueFrom(this.uid);
return `${uid}:${key}`;
}
diff --git a/src/app/welcome/welcomedesk.component.ts b/src/app/welcome/welcomedesk.component.ts
index 0a1ab40a4..1b1580248 100644
--- a/src/app/welcome/welcomedesk.component.ts
+++ b/src/app/welcome/welcomedesk.component.ts
@@ -37,11 +37,11 @@ export class WelcomeDeskComponent implements OnInit {
this.rmmapi.me.subscribe(me => this.me = me);
}
- public postSignup = ''
+ public postSignup = '';
ngOnInit() {
this.activatedRoute.queryParams.subscribe(params => {
this.postSignup = params['postSignup'];
- })
+ });
}
}
diff --git a/src/app/xapian/index.worker.ts b/src/app/xapian/index.worker.ts
index 378cbdc51..a1cfb24bd 100644
--- a/src/app/xapian/index.worker.ts
+++ b/src/app/xapian/index.worker.ts
@@ -21,7 +21,7 @@
import '../sentry';
-import { Observer, Observable, of, from, AsyncSubject } from 'rxjs';
+import { Observer, Observable, of, from, AsyncSubject, firstValueFrom, lastValueFrom } from 'rxjs';
import { mergeMap, map, filter, catchError, tap, take, bufferCount } from 'rxjs/operators';
import { XapianAPI } from '@runboxcom/runbox-searchindex';
@@ -147,7 +147,7 @@ class SearchIndexService {
db.close();
};
} catch (e) {
- console.error(e)
+ console.error(e);
console.log('Worker: Unable to open local xapian index', (e ? e.message : ''));
db.close();
// console.log('Worker: Req failed');
@@ -237,7 +237,7 @@ class SearchIndexService {
}
});
} catch (e) {
- console.error(e)
+ console.error(e);
}
}
@@ -273,7 +273,7 @@ class SearchIndexService {
try {
FS.unlink('xapianglass');
} catch (e) {
- console.error(e)
+ console.error(e);
}
@@ -535,8 +535,8 @@ not matching with rest api counts for current folder`);
(result) => console.log(result.result.result.msg)
).catch(
(err) => {
- console.error(err)
- console.log('Error updating folder counts: ' + err.errors.join(','))
+ console.error(err);
+ console.log('Error updating folder counts: ' + err.errors.join(','));
}
);
}
@@ -556,7 +556,7 @@ not matching with rest api counts for current folder`);
try {
this.api.deleteDocumentByUniqueTerm(uniqueIdTerm);
} catch (e) {
- console.error(e)
+ console.error(e);
console.error('Worker: Unable to delete message from index', msgid);
}
})
@@ -681,7 +681,7 @@ not matching with rest api counts for current folder`);
this.numberOfMessagesSyncedLastTime = searchIndexDocumentUpdates.length;
if (searchIndexDocumentUpdates.length > 0) {
- await this.postMessagesToXapianWorker(searchIndexDocumentUpdates).toPromise();
+ await lastValueFrom(this.postMessagesToXapianWorker(searchIndexDocumentUpdates));
}
// Look up messages with missing body text term and add the missing text to the index
@@ -713,7 +713,7 @@ not matching with rest api counts for current folder`);
console.error('Worker: Failed to add text to document', messageId, e);
}
});
- })).toPromise();
+ }));
}
} else {
// localsearchactivated is off
@@ -861,7 +861,7 @@ not matching with rest api counts for current folder`);
if (this.persistIndexInProgressSubject) {
// Wait for persistence of index to finish before doing more work on the index
- await this.persistIndexInProgressSubject.toPromise();
+ await firstValueFrom(this.persistIndexInProgressSubject);
}
setTimeout(() => processMessage(), 1);
@@ -872,7 +872,7 @@ not matching with rest api counts for current folder`);
this.indexNotPersisted = true;
}
this.api.commitXapianUpdates();
- await this.persistIndex().toPromise();
+ await lastValueFrom(this.persistIndex());
if (hasProgressSnackBar) {
ctx.postMessage({'action': PostMessageAction.closeProgressSnackBar});
diff --git a/src/app/xapian/multiple-search-fields-input/multiple-search-fields-input.component.spec.ts b/src/app/xapian/multiple-search-fields-input/multiple-search-fields-input.component.spec.ts
index dd9d0deed..a17304417 100644
--- a/src/app/xapian/multiple-search-fields-input/multiple-search-fields-input.component.spec.ts
+++ b/src/app/xapian/multiple-search-fields-input/multiple-search-fields-input.component.spec.ts
@@ -1,18 +1,18 @@
// --------- BEGIN RUNBOX LICENSE ---------
// Copyright (C) 2016-2019 Runbox Solutions AS (runbox.com).
-//
+//
// This file is part of Runbox 7.
-//
+//
// Runbox 7 is free software: You can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
-//
+//
// Runbox 7 is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
-//
+//
// You should have received a copy of the GNU General Public License
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
@@ -28,6 +28,7 @@ import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { firstValueFrom } from 'rxjs';
import { take } from 'rxjs/operators';
describe('MultipleSearchFieldsInputComponent', () => {
@@ -63,7 +64,7 @@ describe('MultipleSearchFieldsInputComponent', () => {
component.currentFolder = 'Testfolder';
component.formGroup.get('currentfolderonly').setValue(true);
- const searchExpressionPromise = component.searchexpression.pipe(take(1)).toPromise();
+ const searchExpressionPromise = firstValueFrom(component.searchexpression);
component.formGroup.get('subject').setValue('testsubject');
const searchExpression = await searchExpressionPromise;
diff --git a/src/app/xapian/multiple-search-fields-input/multiple-search-fields-input.component.ts b/src/app/xapian/multiple-search-fields-input/multiple-search-fields-input.component.ts
index 029e45252..d5854fcaf 100644
--- a/src/app/xapian/multiple-search-fields-input/multiple-search-fields-input.component.ts
+++ b/src/app/xapian/multiple-search-fields-input/multiple-search-fields-input.component.ts
@@ -54,7 +54,7 @@ export class MultipleSearchFieldsInputComponent implements OnChanges {
.pipe(distinctUntilChanged())
.subscribe(() => {
this.enableDisableUnread();
- this.buildSearchExpression()
+ this.buildSearchExpression();
});
}
diff --git a/src/app/xapian/searchservice.spec.ts b/src/app/xapian/searchservice.spec.ts
index b7e9277f2..6252a1654 100644
--- a/src/app/xapian/searchservice.spec.ts
+++ b/src/app/xapian/searchservice.spec.ts
@@ -1,18 +1,18 @@
// --------- BEGIN RUNBOX LICENSE ---------
// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com).
-//
+//
// This file is part of Runbox 7.
-//
+//
// Runbox 7 is free software: You can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
-//
+//
// Runbox 7 is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
-//
+//
// You should have received a copy of the GNU General Public License
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
@@ -28,6 +28,7 @@ import { MatLegacySnackBarModule as MatSnackBarModule } from '@angular/material/
import { MessageListService } from '../rmmapi/messagelist.service';
import { XapianAPI } from '@runboxcom/runbox-searchindex/rmmxapianapi';
+import { firstValueFrom } from 'rxjs';
import { xapianLoadedSubject } from './xapianwebloader';
import { PostMessageAction } from './messageactions';
@@ -123,12 +124,12 @@ describe('SearchService', () => {
xit('should load searchservice, but no local index', async () => {
const searchService = TestBed.inject(SearchService);
- await xapianLoadedSubject.toPromise();
+ await firstValueFrom(xapianLoadedSubject);
const req = httpMock.expectOne('/rest/v1/email_folder/list');
req.flush(listEmailFoldersResponse);
- expect(await searchService.initSubject.toPromise()).toBeFalsy();
+ expect(await firstValueFrom(searchService.initSubject)).toBeFalsy();
expect(searchService.localSearchActivated).toBeFalsy();
httpMock.verify();
@@ -193,7 +194,7 @@ describe('SearchService', () => {
const testuserid = 444;
const localdir = 'rmmsearchservice' + testuserid;
- await xapianLoadedSubject.toPromise();
+ await firstValueFrom(xapianLoadedSubject);
FS.mkdir(localdir);
FS.mount(IDBFS, {}, '/' + localdir);
@@ -246,7 +247,7 @@ describe('SearchService', () => {
req.flush(listEmailFoldersResponse);
- expect(await searchService.initSubject.toPromise()).toBeTruthy();
+ expect(await firstValueFrom(searchService.initSubject)).toBeTruthy();
console.log('search service initialised');
expect(searchService.localSearchActivated).toBeTruthy();
expect(localdir).toEqual(searchService.localdir);
diff --git a/src/app/xapian/searchservice.ts b/src/app/xapian/searchservice.ts
index efe5c9420..b83c992c4 100644
--- a/src/app/xapian/searchservice.ts
+++ b/src/app/xapian/searchservice.ts
@@ -223,7 +223,7 @@ export class SearchService {
db.close();
};
} catch (e) {
- console.error(e)
+ console.error(e);
console.log('Unable to open local xapian index', (e ? e.message : ''));
db.close();
observer.next(false);
@@ -366,7 +366,7 @@ export class SearchService {
this.indexLastUpdateTime = new Date().getTime();
}
} catch (e) {
- console.error(e)
+ console.error(e);
if (!this.updateIndexLastUpdateTime()) {
// Corrupt xapian index - delete it and subscribe to changes (fallback to websocket search)
// Deal with this on the non-worker side, then tell it to
@@ -386,7 +386,7 @@ export class SearchService {
} catch (e) {
- console.error(e)
+ console.error(e);
console.log('No xapian db');
this.initSubject.next(false);
}
@@ -411,7 +411,7 @@ export class SearchService {
}
});
} catch (e) {
- console.error(e)
+ console.error(e);
}
}
@@ -560,7 +560,7 @@ export class SearchService {
try {
FS.stat(XAPIAN_GLASS_WR);
} catch (e) {
- console.error(e)
+ console.error(e);
FS.mkdir(XAPIAN_GLASS_WR);
}
@@ -787,7 +787,7 @@ export class SearchService {
try {
FS.stat(`${this.partitionsdir}/${dirname}`);
} catch (e) {
- console.error(e)
+ console.error(e);
FS.mkdir(`${this.partitionsdir}/${dirname}`);
}
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index cf62cf7e2..5bec9c6ac 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -17,9 +17,9 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import env from './env'
+import env from './env';
export const environment = {
...env,
production: true
-}
+};
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index 3d2dfca83..1e1e66b03 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -17,7 +17,7 @@
// along with Runbox 7. If not, see .
// ---------- END RUNBOX LICENSE ----------
-import env from './env'
+import env from './env';
export const environment = {
...env,
diff --git a/src/styles.scss b/src/styles.scss
index fb10e6bf9..7ff559a45 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -32,7 +32,7 @@ $rmm-darker-background: #01001c;
$rmm-gray: #dddddd;
$rmm-gray-light: #eeeeee;
$rmm-gray-lighter: #f3f3f3;
-
+
$rmm-default-theme: mat.define-light-theme($rmm-default-primary, $rmm-default-accent, $rmm-default-warn);
$rmm-default-lighter-gray: #eeeeee;
@@ -42,24 +42,6 @@ $rmm-default-black: #444444;
@include mat.all-legacy-component-themes($rmm-default-theme);
-// GTA 13.06.2018: Load custom fonts
-
-@font-face {
- font-family: "Avenir Next Pro Regular";
- src: url("assets/AvenirNextLTPro-Regular.otf");
- src: url("assets/Avenir-Next-LT-Pro.ttf");
- font-style: normal;
- font-weight: normal;
-}
-
-@font-face {
- font-family: "Avenir Next Pro Medium";
- src: url("assets/AvenirNextLTPro-Medium.otf");
- src: url("assets/AvenirNextLTPro-Medium.ttf");
- font-style: normal;
- font-weight: normal;
-}
-
// GTA 13.06.2018: Override default fonts as per https://material.angular.io/guide/typography
$custom-typography: mat.define-legacy-typography-config(
@@ -159,7 +141,7 @@ a[mat-list-item] .mat-list-item-content {
min-height: 24px !important;
}
-.mat-list[dense] .mat-list-item .mat-list-text,
+.mat-list[dense] .mat-list-item .mat-list-text,
.mat-nav-list[dense] .mat-list-item .mat-list-text>*,
mat-list-item .mat-list-text,
a[mat-list-item] .mat-list-text {
@@ -313,21 +295,21 @@ mat-grid-tile.tableTitle {
height: 16px;
width: 42px;
}
-
+
.mat-slide-toggle.mat-checked .mat-slide-toggle-thumb-container {
top: -5px;
transform: translate3d(20px, 0, 0);
}
-
+
.mat-slide-toggle.mat-checked .mat-slide-toggle-thumb {
height: 24px;
width: 24px;
}
-
+
.mat-slide-toggle-label {
font-size: 16px;
}
-
+
.mat-slide-toggle-content {
margin-left: 2px;
}
@@ -394,12 +376,12 @@ mat-grid-tile.tableTitle {
/*** Main ***/
#main {
- position: fixed;
- top: 0px;
- bottom: 0px;
- left: 0px;
- right: 0px;
- width: 100%;
+ position: fixed;
+ top: 0px;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
+ width: 100%;
height: 100%;
min-height: 100%;
display: flex;
@@ -439,7 +421,7 @@ mat-grid-tile.tableTitle {
display: none;
margin: 0;
}
-
+
#logo {
margin: 0;
width: 300px;
@@ -518,7 +500,7 @@ div.loginScreen {
mat-form-field {
width: 200px;
}
- }
+ }
#loginOptions {
display: flex;
margin: 0.5em;
@@ -724,8 +706,8 @@ rmm-headertoolbar {
/* Sidenav pane */
mat-sidenav-container {
- position: absolute !important;
- bottom: 0px !important;
+ position: absolute !important;
+ bottom: 0px !important;
left: 0px !important;
right: 0px !important;
width: 100% !important;
@@ -782,7 +764,7 @@ mat-sidenav-container {
.mat-mini-fab .mat-button-wrapper {
line-height: 18px;
}
-
+
a {
width: 30%;
}
@@ -936,7 +918,7 @@ rmm-folderlist {
}
.foldersidebarcount {
- font-size: 10px;
+ font-size: 10px;
}
.draftsFolder {
@@ -982,7 +964,7 @@ app-saved-searches .mat-list-base[dense] .mat-list-item {
flex-grow: 1;
overflow: hidden;
}
-
+
.messageListActionButtonsRight button {
width: 30px; // Remember to also update TOOLBAR_LIST_BUTTON_WIDTH in app.component.ts in order to show the correct number of menu items
}
@@ -1008,7 +990,7 @@ app-saved-searches .mat-list-base[dense] .mat-list-item {
button {
margin-right: 10px;
-
+
@media(max-width: 540px) {
margin-right: 2px;
height: 30px;
@@ -1022,7 +1004,7 @@ app-saved-searches .mat-list-base[dense] .mat-list-item {
#offerLocalIndex .mat-list-item-content {
padding: 0 5px;
-}
+}
#searchField {
flex-grow: 10;
@@ -1097,7 +1079,7 @@ app-saved-searches .mat-list-base[dense] .mat-list-item {
.mat-icon, .mat-icon-button {
color: mat.get-color-from-palette($rmm-default-primary);
}
-
+
@media (max-width: 540px) {
#threadedCheckbox {
display: none;
@@ -1172,7 +1154,7 @@ app-saved-searches .mat-list-base[dense] .mat-list-item {
.mat-radio-label-content {
padding: 0 !important;
}
-
+
button, .mat-radio-button, .mat-checkbox {
margin-left: 5px;
}
@@ -1195,7 +1177,7 @@ app-saved-searches .mat-list-base[dense] .mat-list-item {
.mat-icon {
margin: 0 3px !important;
}
-
+
mat-flat-button, .mat-flat-button, mat-raised-button, .mat-raised-button {
min-width: 30px !important;
width: 30px !important;
@@ -1307,7 +1289,7 @@ compose #fieldFrom .mat-form-field-wrapper, compose .fieldRecipient .mat-form-fi
.recipientSuggestionContainer {
max-height: 90px;
overflow: auto;
-
+
span {
font-size: 12.5px;
}
@@ -1347,7 +1329,7 @@ compose #fieldFrom .mat-form-field-wrapper, compose .fieldRecipient .mat-form-fi
.mat-nav-list[dense], .mat-list-item, .mat-list-text, .mat-form-field {
height: 48px !important;
}
-}
+}
.contactList .mat-form-field-infix {
font-size: 16px;
@@ -1370,7 +1352,7 @@ compose #fieldFrom .mat-form-field-wrapper, compose .fieldRecipient .mat-form-fi
.mat-form-field-appearance-legacy .mat-form-field-infix {
padding-top: 0 !important;
}
-
+
.mat-form-field-appearance-legacy .mat-form-field-label {
top: 0.75em;
}
@@ -1519,7 +1501,7 @@ app-calendar-event-editor-dialog p {
.productGrid mat-card.recommended {
border: 1px solid mat.get-color-from-palette($rmm-default-primary);
-}
+}
#pricePlans td {
/* border-right: 1px solid $rmm-dark-background !important; */
@@ -1573,7 +1555,7 @@ app-payment-method {
cursor: pointer;
}
}
-
+
#otherPaymentMethods {
margin: 20px 0 50px 0;
max-width: 90%;
@@ -1594,7 +1576,7 @@ app-payment-method {
color: #0F0;
}
-.dev.runbox-components .nice_green_timer .timeunit {
+.dev.runbox-components .nice_green_timer .timeunit {
border: 1px solid #0F0;
width: 40px;
height: 40px;
@@ -1609,22 +1591,22 @@ app-payment-method {
.dev.runbox-components .nice_green_timer .timeunit.hours {
color: #0f0;
}
-.dev.runbox-components .nice_green_timer .timeunit.years::after {
+.dev.runbox-components .nice_green_timer .timeunit.years::after {
content: "y";
}
-.dev.runbox-components .nice_green_timer .timeunit.months::after {
+.dev.runbox-components .nice_green_timer .timeunit.months::after {
content: "m";
}
-.dev.runbox-components .nice_green_timer .timeunit.days::after {
+.dev.runbox-components .nice_green_timer .timeunit.days::after {
content: "d";
}
-.dev.runbox-components .nice_green_timer .timeunit.hours::after {
+.dev.runbox-components .nice_green_timer .timeunit.hours::after {
content: "h";
}
-.dev.runbox-components .nice_green_timer .timeunit.minutes::after {
+.dev.runbox-components .nice_green_timer .timeunit.minutes::after {
content: "m";
}
-.dev.runbox-components .nice_green_timer .timeunit.seconds::after {
+.dev.runbox-components .nice_green_timer .timeunit.seconds::after {
content: "s";
}
@@ -1640,22 +1622,22 @@ app-payment-method {
align-items: center;
}
-.dev.runbox-components .nice_blue_timer .timeunit.years::after {
+.dev.runbox-components .nice_blue_timer .timeunit.years::after {
content: " years";
}
-.dev.runbox-components .nice_blue_timer .timeunit.months::after {
+.dev.runbox-components .nice_blue_timer .timeunit.months::after {
content: " months";
}
-.dev.runbox-components .nice_blue_timer .timeunit.days::after {
+.dev.runbox-components .nice_blue_timer .timeunit.days::after {
content: " days";
}
-.dev.runbox-components .nice_blue_timer .timeunit.hours::after {
+.dev.runbox-components .nice_blue_timer .timeunit.hours::after {
content: " hours";
}
-.dev.runbox-components .nice_blue_timer .timeunit.minutes::after {
+.dev.runbox-components .nice_blue_timer .timeunit.minutes::after {
content: " mins";
}
-.dev.runbox-components .nice_blue_timer .timeunit.seconds::after {
+.dev.runbox-components .nice_blue_timer .timeunit.seconds::after {
content: " secs";
}
@@ -1743,7 +1725,7 @@ td.mat-cell.cdk-column-renewal_name.mat-column-renewal_name {
table.renewalsTable td, table.paymentsTable td {
padding: 5px 10px 0px 10px !important;
-}
+}
table.detailsTable {
width: 100%;