diff --git a/src/app/accessible-table/accessible-table.component.html b/src/app/accessible-table/accessible-table.component.html new file mode 100644 index 000000000..148e6d4c7 --- /dev/null +++ b/src/app/accessible-table/accessible-table.component.html @@ -0,0 +1,13 @@ + + + + + + + + +
+
diff --git a/src/app/accessible-table/accessible-table.component.scss b/src/app/accessible-table/accessible-table.component.scss new file mode 100644 index 000000000..66ce30d72 --- /dev/null +++ b/src/app/accessible-table/accessible-table.component.scss @@ -0,0 +1,16 @@ +table { + border-collapse: collapse; /* Removes space between table cells */ + border-spacing: 0; /* Ensures no extra spacing between cells */ + max-width: 100%; + table-layout: fixed; + user-select: none; + width: 100%; +} + +cdk-virtual-scroll-viewport { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} diff --git a/src/app/accessible-table/accessible-table.component.ts b/src/app/accessible-table/accessible-table.component.ts new file mode 100644 index 000000000..0de0c8ef1 --- /dev/null +++ b/src/app/accessible-table/accessible-table.component.ts @@ -0,0 +1,110 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2025 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 { + OnDestroy, + ChangeDetectionStrategy, + AfterViewInit, + Component, + ContentChild, + ElementRef, + EventEmitter, + Input, + OnChanges, + Output, + SimpleChanges, + TemplateRef, + ViewChild, +} from '@angular/core'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { CommonModule } from '@angular/common'; +import { ScrollingModule, CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { ListRange } from '@angular/cdk/collections'; +import { Subscription } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; + +@Component({ + selector: 'app-accessible-table', + standalone: true, // Make the component standalone + imports: [ScrollingModule, CommonModule, MatCheckboxModule], + templateUrl: './accessible-table.component.html', + styleUrls: ['./accessible-table.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AccessibleTableComponent implements OnDestroy, AfterViewInit, OnChanges { + @ContentChild('tbody', { read: TemplateRef }) tbodyTemplate!: TemplateRef | null; + @ContentChild('thead', { read: TemplateRef }) theadTemplate!: TemplateRef | null; + + @ViewChild(CdkVirtualScrollViewport) viewport: CdkVirtualScrollViewport; + + @Output() renderedRangeChange = new EventEmitter(); + @Input() items: any[] = []; + + @Input() scrollToIndex: null | number = null + + firstRowHeight: number = 100; + + private renderedRangeSub!: Subscription; + + constructor( + private elementRef: ElementRef, + ) { } + + ngAfterViewInit() { + this.renderedRangeSub = this.viewport.renderedRangeStream + .pipe(debounceTime(500)) + .subscribe(range => { + this.renderedRangeChange.emit(range) + }); + + setTimeout(() => { + this.updateFirstRowHeight(); + }, 1000) + } + + ngOnDestroy(): void { + this.renderedRangeSub?.unsubscribe(); + } + + ngOnChanges(changes: SimpleChanges): void { + this.updateFirstRowHeight() + + if (changes.scrollToIndex && this.items.length > this.scrollToIndex) { + this.doScrollToIndex(this.scrollToIndex) + } + } + + trackBy(index: number) { + return index; + } + + doScrollToIndex(index: number) { + return this.viewport?.scrollToIndex(index, 'smooth'); + } + + private updateFirstRowHeight(): void { + this.firstRowHeight = this + .elementRef + .nativeElement + .parentElement + ?.querySelector('tbody') + ?.offsetHeight + || this.firstRowHeight; + } +} diff --git a/src/app/app.component.html b/src/app/app.component.html index af34b26b4..b3a8f5844 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -6,7 +6,7 @@ fixedTopGap="0" id="sideMenu" appResizable> - + @@ -182,7 +182,7 @@

No Message Selected

- +

@@ -232,12 +232,12 @@

No Message Selected

- + - +
- +
- + @@ -361,7 +361,7 @@

No Message Selected

Unread only
- + No Message Selected
-
- -
+ +
+ + + + + + + + + + + + + Date + + + + + + {{ + selectedFolder !== messagelistservice.sentFolderName ? "From" : "To" + }} + + + + + + + Subject + + + + + + Size + + + + + + + Folder + + + + Attachments + Answered + Flagged + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{item.messageDate}} + + + + {{item.from}} + + + + {{item.subject}} + + + {{item.size | humanBytes}} + + + {{item.folder}} + + + + + + + + + + + + + + + + + + {{item.plaintext}} + + + + + + +
No Message Selected [messageActionsHandler]="messageActionsHandler" (afterLoadMessage)="updateMessageListHeight()" (orientationChangeRequest)="mailViewerOrientationChangeRequest($event)" - (onClose)="singleMailViewerClosed($event)"> + (onClose)="singleMailViewerClosed()">
@@ -442,7 +655,7 @@

No Message Selected

- +
+ + + + mail Moving {{rowsSelectionModel.selected.length}} + + +
+ + diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 1150ca33e..e541cd850 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -2,6 +2,10 @@ border-right: 1px solid darkgrey !important; } +canvastablecontainer { + opacity: 0; +} + #rightPane { border-left: 1px solid darkgrey !important; } @@ -68,3 +72,125 @@ width: 150px; } +.resizable { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.messages-table { + --checked-color: #eee; + --focussed-color: #ddd; + --selected-color: #ccc; + --border-color: #ddd; + + --cell-y-spacing: 0.3lh; + + & .checkbox-cell { + width: 2rem; + text-align: center; + } + + & th { + text-align: left; + position: relative; + } + + & td { + padding-bottom: var(--cell-y-spacing); + } + + & td, + & th { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + + & tbody tr:first-child td { + padding-top: var(--cell-y-spacing); + } + + & tbody { + cursor: pointer; + } + + & tbody.checked { + background-color: var(--checked-color); + } + + & tbody.focussed { + background-color: var(--focussed-color); + } + + & tbody:hover, + & tbody.selected { + background-color: var(--selected-color); + } + + & .preview { + display: block; + height: 1lh; + } + + @media (max-width: 60rem) { + & { + display: block; + } + + & thead { + display: none; + } + + & tbody { + display: block; + } + + td.sm-hidden { + display: none; + } + + & tr { + margin-left: 8px; + display: flex; + flex-flow: row wrap; + gap: 0.5rem; + } + + & td { + margin-top: auto; + margin-bottom: auto; + + &.subject { + width: 100%; + } + } + } + +} + +.skeleton-bone { + content: ' '; + display: inline-block; + width: 100%; + /* Adjust based on the required placeholder size */ + height: 1em; + /* Adjust for height */ + background: linear-gradient(90deg, #e0e0e0 25%, #f8f8f8 50%, #e0e0e0 75%); + background-size: 200% 100%; + animation: skeleton-loading 1.5s infinite; + border-radius: 4px; + /* Optional for rounded edges */ +} + +@keyframes skeleton-loading { + 0% { + background-position: 200% 0; + } + + 100% { + background-position: -200% 0; + } +} + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 48be26b95..72bf60c83 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -17,7 +17,7 @@ // along with Runbox 7. If not, see . // ---------- END RUNBOX LICENSE ---------- -import { AfterViewInit, Component, DoCheck, NgZone, OnInit, ViewChild, Renderer2, ChangeDetectorRef, ElementRef } from '@angular/core'; +import { AfterViewInit, Component, DoCheck, NgZone, OnInit, ViewChild, Renderer2, ChangeDetectorRef, ElementRef, HostListener } from '@angular/core'; import { CanvasTableSelectListener, CanvasTableComponent, CanvasTableContainerComponent @@ -48,7 +48,7 @@ import { WebSocketSearchService } from './websocketsearch/websocketsearch.servic import { WebSocketSearchMailList } from './websocketsearch/websocketsearchmaillist'; import { BUILD_TIMESTAMP } from './buildtimestamp'; -import { from, Observable } from 'rxjs'; +import { from, Observable, BehaviorSubject } from 'rxjs'; import { xapianLoadedSubject } from './xapian/xapianwebloader'; import { SwPush } from '@angular/service-worker'; import { exportKeysFromJWK } from './webpush/vapid.tools'; @@ -65,6 +65,9 @@ import { StorageService } from './storage.service'; import { SearchMessageDisplay } from './xapian/searchmessagedisplay'; import { UsageReportsService } from './common/usage-reports.service'; import { objectEqualWithKeys } from './common/util'; +import { FilterSelectionModel } from './models/filter-selection-model'; +import { BindableSelectionModel } from './models/bindable-selection-model'; +import { Direction } from './sort-button/sort-button.component'; const LOCAL_STORAGE_SETTING_MAILVIEWER_ON_RIGHT_SIDE_IF_MOBILE = 'mailViewerOnRightSideIfMobile'; const LOCAL_STORAGE_SETTING_MAILVIEWER_ON_RIGHT_SIDE = 'mailViewerOnRightSide'; @@ -81,12 +84,40 @@ const TOOLBAR_LIST_BUTTON_WIDTH = 30; // eslint-disable-next-line @angular-eslint/component-selector selector: 'app', styleUrls: ['app.component.scss'], - templateUrl: 'app.component.html' + templateUrl: 'app.component.html', }) export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectListener, DoCheck { showSelectOperations: boolean; showSelectMarkOpMenu: boolean; + + rows = []; + + private rowsSubject= new BehaviorSubject(this.rows); + debouncedRows$ = this.rowsSubject.asObservable().pipe(debounceTime(300)); + + lastCheckedIndex: number = null; + scrollToIndex: number = 0; + rowSelectionModel = new FilterSelectionModel( + false, + [], + false, + messagesEqual, + hasId + ); + rowsSelectionModel = new FilterSelectionModel( + true, + [], + false, + messagesEqual, + hasId + ); + orderSelectionModel = new BindableSelectionModel( + false, + [], + true, + ) + lastSearchText = ''; searchText = ''; dataReady = false; @@ -103,6 +134,8 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis localSearchIndexPrompted = false; offerInitialLocalIndex = false; + dragEvent: DragEvent | null = null + indexDocCount = 0; entireHistoryInProgress = false; @@ -153,6 +186,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis messageActionsHandler: RMM7MessageActions = new RMM7MessageActions(); + dynamicSearchFieldPlaceHolder: string; numHistoryChunksProcessed = 0; @@ -164,8 +198,12 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis xapianLoaded = xapianLoadedSubject; morelistbuttonindex = 7; + renderedRange = {start: 0, end: 0}; // First ten messages. + + widths = {}; - constructor(public searchService: SearchService, + constructor( + public searchService: SearchService, public rmmapi: RunboxWebmailAPI, public rmm: RMM, public snackBar: MatSnackBar, @@ -191,21 +229,20 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis private savedSearchService: SavedSearchesService, private usage: UsageReportsService, ) { - this.hotkeysService.add( - new Hotkey(['j', 'k'], - (event: KeyboardEvent, combo: string): ExtendedKeyboardEvent => { - if (combo === 'k') { - this.canvastable.scrollUp(); - combo = null; - } - if (combo === 'j') { - this.canvastable.scrollDown(); - } - const e: ExtendedKeyboardEvent = event; - e.returnValue = false; - return e; - }) - ); + this.orderSelectionModel.selectionModel.changed.subscribe(() => { + const {data: column, direction} = this.orderSelectionModel.selected + + if (direction === Direction.None) { + this.canvastablecontainer.sortColumn = 2; + this.canvastablecontainer.sortDescending = true; + } else { + this.canvastablecontainer.sortColumn = column; + this.canvastablecontainer.sortDescending = Direction.Descending === direction; + } + + this.updateSearch(true) + }) + this.hotkeysService.add( new Hotkey( 'up up down down left right left right b a', @@ -365,8 +402,10 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis if (this.preferences.has(`${this.preferenceService.prefGroup}:canvasNamedColumnWidthsBySet`)) { this.canvastable.columnWidths = this.preferences.get(`${this.preferenceService.prefGroup}:canvasNamedColumnWidthsBySet`) || {}; } - this.canvastablecontainer.sortColumn = 2; - this.canvastablecontainer.sortDescending = true; + this.orderSelectionModel.selected = { + data: 2, + direction: Direction.Descending + } this.resetColumns(); this.messagelistservice.messagesInViewSubject.subscribe(res => { @@ -484,7 +523,9 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis if ('serviceWorker' in navigator) { try { Notification.requestPermission(); - } catch (e) {} + } catch (e) { + console.error(e) + } } this.subscribeToNotifications(); @@ -612,7 +653,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis true, spamFolderName ).toPromise(); - const messageIds = messageLists.map(msg => msg.id); + const messageIds = messageLists.map(idValue); this.messageActionsHandler.updateMessages({ messageIds: messageIds, updateLocal: (msgIds: number[]) => this.messagelistservice.moveMessages(msgIds, this.messagelistservice.trashFolderName), @@ -671,7 +712,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis public trainSpam(params) { const msg = params.is_spam ? 'Reporting spam' : 'Reporting not spam'; const snackBarRef = this.snackBar.open( msg ); - const unfilteredMessageIds = this.canvastable.rows.selectedMessageIds(); + const unfilteredMessageIds = this.selectedMessageIds; // ensure valid IDs const messageIds = unfilteredMessageIds.filter(id => Number.isInteger(id)); @@ -742,7 +783,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis public setReadStatus(status: boolean) { const snackBarRef = this.snackBar.open('Toggling read status...'); - const messageIds = this.canvastable.rows.selectedMessageIds(); + const messageIds = this.selectedMessageIds; this.messageActionsHandler.updateMessages({ messageIds: messageIds, @@ -753,7 +794,6 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis new MessageFlagChange(id, status, null) ); }); - this.clearSelection(); if (this.singlemailviewer && messageIds.find((id) => id === this.singlemailviewer.messageId)) { this.singlemailviewer.mailObj.seen_flag = status ? 1 : 0; } @@ -775,7 +815,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis public setFlaggedStatus(status: boolean) { const snackBarRef = this.snackBar.open('Toggling flags...'); - const messageIds = this.canvastable.rows.selectedMessageIds(); + const messageIds = this.selectedMessageIds; this.messageActionsHandler.updateMessages({ messageIds: messageIds, @@ -786,7 +826,6 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis new MessageFlagChange(id, null, status) ); }); - this.clearSelection(); if (this.singlemailviewer && messageIds.find((id) => id === this.singlemailviewer.messageId)) { this.singlemailviewer.mailObj.flagged_flag = status ? 1 : 0; } @@ -809,7 +848,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis // Delete selected messages in current canvastable view // If looking at Trash, this will be "delete permanently" public deleteMessages() { - const messageIds = this.canvastable.rows.selectedMessageIds(); + const messageIds = this.selectedMessageIds; this.messageActionsHandler.updateMessages({ messageIds: messageIds, @@ -886,6 +925,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis this.selectMessageFromFragment(this.fragment); } } + this.filterMessageDisplay(); // FIXME: looks weird, should probably rename "rows" to "messagedisplay" @@ -897,6 +937,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis // NB this triggers hasChanged for us and forces a redraw this.canvastable.columns = this.canvastable.rows.getCanvasTableColumns(this); + this.updateRows() } public filterMessageDisplay() { @@ -910,12 +951,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis } public clearSelection() { - if (this.canvastable.rows) { - this.canvastable.rows.clearSelection(); - } - this.canvastable.hasChanges = true; - this.showSelectOperations = false; - this.showSelectMarkOpMenu = false; + this.rowsSelectionModel.clear() } public selectRowByMessageId(messageId: number) { @@ -930,6 +966,14 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis public rowSelected(rowIndex: number, columnIndex: number, multiSelect?: boolean) { const isSelect = (columnIndex === 0) || multiSelect + this.rowSelectionModel.select(this.rows[rowIndex]) + this.lastCheckedIndex = rowIndex + // For some reason this needs a timeout. + + setTimeout(() => { + this.scrollToIndex = rowIndex + }, 1000) + if ((this.selectedFolder === this.messagelistservice.templateFolderName) && !isSelect) { this.draftDeskService.newTemplateDraft( this.canvastable.rows.getRowMessageId(rowIndex) @@ -1059,7 +1103,7 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis this.offerInitialLocalIndex = false; } - singleMailViewerClosed(action: string): void { + singleMailViewerClosed(): void { this.canvastable.rows.clearOpenedRow(); this.updateUrlFragment(); } @@ -1088,8 +1132,21 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis } } + onMessagesDragStart(event: DragEvent, row) { + + // If no messages are selected we'll select the current message + if (this.rowsSelectionModel.isEmpty()) { + this.rowsSelectionModel.select(row) + } + + // Remove the default image + event.dataTransfer?.setDragImage(new Image(), 0, 0); // Set an empty image + + this.dragEvent = event + } + dropToFolder(folderId): void { - const messageIds = this.canvastable.rows.selectedMessageIds(); + const messageIds = this.selectedMessageIds this.messageActionsHandler.updateMessages({ messageIds: messageIds, @@ -1122,9 +1179,8 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis public moveToFolder() { const dialogRef: MatDialogRef = this.dialog.open(MoveMessageDialogComponent); // dialogRef.componentInstance.messageActionsHandler = this.messageActionsHandler; - const messageIds = this.canvastable.rows.selectedMessageIds(); + const messageIds = this.selectedMessageIds; - console.log('selected messages', messageIds); // dialogRef.componentInstance.selectedMessageIds = messageIds; dialogRef.afterClosed().subscribe(folder => { if (folder) { @@ -1366,7 +1422,9 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis this.canvastable.scrollTop(); } } - } catch (e) { } + } catch (e) { + console.error(e) + } } } @@ -1438,6 +1496,133 @@ export class AppComponent implements OnInit, AfterViewInit, CanvasTableSelectLis this.router.navigate(['/'], { fragment }); } } + + get selectedMessageIds() { + return this.rowsSelectionModel.isEmpty() + ? this.rowSelectionModel.selected.map(idValue) + : this.rowsSelectionModel.selected.map(idValue) + } + + updateRows() { + this.rows = [...this.canvastable?.rows?.rows] ?? [] + + console.log(this.rows) + + return this.enrichRows() + } + + async enrichRows() { + const { start, end } = this.renderedRange; + + for (let index = start; index < end; index++) { + if (index >= this.rows.length) break + + this.rows[index] = this.canvastable.rows.getRowData(index, this) + this.rows[index].loaded = true + } + + this.rows = Object.create(this.rows) + this.rowsSubject.next(this.rows) + } + + rangeSelectFrom(from: number, to: number, check: boolean) { + const left = Math.min(from, to) + const right = Math.max(from, to) + + for (let i = left; i <= right; i++) { + if (check) { + this.rowsSelectionModel.select(this.rows[i]) + } else { + this.rowsSelectionModel.deselect(this.rows[i]) + } + } + + this.lastCheckedIndex = to + + } + + onCheckboxClick(event, row, index) { + this.onRowClick(event, row, index, true) + event.stopPropagation() + event.preventDefault() + } + + rangeSelect(to: number, check: boolean) { + let from = this.lastCheckedIndex; + + // When nothing is selected yet. + if (from === -1) return this.oneSelect(to, check) + + + return this.rangeSelectFrom(from, to, check) + } + + oneSelect(index, check) { + this.rangeSelectFrom(index, index, check) + } + + onRowClick(event, row, index, checkbox = false) { + const shiftKey = event.getModifierState("Shift") + const check = !this.rowsSelectionModel.isSelected(this.rows[index]) + + if (shiftKey) { + return this.rangeSelect(index, check) + } + + const ctrlKey = event.getModifierState("Control") + const metaKey = event.getModifierState("Meta") + + if (ctrlKey || metaKey) { + return this.oneSelect(index, check) + } + + if (!checkbox) { + // Deselect an email when clicking on a selected email. + if (this.rowSelectionModel.isSelected(this.canvastable.rows.rows[index])) { + this.singlemailviewer.messageId = null; + this.rowSelectionModel.clear() + return this.singleMailViewerClosed() + } + + return this.rowSelected(this.rows.indexOf(row), 3, false); + } + + this.oneSelect(index, check) + } + + onRowKeydown(event, row, index) { + // Only work on Enter and space. + if (event.key !== 'Enter') return; + + return this.onRowClick(event, row, index) + } + + onAllCheckboxChange() { + if (this.rowsSelectionModel.isEmpty()) { + this.rowsSelectionModel.select(...this.rows); + } else { + this.rowsSelectionModel.deselect(...this.rows); + } + } + + get allItemsSelected() { + return this.rowsSelectionModel.selected.length === this.rows.length + } + + @HostListener('document:dragend', ['$event']) + onDragEnded() { + delete this.dragEvent; + } + + // TODO: The this.rows can change after a onRenderedRangeChange is called. + // This will drop the resolved values. + onRenderedRangeChange(event) { + this.renderedRange = event; + this.enrichRows() + } + } -// vim: set shiftwidth=2 +const idValue = (x: any) => x.id +const messagesEqual = (a: any, b: any) => a?.id === b?.id +const hasId = (x: any) => Boolean(x?.id) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index de2216dad..12af37a34 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -52,6 +52,7 @@ import { MatLegacySnackBarModule as MatSnackBarModule } from '@angular/material/ import { MatToolbarModule } from '@angular/material/toolbar'; import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-tooltip'; import { CanvasTableModule } from './canvastable/canvastable'; +import { AccessibleTableComponent } from './accessible-table/accessible-table.component' import { MoveMessageDialogComponent } from './actions/movemessage.action'; import { RunboxWebmailAPI } from './rmmapi/rbwebmail'; import { RMMOfflineService } from './rmmapi/rmmoffline.service'; @@ -88,7 +89,11 @@ import { SavedSearchesService } from './saved-searches/saved-searches.service'; import { HelpComponent } from './help/help.component'; import { HelpModule } from './help/help.module'; import { DomainRegisterRedirectComponent } from './domainregister/domreg-redirect.component'; - +import { HumanBytesPipe } from './human-bytes.pipe'; +import { FollowsMouseComponent } from './follows-mouse/follows-mouse.component'; +import { DatePipe } from '@angular/common'; +import { ResizableButtonComponent } from './resizable-button/resizable-button.component'; +import { SortButtonComponent } from './sort-button/sort-button.component'; window.addEventListener('dragover', (event) => event.preventDefault()); window.addEventListener('drop', (event) => event.preventDefault()); @@ -141,7 +146,11 @@ const routes: Routes = [ @NgModule({ imports: [BrowserModule, FormsModule, + DatePipe, + ResizableButtonComponent, + SortButtonComponent, HttpClientModule, + AccessibleTableComponent, HttpClientJsonpModule, CanvasTableModule, ComposeModule, @@ -180,7 +189,9 @@ const routes: Routes = [ RunboxCommonModule, RouterModule.forRoot(routes), ServiceWorkerModule.register('/app/ngsw-worker.js', { enabled: environment.production }), - HotkeyModule.forRoot() + HotkeyModule.forRoot(), + HumanBytesPipe, + FollowsMouseComponent ], exports: [], declarations: [MainContainerComponent, AppComponent, diff --git a/src/app/canvastable/canvastable.ts b/src/app/canvastable/canvastable.ts index 0186e032d..468ad5793 100644 --- a/src/app/canvastable/canvastable.ts +++ b/src/app/canvastable/canvastable.ts @@ -24,9 +24,9 @@ import { NgModule, Component, AfterViewInit, - Input, Output, Renderer2, + Input, Output, ElementRef, - DoCheck, NgZone, EventEmitter, OnInit, ViewChild + EventEmitter, OnInit, ViewChild } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button'; @@ -83,7 +83,7 @@ export namespace CanvasTable { moduleId: 'angular2/app/canvastable/', templateUrl: 'canvastable.component.html' }) -export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { +export class CanvasTableComponent implements AfterViewInit, OnInit { static incrementalId = 1; public elementId: string; private _topindex = 0.0; @@ -110,14 +110,14 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { private canv: HTMLCanvasElement; - private ctx: CanvasRenderingContext2D; - private wantedCanvasWidth = 300; - private wantedCanvasHeight = 300; + // private ctx: CanvasRenderingContext2D; + // private wantedCanvasWidth = 300; + // private wantedCanvasHeight = 300; private _rowheight = 28; - private fontheight = 14; - private fontheightSmall = 13; - private fontheightSmaller = 12; + // private fontheight = 14; + // private fontheightSmall = 13; + // private fontheightSmaller = 12; private scrollbarwidth = 12; @@ -133,8 +133,6 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { columnResizeInProgress = false; private scrollbarArea = false; - private jumpToMessage = false; - visibleColumnSeparatorAlpha = 0; visibleColumnSeparatorIndex = 0; lastClientY: number; @@ -195,7 +193,7 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { } } - private dragSelectionDirectionIsDown: Boolean = null; + // private dragSelectionDirectionIsDown: Boolean = null; // Auto row wrap mode (width based on iphone 5) - set to 0 to disable row wrap mode public autoRowWrapModeWidth = 540; @@ -208,7 +206,7 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { public hasChanges: boolean; - private formattedValueCache: { [key: string]: string; } = {}; + // private formattedValueCache: { [key: string]: string; } = {}; public scrollLimitHit: BehaviorSubject = new BehaviorSubject(0); @@ -222,21 +220,22 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { // Are we selecting all rows, or just the visible ones? public selectWhichRows = CanvasTable.RowSelect.Visible; - constructor(elementRef: ElementRef, private renderer: Renderer2, private _ngZone: NgZone) { + constructor(elementRef: ElementRef) { } - ngDoCheck() { - if (this.canv) { + // No need to track changes. + // ngDoCheck() { + // if (this.canv) { - const devicePixelRatio = window.devicePixelRatio ? window.devicePixelRatio : 1; - this.wantedCanvasWidth = this.canv.parentElement.parentElement.clientWidth * devicePixelRatio; - this.wantedCanvasHeight = this.canv.parentElement.parentElement.clientHeight * devicePixelRatio; + // const devicePixelRatio = window.devicePixelRatio ? window.devicePixelRatio : 1; + // this.wantedCanvasWidth = this.canv.parentElement.parentElement.clientWidth * devicePixelRatio; + // this.wantedCanvasHeight = this.canv.parentElement.parentElement.clientHeight * devicePixelRatio; - if (this.canv.width !== this.wantedCanvasWidth || this.canv.height !== this.wantedCanvasHeight) { - this.hasChanges = true; - } - } - } + // if (this.canv.width !== this.wantedCanvasWidth || this.canv.height !== this.wantedCanvasHeight) { + // this.hasChanges = true; + // } + // } + // } private calculateColumnWidths(columns: CanvasTableColumn[]) { const colWidthSet = columns.map((col) => col.name).filter((cname) => cname.length > 0).join(','); @@ -254,7 +253,7 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { ngAfterViewInit() { this.canv = this.canvRef.nativeElement; - this.ctx = this.canv.getContext('2d'); + // this.ctx = this.canv.getContext('2d'); this.canv.onwheel = (event: WheelEvent) => { event.preventDefault(); @@ -273,7 +272,7 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { break; } - this.enforceScrollLimit(); + // this.enforceScrollLimit(); }; /** @@ -330,7 +329,7 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { } // Reset drag select direction - this.dragSelectionDirectionIsDown = null; + // this.dragSelectionDirectionIsDown = null; }; let previousTouchY: number; @@ -378,7 +377,7 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { previousTouchY = newTouchY; previousTouchX = newTouchX; } - this.enforceScrollLimit(); + // this.enforceScrollLimit(); this.touchscroll.emit(this.horizScroll); } @@ -398,116 +397,117 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { } }); - this.renderer.listen('window', 'mousemove', (event: MouseEvent) => { - if (this.scrollbarDragInProgress === true) { - event.preventDefault(); - this.doScrollBarDrag(event.clientY); - } - }); - - this.canv.onmousemove = (event: MouseEvent) => { - if (this.scrollbarDragInProgress === true || this.columnResizeInProgress === true) { - event.preventDefault(); - return; - } - - const canvrect = this.canv.getBoundingClientRect(); - const clientX = event.clientX - canvrect.left; - - let newHoverRowIndex = this.getRowIndexByClientY(event.clientY); - if (this.scrollbarDragInProgress || checkIfScrollbarArea(event.clientX, event.clientY, true)) { - newHoverRowIndex = null; - } - - if (this.hoverRowIndex !== newHoverRowIndex) { - // check if mouse is down - if (this.lastMouseDownEvent) { - // set drag select direction to true if down, or false if up - const newDragSelectionDirectionIsDown = newHoverRowIndex > this.hoverRowIndex ? true : false; - - if (this.dragSelectionDirectionIsDown !== newDragSelectionDirectionIsDown) { - // select previous row on drag select direction change - this.selectRowByIndex(this.lastMouseDownEvent.clientX, this.hoverRowIndex); - this.dragSelectionDirectionIsDown = newDragSelectionDirectionIsDown; - } - let rowIndex = this.hoverRowIndex; - // Select all rows between the previous and current hover row index - while ( - (newDragSelectionDirectionIsDown === true && rowIndex < newHoverRowIndex) || - (newDragSelectionDirectionIsDown === false && rowIndex > newHoverRowIndex) - ) { - if (newDragSelectionDirectionIsDown === true) { - rowIndex ++; - } else { - rowIndex --; - } - this.selectRowByIndex(this.lastMouseDownEvent.clientX, rowIndex); - } - } - this.hoverRowIndex = newHoverRowIndex; - } - - if (this.dragSelectionDirectionIsDown === null) { - // Check for column resize - if (this.lastMouseDownEvent && this.visibleColumnSeparatorIndex > 0) { - this.columnresize.emit(this.visibleColumnSeparatorIndex); - } else { - this.updateVisibleColumnSeparatorIndex(clientX); - } - - if (this.visibleColumnSeparatorIndex > 0) { - this.lastClientY = event.clientY - canvrect.top; - this.hasChanges = true; - return; - } - } - - if (this.dragSelectionDirectionIsDown === null && this.hoverRowIndex !== null) { - const colIndex = this.getColIndexByClientX(clientX); - let colStartX = this.columns.reduce((prev, curr, ndx) => ndx < colIndex ? prev + curr.width : prev, 0); - - let tooltipText: string | ((rowIndex: any) => string) = - this.columns[colIndex] && this.columns[colIndex].tooltipText; - - // FIXME: message display class - if (typeof tooltipText === 'function' && this.rows.rowExists(this.hoverRowIndex)) { - tooltipText = tooltipText(this.hoverRowIndex); - } - - if (!event.shiftKey && !this.lastMouseDownEvent && - (tooltipText || (this.columns[colIndex] && this.columns[colIndex].draggable)) - ) { - if (this.rowWrapMode && - colIndex >= this.rowWrapModeWrapColumn) { - // Subtract first row width if in row wrap mode - colStartX -= this.columns.reduce((prev, curr, ndx) => - ndx < this.rowWrapModeWrapColumn ? prev + curr.width : prev, 0); - } - - this.floatingTooltip = new FloatingTooltip( - (this.hoverRowIndex - this.topindex) * this.rowheight, - colStartX - this.horizScroll + this.colpaddingleft, - this.columns[colIndex].width - this.colpaddingright - this.colpaddingleft, - this.rowheight, tooltipText as string); - - if (this.rowWrapMode) { - this.floatingTooltip.top += - + (colIndex >= this.rowWrapModeWrapColumn ? this.rowheight / 2 : 0); - this.floatingTooltip.height = this.rowheight / 2; - } - - setTimeout(() => { - if (this.columnOverlay) { - this.columnOverlay.show(300); - } - }, 0); - } else { - this.floatingTooltip = null; - } - } else { - this.floatingTooltip = null; - } - }; + // this.renderer.listen('window', 'mousemove', (event: MouseEvent) => { + // if (this.scrollbarDragInProgress === true) { + // event.preventDefault(); + // this.doScrollBarDrag(event.clientY); + // } + // }); + + // this.canv.onmousemove = (event: MouseEvent) => { + // if (this.scrollbarDragInProgress === true || this.columnResizeInProgress === true) { + // event.preventDefault(); + // return; + // } + + // const canvrect = this.canv.getBoundingClientRect(); + // const clientX = event.clientX - canvrect.left; + + // let newHoverRowIndex = this.getRowIndexByClientY(event.clientY); + // if (this.scrollbarDragInProgress || checkIfScrollbarArea(event.clientX, event.clientY, true)) { + // newHoverRowIndex = null; + // } + + // if (this.hoverRowIndex !== newHoverRowIndex) { + // // check if mouse is down + // if (this.lastMouseDownEvent) { + // // set drag select direction to true if down, or false if up + // const newDragSelectionDirectionIsDown = newHoverRowIndex > this.hoverRowIndex ? true : false; + + // if (this.dragSelectionDirectionIsDown !== newDragSelectionDirectionIsDown) { + // // select previous row on drag select direction change + // this.selectRowByIndex(this.lastMouseDownEvent.clientX, this.hoverRowIndex); + // this.dragSelectionDirectionIsDown = newDragSelectionDirectionIsDown; + // } + // let rowIndex = this.hoverRowIndex; + // // Select all rows between the previous and current hover row index + // while ( + // (newDragSelectionDirectionIsDown === true && rowIndex < newHoverRowIndex) || + // (newDragSelectionDirectionIsDown === false && rowIndex > newHoverRowIndex) + // ) { + // if (newDragSelectionDirectionIsDown === true) { + // rowIndex ++; + // } else { + // rowIndex --; + // } + // this.selectRowByIndex(this.lastMouseDownEvent.clientX, rowIndex); + // } + // } + // this.hoverRowIndex = newHoverRowIndex; + // this.updateDragImage(newHoverRowIndex); + // } + + // if (this.dragSelectionDirectionIsDown === null) { + // // Check for column resize + // if (this.lastMouseDownEvent && this.visibleColumnSeparatorIndex > 0) { + // this.columnresize.emit(this.visibleColumnSeparatorIndex); + // } else { + // this.updateVisibleColumnSeparatorIndex(clientX); + // } + + // if (this.visibleColumnSeparatorIndex > 0) { + // this.lastClientY = event.clientY - canvrect.top; + // this.hasChanges = true; + // return; + // } + // } + + // if (this.dragSelectionDirectionIsDown === null && this.hoverRowIndex !== null) { + // const colIndex = this.getColIndexByClientX(clientX); + // let colStartX = this.columns.reduce((prev, curr, ndx) => ndx < colIndex ? prev + curr.width : prev, 0); + + // let tooltipText: string | ((rowIndex: any) => string) = + // this.columns[colIndex] && this.columns[colIndex].tooltipText; + + // // FIXME: message display class + // if (typeof tooltipText === 'function' && this.rows.rowExists(this.hoverRowIndex)) { + // tooltipText = tooltipText(this.hoverRowIndex); + // } + + // if (!event.shiftKey && !this.lastMouseDownEvent && + // (tooltipText || (this.columns[colIndex] && this.columns[colIndex].draggable)) + // ) { + // if (this.rowWrapMode && + // colIndex >= this.rowWrapModeWrapColumn) { + // // Subtract first row width if in row wrap mode + // colStartX -= this.columns.reduce((prev, curr, ndx) => + // ndx < this.rowWrapModeWrapColumn ? prev + curr.width : prev, 0); + // } + + // this.floatingTooltip = new FloatingTooltip( + // (this.hoverRowIndex - this.topindex) * this.rowheight, + // colStartX - this.horizScroll + this.colpaddingleft, + // this.columns[colIndex].width - this.colpaddingright - this.colpaddingleft, + // this.rowheight, tooltipText as string); + + // if (this.rowWrapMode) { + // this.floatingTooltip.top += + // + (colIndex >= this.rowWrapModeWrapColumn ? this.rowheight / 2 : 0); + // this.floatingTooltip.height = this.rowheight / 2; + // } + + // setTimeout(() => { + // if (this.columnOverlay) { + // this.columnOverlay.show(300); + // } + // }, 0); + // } else { + // this.floatingTooltip = null; + // } + // } else { + // this.floatingTooltip = null; + // } + // }; this.canv.onmouseout = (event: MouseEvent) => { const newHoverRowIndex = null; @@ -516,13 +516,13 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { } }; - this.renderer.listen('window', 'mouseup', (event: MouseEvent) => { - this.lastMouseDownEvent = undefined; - if (this.scrollbarDragInProgress) { - this.scrollbarDragInProgress = false; - this.hasChanges = true; - } - }); + // this.renderer.listen('window', 'mouseup', (event: MouseEvent) => { + // this.lastMouseDownEvent = undefined; + // if (this.scrollbarDragInProgress) { + // this.scrollbarDragInProgress = false; + // this.hasChanges = true; + // } + // }); this.canv.onmouseup = (event: MouseEvent) => { event.preventDefault(); @@ -539,56 +539,56 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { } this.lastMouseDownEvent = null; - this.dragSelectionDirectionIsDown = null; + // this.dragSelectionDirectionIsDown = null; }; - this.renderer.listen('window', 'resize', () => true); - - const paintLoop = () => { - if (this.hasChanges) { - if (Math.abs(this.touchScrollSpeedY) > 0) { - // Scroll if speed - this.topindex -= this.touchScrollSpeedY / this.rowheight; - - // ---- Enforce scroll limit - if (this.topindex < 0) { - this.topindex = 0; - } else if (this.rows.rowCount() < this.maxVisibleRows) { - this.topindex = 0; - } else if (this.topindex + this.maxVisibleRows > this.rows.rowCount()) { - this.topindex = this.rows.rowCount() - this.maxVisibleRows; - } - // --------- - - // Slow down - this.touchScrollSpeedY *= 0.9; - if (Math.abs(this.touchScrollSpeedY) < 0.4) { - this.touchScrollSpeedY = 0; - } - } - try { - this.dopaint(); - if (this.rows) { - this.repaintDoneSubject.next(undefined); - } - } catch (e) { - console.log(e); - } - - if (Math.abs(this.touchScrollSpeedY) > 0) { - // Continue scrolling while we have scroll speed - this.hasChanges = true; - } else { - this.hasChanges = false; - } - } - window.requestAnimationFrame(() => paintLoop()); - }; - - this._ngZone.runOutsideAngular(() => - window.requestAnimationFrame(() => paintLoop()) - ); + // this.renderer.listen('window', 'resize', () => true); + + // const paintLoop = () => { + // if (this.hasChanges) { + // if (Math.abs(this.touchScrollSpeedY) > 0) { + // // Scroll if speed + // this.topindex -= this.touchScrollSpeedY / this.rowheight; + + // // ---- Enforce scroll limit + // if (this.topindex < 0) { + // this.topindex = 0; + // } else if (this.rows.rowCount() < this.maxVisibleRows) { + // this.topindex = 0; + // } else if (this.topindex + this.maxVisibleRows > this.rows.rowCount()) { + // this.topindex = this.rows.rowCount() - this.maxVisibleRows; + // } + // // --------- + + // // Slow down + // this.touchScrollSpeedY *= 0.9; + // if (Math.abs(this.touchScrollSpeedY) < 0.4) { + // this.touchScrollSpeedY = 0; + // } + // } + // try { + // this.dopaint(); + // if (this.rows) { + // this.repaintDoneSubject.next(undefined); + // } + // } catch (e) { + // console.log(e); + // } + + // if (Math.abs(this.touchScrollSpeedY) > 0) { + // // Continue scrolling while we have scroll speed + // this.hasChanges = true; + // } else { + // this.hasChanges = false; + // } + // } + // // window.requestAnimationFrame(() => paintLoop()); + // }; + + // this._ngZone.runOutsideAngular(() => + // window.requestAnimationFrame(() => paintLoop()) + // ); } private updateDragImage(selectedRowIndex: number) :HTMLCanvasElement { @@ -663,7 +663,7 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { const canvrect = this.canv.getBoundingClientRect(); this.topindex = this.rows.rowCount() * ((clientY - canvrect.top) / this.canv.scrollHeight); - this.enforceScrollLimit(); + // this.enforceScrollLimit(); } private getRowIndexByClientY(clientY: number) { @@ -777,11 +777,14 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { } public autoAdjustColumnWidths(minwidth: number, tryFitScreenWidth = false) { + // Make innert + return + if (!this.canv || this._columns.length === 0) { return; } - const canvasWidth = Math.floor(this.wantedCanvasWidth / window.devicePixelRatio) - this.scrollbarwidth - 2; + const canvasWidth = Math.floor(window.devicePixelRatio) - this.scrollbarwidth - 2; const columnsTotalWidth = () => this.columns.reduce((prev, curr) => prev + curr.width, 0); @@ -826,13 +829,13 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { public scrollUp() { this.topindex--; - this.enforceScrollLimit(); + // this.enforceScrollLimit(); this.hasChanges = true; } public scrollDown() { this.topindex++; - this.enforceScrollLimit(); + // this.enforceScrollLimit(); this.hasChanges = true; } @@ -850,7 +853,7 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { public updateRows(newList) { this.rows.setRows(newList); - this.enforceScrollLimit(); + // this.enforceScrollLimit(); this.hasChanges = true; } @@ -865,33 +868,37 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { // When loading a url with a fragment containing a msg id - scroll to there public jumpToOpenMessage() { - this.jumpToMessage = true; - } - - private enforceScrollLimit() { - if (this.topindex < 0) { - this.topindex = 0; - } else if (this.rows && this.rows.rowCount() < this.maxVisibleRows) { - this.topindex = 0; - } else if (this.rows && this.topindex + this.maxVisibleRows > this.rows.rowCount()) { - this.topindex = this.rows.rowCount() - this.maxVisibleRows; - // send max rows hit events (use to fetch more data) - this.scrollLimitHit.next(this.rows.rowCount()); - } - - - const columnsTotalWidth = this.columns.reduce((width, col) => - col.width + width, 0); - - if (this.horizScroll < 0) { - this.horizScroll = 0; - } else if ( - this.canv.scrollWidth < columnsTotalWidth && - this.horizScroll + this.canv.scrollWidth > columnsTotalWidth) { - this.horizScroll = columnsTotalWidth - this.canv.scrollWidth; + // currently selected row in the centre: + if (this.rows.rowCount() > 0 && this.rows.openedRowIndex) { + this.topindex = this.rows.openedRowIndex - Math.round(this.maxVisibleRows / 2); + // this.enforceScrollLimit(); } } + // private enforceScrollLimit() { + // if (this.topindex < 0) { + // this.topindex = 0; + // } else if (this.rows && this.rows.rowCount() < this.maxVisibleRows) { + // this.topindex = 0; + // } else if (this.rows && this.topindex + this.maxVisibleRows > this.rows.rowCount()) { + // this.topindex = this.rows.rowCount() - this.maxVisibleRows; + // // send max rows hit events (use to fetch more data) + // this.scrollLimitHit.next(this.rows.rowCount()); + // } + + + // const columnsTotalWidth = this.columns.reduce((width, col) => + // col.width + width, 0); + + // if (this.horizScroll < 0) { + // this.horizScroll = 0; + // } else if ( + // this.canv.scrollWidth < columnsTotalWidth && + // this.horizScroll + this.canv.scrollWidth > columnsTotalWidth) { + // this.horizScroll = columnsTotalWidth - this.canv.scrollWidth; + // } + // } + /** * Draws a rounded rectangle using the current state of the canvas. * If you omit the last three params, it will draw a rectangle @@ -910,40 +917,40 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { * @param {Boolean} [fill = false] Whether to fill the rectangle. * @param {Boolean} [stroke = true] Whether to stroke the rectangle. */ - private roundRect(ctx: CanvasRenderingContext2D, x: number, y: number, - width: number, height: number, - radius?: any, fill?: boolean, stroke?: boolean) { - if (typeof stroke === 'undefined') { - stroke = true; - } - if (typeof radius === 'undefined') { - radius = 5; - } - if (typeof radius === 'number') { - radius = { tl: radius, tr: radius, br: radius, bl: radius }; - } else { - const defaultRadius = { tl: 0, tr: 0, br: 0, bl: 0 }; - Object.keys(defaultRadius).forEach(side => - radius[side] = radius[side] || defaultRadius[side]); - } - ctx.beginPath(); - ctx.moveTo(x + radius.tl, y); - ctx.lineTo(x + width - radius.tr, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr); - ctx.lineTo(x + width, y + height - radius.br); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height); - ctx.lineTo(x + radius.bl, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl); - ctx.lineTo(x, y + radius.tl); - ctx.quadraticCurveTo(x, y, x + radius.tl, y); - ctx.closePath(); - if (fill) { - ctx.fill(); - } - if (stroke) { - ctx.stroke(); - } - } + // private roundRect(ctx: CanvasRenderingContext2D, x: number, y: number, + // width: number, height: number, + // radius?: any, fill?: boolean, stroke?: boolean) { + // if (typeof stroke === 'undefined') { + // stroke = true; + // } + // if (typeof radius === 'undefined') { + // radius = 5; + // } + // if (typeof radius === 'number') { + // radius = { tl: radius, tr: radius, br: radius, bl: radius }; + // } else { + // const defaultRadius = { tl: 0, tr: 0, br: 0, bl: 0 }; + // Object.keys(defaultRadius).forEach(side => + // radius[side] = radius[side] || defaultRadius[side]); + // } + // ctx.beginPath(); + // ctx.moveTo(x + radius.tl, y); + // ctx.lineTo(x + width - radius.tr, y); + // ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr); + // ctx.lineTo(x + width, y + height - radius.br); + // ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height); + // ctx.lineTo(x + radius.bl, y + height); + // ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl); + // ctx.lineTo(x, y + radius.tl); + // ctx.quadraticCurveTo(x, y, x + radius.tl, y); + // ctx.closePath(); + // if (fill) { + // ctx.fill(); + // } + // if (stroke) { + // ctx.stroke(); + // } + // } // Height of message list rows public get rowheight(): number { @@ -958,410 +965,403 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { } } - private dopaint() { - const devicePixelRatio = window.devicePixelRatio; - if (this.canv.width !== this.wantedCanvasWidth || - this.canv.height !== this.wantedCanvasHeight) { - - const widthChanged = this.canv.width !== this.wantedCanvasWidth; - /* Only resize on detection of width change - * otherwise reducing column widths so that the scrollbar - * disappears indicates a change of height and triggers resize - */ - - this.canv.style.width = (this.wantedCanvasWidth / devicePixelRatio) + 'px'; - this.canv.style.height = (this.wantedCanvasHeight / devicePixelRatio) + 'px'; - - this.canv.width = this.wantedCanvasWidth; - this.canv.height = this.wantedCanvasHeight; - - this.maxVisibleRows = this.canv.scrollHeight / this.rowheight; - if(this.jumpToMessage) { - // currently selected row in the centre: - if (this.rows.rowCount() > 0 && this.rows.openedRowIndex) { - this.topindex = this.rows.openedRowIndex - Math.round(this.maxVisibleRows / 2); - } - this.jumpToMessage = false; - } - this.enforceScrollLimit(); - this.hasChanges = true; - if (this.canv.clientWidth < this.autoRowWrapModeWidth) { - this.rowWrapMode = true; - } else { - this.rowWrapMode = false; - } - - this.canvasResizedSubject.next(widthChanged); - } - - if (devicePixelRatio !== 1) { - // This is not scale() as that would keep multiplying - // Moved out of above if() statement as something (!?) - // was resetting transform, still not sure what - this.ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0); - } - - this.ctx.textBaseline = 'middle'; - this.ctx.font = this.fontheight + 'px ' + this.fontFamily; - - const canvwidth: number = this.canv.scrollWidth; - const canvheight: number = this.canv.scrollHeight; - - let colx = 0 - this.horizScroll; - // Columns - for (let colindex = 0; colindex < this.columns.length; colindex++) { - const col: CanvasTableColumn = this.columns[colindex]; - if (colx + col.width > 0 && colx < canvwidth) { - this.ctx.fillStyle = col.backgroundColor ? col.backgroundColor : '#fff'; - this.ctx.fillRect(colx, - 0, - colindex === this.columns.length - 1 ? - canvwidth - colx : - col.width, - canvheight - ); - } - colx += col.width; - } - - if (!this.rows || this.rows.rowCount() < 1) { - return; - } - - // Rows - for (let n = this.topindex; n < this.rows.rowCount(); n += 1.0) { - const rowIndex = Math.floor(n); - - if (rowIndex > this.rows.rowCount()) { - break; - } - -// const rowobj = this.rows[rowIndex]; - - const halfrowheight = (this.rowheight / 2); - const rowy = (rowIndex - this.topindex) * this.rowheight; - if (this.rows.rowExists(rowIndex)) { - // Clear row area - // Alternating row colors: - // let rowBgColor : string = (rowIndex%2===0 ? "#e8e8e8" : "rgba(255,255,255,0.7)"); - // Single row color: - let rowBgColor = '#fff'; - - const isBoldRow = this.rows.isBoldRow(rowIndex); - const isSelectedRow = this.rows.isSelectedRow(rowIndex); - const isOpenedRow = this.rows.isOpenedRow(rowIndex); - if (this.hoverRowIndex === rowIndex) { - rowBgColor = this.hoverRowColor; - } - if (isSelectedRow) { - rowBgColor = this.selectedRowColor; - } - if (isOpenedRow) { - rowBgColor = this.openedRowColor; - } - - this.ctx.fillStyle = rowBgColor; - this.ctx.fillRect(0, rowy, canvwidth, this.rowheight); - - // Row borders separating each row - this.ctx.strokeStyle = '#eee'; - this.ctx.beginPath(); - this.ctx.moveTo(0, rowy); - this.ctx.lineTo(canvwidth, rowy); - this.ctx.stroke(); - - let x = 0; - for (let colindex = 0; colindex < this.columns.length; colindex++) { - const col: CanvasTableColumn = this.columns[colindex]; - let val: any = col.getValue(rowIndex); - if (val === 'RETRY') { - // retry later if value is null - setTimeout(() => this.hasChanges = true, 2); - val = ''; - } - let formattedVal: string; - const formattedValueCacheKey: string = col.cacheKey + ':' + val; - if (this.formattedValueCache[formattedValueCacheKey]) { - formattedVal = this.formattedValueCache[formattedValueCacheKey]; - } else if (('' + val).length > 0 && col.getFormattedValue) { - formattedVal = col.getFormattedValue(val); - this.formattedValueCache[formattedValueCacheKey] = formattedVal; - } else { - formattedVal = '' + val; - this.formattedValueCache[formattedValueCacheKey] = formattedVal; - } - if (this.rowWrapMode && col.rowWrapModeHidden) { - continue; - } else if (this.rowWrapMode && col.rowWrapModeChipCounter && parseInt(val, 10) > 1) { - this.ctx.save(); - - this.ctx.strokeStyle = ''; - - this.roundRect(this.ctx, - canvwidth - 50, - rowy + 9, - 28, - 15, 10, false); - this.ctx.font = '10px ' + this.fontFamily; - - this.ctx.strokeStyle = '#000'; - if (isSelectedRow) { - this.ctx.fillStyle = this.textColor; - } else { - this.ctx.fillStyle = this.textColor; - } - this.ctx.textAlign = 'center'; - this.ctx.fillText(formattedVal + '', canvwidth - 36, rowy + halfrowheight - 15); - - this.ctx.restore(); - - continue; - } else if (this.rowWrapMode && col.rowWrapModeChipCounter) { - continue; - } - if (this.rowWrapMode && colindex === this.rowWrapModeWrapColumn) { - x = 0; - } - - x += this.colpaddingleft; - - if ((x - this.horizScroll + col.width) >= 0 && formattedVal.length > 0) { - this.ctx.fillStyle = this.textColor; // Text color of unselected row - if (isSelectedRow) { - this.ctx.fillStyle = this.textColor; // Text color of selected row - } - - if (this.rowWrapMode) { - // Wrap rows if in row wrap mode (for e.g. mobile portrait view) - - // Check box - const texty: number = rowy + halfrowheight; - const textx: number = x - this.horizScroll; - - const width = col.width - this.colpaddingright - this.colpaddingleft; - - this.ctx.save(); - this.ctx.beginPath(); - this.ctx.moveTo(textx, rowy); - this.ctx.lineTo(textx + width, rowy); - this.ctx.lineTo(textx + width, rowy + this.rowheight); - this.ctx.lineTo(textx, rowy + this.rowheight); - this.ctx.closePath(); - - if (col.checkbox) { - const checkboxWidthHeight = 12; - const checkboxCheckedPadding = 3; - const checkboxLeftPadding = 4; - this.ctx.strokeStyle = this.textColor; - this.ctx.beginPath(); - this.ctx.rect(checkboxLeftPadding + textx, texty - checkboxWidthHeight / 2, checkboxWidthHeight, checkboxWidthHeight); - this.ctx.stroke(); - if (val) { - this.ctx.beginPath(); - this.ctx.rect(checkboxLeftPadding + textx + checkboxCheckedPadding, - checkboxCheckedPadding + texty - checkboxWidthHeight / 2, - checkboxWidthHeight - checkboxCheckedPadding * 2, - checkboxWidthHeight - checkboxCheckedPadding * 2); - this.ctx.fill(); - } - } else { - - // Other columns - if (colindex >= this.rowWrapModeWrapColumn) { - // Subject - x += 30; // Increase padding before Subject - this.ctx.save(); - if (isBoldRow) { - this.ctx.save(); - this.ctx.font = 'bold ' + this.fontheight + 'px ' + this.fontFamilyBold; - this.ctx.fillStyle = this.textColorLink; - } else { - this.ctx.save(); - this.ctx.font = this.fontheight + 'px ' + this.fontFamily; - this.ctx.fillStyle = this.textColorLink; - } - this.ctx.fillText(formattedVal, x, rowy + halfrowheight + 12 - - (this.showContentTextPreview ? 12 : 0) - ); - this.ctx.restore(); - } else if (col.rowWrapModeMuted) { - // Date/time - x = 42; // sufficiently away from the checkbox - this.ctx.save(); - this.ctx.font = this.fontheightSmaller + 'px ' + this.fontFamily; - this.ctx.fillStyle = this.textColor; - this.ctx.fillText(formattedVal, x, rowy + halfrowheight - 10 - - (this.showContentTextPreview ? 8 : 0) - ); - this.ctx.restore(); - } else { - x = 128; // far enough to make the date above fit nicely - this.ctx.font = this.fontheightSmall + 'px ' + this.fontFamily; - this.ctx.fillText(formattedVal, x, rowy + halfrowheight - 10 - - (this.showContentTextPreview ? 8 : 0)); - this.ctx.fillStyle = this.textColorLink; - } - } - this.ctx.restore(); - } else if (x - this.horizScroll < canvwidth) { - // Normal no-wrap mode - - // Check box - const texty: number = rowy + halfrowheight - (this.showContentTextPreview ? 10 : 0); - let textx: number = x - this.horizScroll; - - const width = col.width - this.colpaddingright - this.colpaddingleft; - - this.ctx.save(); - this.ctx.beginPath(); - this.ctx.moveTo(textx, rowy); - this.ctx.lineTo(textx + width, rowy); - this.ctx.lineTo(textx + width, rowy + this.rowheight); - this.ctx.lineTo(textx, rowy + this.rowheight); - this.ctx.closePath(); - - this.ctx.clip(); - - if (col.checkbox) { - const checkboxWidthHeight = 12; - const checkboxCheckedPadding = 3; - const checkboxLeftPadding = 4; - this.ctx.strokeStyle = this.textColor; - this.ctx.beginPath(); - this.ctx.rect(checkboxLeftPadding + textx, texty - checkboxWidthHeight / 2, checkboxWidthHeight, checkboxWidthHeight); - this.ctx.stroke(); - if (val) { - this.ctx.beginPath(); - this.ctx.rect(checkboxLeftPadding + textx + checkboxCheckedPadding, - checkboxCheckedPadding + texty - checkboxWidthHeight / 2, - checkboxWidthHeight - checkboxCheckedPadding * 2, - checkboxWidthHeight - checkboxCheckedPadding * 2); - this.ctx.fill(); - } - } else { - // Other columns - if (col.textAlign === 1) { - textx += width; - this.ctx.textAlign = 'end'; - } - - if (col.font) { - this.ctx.font = col.font; - } - if (colindex === 2 || colindex === 3) { - // Column 2 is From, 3 is Subject - this.ctx.fillStyle = this.textColorLink; - if (isBoldRow) { - this.ctx.font = 'bold ' + this.fontheight + 'px ' + this.fontFamilyBold; - } - } - this.ctx.fillText(formattedVal, textx, texty); - } - this.ctx.restore(); - } - } - - x += (Math.round(col.width * (this.rowWrapMode && col.rowWrapModeMuted ? - (10 / this.fontheight) : 1)) - this.colpaddingleft); // We've already added colpaddingleft above - } - } else { - // skipping rows we've removed while canvas was updating.... - console.log('Skipped repainting a row as its data is missing, continuing anyway'); - } - if (this.showContentTextPreview) { - const contentTextPreviewColumn = this.columns - .find(col => col.getContentPreviewText ? true : false); - if (contentTextPreviewColumn) { - const contentPreviewText = contentTextPreviewColumn.getContentPreviewText(rowIndex); - if (contentPreviewText) { - this.ctx.save(); - this.ctx.fillStyle = this.textColor; - this.ctx.font = this.fontheightSmaller + 'px ' + this.fontFamily; - const contentTextPreviewColumnPadding = this.rowWrapMode ? 2 : 10; // Increase left padding of content preview - this.ctx.fillText(contentPreviewText, this.columns[0]. width + contentTextPreviewColumnPadding, - rowy + halfrowheight + (this.rowWrapMode ? 18 : 15)); - this.ctx.restore(); - } - } - } - - if (rowy > canvheight) { - break; - } - this.ctx.fillStyle = this.textColor; - - } - - // Column separators - - if (!this.rowWrapMode) { - // No column separators in row wrap mode - this.ctx.fillStyle = `rgba(166,166,166,${this.visibleColumnSeparatorAlpha})`; - this.ctx.strokeStyle = `rgba(176,176,176,${this.visibleColumnSeparatorAlpha})`; - - if (this.visibleColumnSeparatorAlpha < 1) { - this.visibleColumnSeparatorAlpha += 0.01; - setTimeout(() => this.hasChanges = true, 0); - } - - let x = 0; - for (let colindex = 0; colindex < this.columns.length; colindex++) { - if (colindex > 0 && this.visibleColumnSeparatorIndex === colindex) { - // Only draw column separator near the mouse pointer - this.ctx.beginPath(); - this.ctx.moveTo(x - this.horizScroll, 0); - this.ctx.lineTo(x - this.horizScroll, canvheight); - this.ctx.stroke(); - - this.ctx.fillRect(x - this.horizScroll - 5, this.lastClientY - 10, 10, 20); - } - x += this.columns[colindex].width; - } - } - - // Scrollbar - let scrollbarheight = (this.maxVisibleRows / this.rows.rowCount()) * canvheight; - if (scrollbarheight < 20) { - scrollbarheight = 20; - } - const scrollbarpos = - (this.topindex / (this.rows.rowCount() - this.maxVisibleRows)) * (canvheight - scrollbarheight); - - if (scrollbarheight < canvheight) { - const scrollbarverticalpadding = 4; - - const scrollbarx = canvwidth - this.scrollbarwidth; - this.ctx.fillStyle = '#aaa'; - this.ctx.fillRect(scrollbarx, 0, this.scrollbarwidth, canvheight); - this.ctx.fillStyle = '#fff'; - this.scrollBarRect = { - x: scrollbarx + 1, - y: scrollbarpos + scrollbarverticalpadding / 2, - width: this.scrollbarwidth - 2, - height: scrollbarheight - scrollbarverticalpadding - }; - - if (this.scrollbarDragInProgress) { - this.ctx.fillStyle = 'rgba(200,200,255,0.5)'; - this.roundRect(this.ctx, - this.scrollBarRect.x - 4, - this.scrollBarRect.y - 4, - this.scrollBarRect.width + 8, - this.scrollBarRect.height + 8, 5, true); - - this.ctx.fillStyle = '#fff'; - this.ctx.fillRect(this.scrollBarRect.x, - this.scrollBarRect.y, - this.scrollBarRect.width, - this.scrollBarRect.height); - } else { - this.ctx.fillStyle = '#fff'; - this.ctx.fillRect(this.scrollBarRect.x, this.scrollBarRect.y, this.scrollBarRect.width, this.scrollBarRect.height); - } - - } - - } +// private dopaint() { +// const devicePixelRatio = window.devicePixelRatio; +// if (this.canv.width !== this.wantedCanvasWidth || +// this.canv.height !== this.wantedCanvasHeight) { + +// const widthChanged = this.canv.width !== this.wantedCanvasWidth; +// /* Only resize on detection of width change +// * otherwise reducing column widths so that the scrollbar +// * disappears indicates a change of height and triggers resize +// */ + +// this.canv.style.width = (this.wantedCanvasWidth / devicePixelRatio) + 'px'; +// this.canv.style.height = (this.wantedCanvasHeight / devicePixelRatio) + 'px'; + +// this.canv.width = this.wantedCanvasWidth; +// this.canv.height = this.wantedCanvasHeight; + +// this.maxVisibleRows = this.canv.scrollHeight / this.rowheight; +// this.enforceScrollLimit(); +// this.hasChanges = true; +// if (this.canv.clientWidth < this.autoRowWrapModeWidth) { +// this.rowWrapMode = true; +// } else { +// this.rowWrapMode = false; +// } + +// this.canvasResizedSubject.next(widthChanged); +// } + +// if (devicePixelRatio !== 1) { +// // This is not scale() as that would keep multiplying +// // Moved out of above if() statement as something (!?) +// // was resetting transform, still not sure what +// this.ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0); +// } + +// this.ctx.textBaseline = 'middle'; +// this.ctx.font = this.fontheight + 'px ' + this.fontFamily; + +// const canvwidth: number = this.canv.scrollWidth; +// const canvheight: number = this.canv.scrollHeight; + +// let colx = 0 - this.horizScroll; +// // Columns +// for (let colindex = 0; colindex < this.columns.length; colindex++) { +// const col: CanvasTableColumn = this.columns[colindex]; +// if (colx + col.width > 0 && colx < canvwidth) { +// this.ctx.fillStyle = col.backgroundColor ? col.backgroundColor : '#fff'; +// this.ctx.fillRect(colx, +// 0, +// colindex === this.columns.length - 1 ? +// canvwidth - colx : +// col.width, +// canvheight +// ); +// } +// colx += col.width; +// } + +// if (!this.rows || this.rows.rowCount() < 1) { +// return; +// } + +// // Rows +// for (let n = this.topindex; n < this.rows.rowCount(); n += 1.0) { +// const rowIndex = Math.floor(n); + +// if (rowIndex > this.rows.rowCount()) { +// break; +// } + +// // const rowobj = this.rows[rowIndex]; + +// const halfrowheight = (this.rowheight / 2); +// const rowy = (rowIndex - this.topindex) * this.rowheight; +// if (this.rows.rowExists(rowIndex)) { +// // Clear row area +// // Alternating row colors: +// // let rowBgColor : string = (rowIndex%2===0 ? "#e8e8e8" : "rgba(255,255,255,0.7)"); +// // Single row color: +// let rowBgColor = '#fff'; + +// const isBoldRow = this.rows.isBoldRow(rowIndex); +// const isSelectedRow = this.rows.isSelectedRow(rowIndex); +// const isOpenedRow = this.rows.isOpenedRow(rowIndex); +// if (this.hoverRowIndex === rowIndex) { +// rowBgColor = this.hoverRowColor; +// } +// if (isSelectedRow) { +// rowBgColor = this.selectedRowColor; +// } +// if (isOpenedRow) { +// rowBgColor = this.openedRowColor; +// } + +// this.ctx.fillStyle = rowBgColor; +// this.ctx.fillRect(0, rowy, canvwidth, this.rowheight); + +// // Row borders separating each row +// this.ctx.strokeStyle = '#eee'; +// this.ctx.beginPath(); +// this.ctx.moveTo(0, rowy); +// this.ctx.lineTo(canvwidth, rowy); +// this.ctx.stroke(); + +// let x = 0; +// for (let colindex = 0; colindex < this.columns.length; colindex++) { +// const col: CanvasTableColumn = this.columns[colindex]; +// let val: any = col.getValue(rowIndex); +// if (val === 'RETRY') { +// // retry later if value is null +// setTimeout(() => this.hasChanges = true, 2); +// val = ''; +// } +// let formattedVal: string; +// const formattedValueCacheKey: string = col.cacheKey + ':' + val; +// if (this.formattedValueCache[formattedValueCacheKey]) { +// formattedVal = this.formattedValueCache[formattedValueCacheKey]; +// } else if (('' + val).length > 0 && col.getFormattedValue) { +// formattedVal = col.getFormattedValue(val); +// this.formattedValueCache[formattedValueCacheKey] = formattedVal; +// } else { +// formattedVal = '' + val; +// this.formattedValueCache[formattedValueCacheKey] = formattedVal; +// } +// if (this.rowWrapMode && col.rowWrapModeHidden) { +// continue; +// } else if (this.rowWrapMode && col.rowWrapModeChipCounter && parseInt(val, 10) > 1) { +// this.ctx.save(); + +// this.ctx.strokeStyle = ''; + +// this.roundRect(this.ctx, +// canvwidth - 50, +// rowy + 9, +// 28, +// 15, 10, false); +// this.ctx.font = '10px ' + this.fontFamily; + +// this.ctx.strokeStyle = '#000'; +// if (isSelectedRow) { +// this.ctx.fillStyle = this.textColor; +// } else { +// this.ctx.fillStyle = this.textColor; +// } +// this.ctx.textAlign = 'center'; +// this.ctx.fillText(formattedVal + '', canvwidth - 36, rowy + halfrowheight - 15); + +// this.ctx.restore(); + +// continue; +// } else if (this.rowWrapMode && col.rowWrapModeChipCounter) { +// continue; +// } +// if (this.rowWrapMode && colindex === this.rowWrapModeWrapColumn) { +// x = 0; +// } + +// x += this.colpaddingleft; + +// if ((x - this.horizScroll + col.width) >= 0 && formattedVal.length > 0) { +// this.ctx.fillStyle = this.textColor; // Text color of unselected row +// if (isSelectedRow) { +// this.ctx.fillStyle = this.textColor; // Text color of selected row +// } + +// if (this.rowWrapMode) { +// // Wrap rows if in row wrap mode (for e.g. mobile portrait view) + +// // Check box +// const texty: number = rowy + halfrowheight; +// const textx: number = x - this.horizScroll; + +// const width = col.width - this.colpaddingright - this.colpaddingleft; + +// this.ctx.save(); +// this.ctx.beginPath(); +// this.ctx.moveTo(textx, rowy); +// this.ctx.lineTo(textx + width, rowy); +// this.ctx.lineTo(textx + width, rowy + this.rowheight); +// this.ctx.lineTo(textx, rowy + this.rowheight); +// this.ctx.closePath(); + +// if (col.checkbox) { +// const checkboxWidthHeight = 12; +// const checkboxCheckedPadding = 3; +// const checkboxLeftPadding = 4; +// this.ctx.strokeStyle = this.textColor; +// this.ctx.beginPath(); +// this.ctx.rect(checkboxLeftPadding + textx, texty - checkboxWidthHeight / 2, checkboxWidthHeight, checkboxWidthHeight); +// this.ctx.stroke(); +// if (val) { +// this.ctx.beginPath(); +// this.ctx.rect(checkboxLeftPadding + textx + checkboxCheckedPadding, +// checkboxCheckedPadding + texty - checkboxWidthHeight / 2, +// checkboxWidthHeight - checkboxCheckedPadding * 2, +// checkboxWidthHeight - checkboxCheckedPadding * 2); +// this.ctx.fill(); +// } +// } else { + +// // Other columns +// if (colindex >= this.rowWrapModeWrapColumn) { +// // Subject +// x += 30; // Increase padding before Subject +// this.ctx.save(); +// if (isBoldRow) { +// this.ctx.save(); +// this.ctx.font = 'bold ' + this.fontheight + 'px ' + this.fontFamilyBold; +// this.ctx.fillStyle = this.textColorLink; +// } else { +// this.ctx.save(); +// this.ctx.font = this.fontheight + 'px ' + this.fontFamily; +// this.ctx.fillStyle = this.textColorLink; +// } +// this.ctx.fillText(formattedVal, x, rowy + halfrowheight + 12 +// - (this.showContentTextPreview ? 12 : 0) +// ); +// this.ctx.restore(); +// } else if (col.rowWrapModeMuted) { +// // Date/time +// x = 42; // sufficiently away from the checkbox +// this.ctx.save(); +// this.ctx.font = this.fontheightSmaller + 'px ' + this.fontFamily; +// this.ctx.fillStyle = this.textColor; +// this.ctx.fillText(formattedVal, x, rowy + halfrowheight - 10 +// - (this.showContentTextPreview ? 8 : 0) +// ); +// this.ctx.restore(); +// } else { +// x = 128; // far enough to make the date above fit nicely +// this.ctx.font = this.fontheightSmall + 'px ' + this.fontFamily; +// this.ctx.fillText(formattedVal, x, rowy + halfrowheight - 10 +// - (this.showContentTextPreview ? 8 : 0)); +// this.ctx.fillStyle = this.textColorLink; +// } +// } +// this.ctx.restore(); +// } else if (x - this.horizScroll < canvwidth) { +// // Normal no-wrap mode + +// // Check box +// const texty: number = rowy + halfrowheight - (this.showContentTextPreview ? 10 : 0); +// let textx: number = x - this.horizScroll; + +// const width = col.width - this.colpaddingright - this.colpaddingleft; + +// this.ctx.save(); +// this.ctx.beginPath(); +// this.ctx.moveTo(textx, rowy); +// this.ctx.lineTo(textx + width, rowy); +// this.ctx.lineTo(textx + width, rowy + this.rowheight); +// this.ctx.lineTo(textx, rowy + this.rowheight); +// this.ctx.closePath(); + +// this.ctx.clip(); + +// if (col.checkbox) { +// const checkboxWidthHeight = 12; +// const checkboxCheckedPadding = 3; +// const checkboxLeftPadding = 4; +// this.ctx.strokeStyle = this.textColor; +// this.ctx.beginPath(); +// this.ctx.rect(checkboxLeftPadding + textx, texty - checkboxWidthHeight / 2, checkboxWidthHeight, checkboxWidthHeight); +// this.ctx.stroke(); +// if (val) { +// this.ctx.beginPath(); +// this.ctx.rect(checkboxLeftPadding + textx + checkboxCheckedPadding, +// checkboxCheckedPadding + texty - checkboxWidthHeight / 2, +// checkboxWidthHeight - checkboxCheckedPadding * 2, +// checkboxWidthHeight - checkboxCheckedPadding * 2); +// this.ctx.fill(); +// } +// } else { +// // Other columns +// if (col.textAlign === 1) { +// textx += width; +// this.ctx.textAlign = 'end'; +// } + +// if (col.font) { +// this.ctx.font = col.font; +// } +// if (colindex === 2 || colindex === 3) { +// // Column 2 is From, 3 is Subject +// this.ctx.fillStyle = this.textColorLink; +// if (isBoldRow) { +// this.ctx.font = 'bold ' + this.fontheight + 'px ' + this.fontFamilyBold; +// } +// } +// this.ctx.fillText(formattedVal, textx, texty); +// } +// this.ctx.restore(); +// } +// } + +// x += (Math.round(col.width * (this.rowWrapMode && col.rowWrapModeMuted ? +// (10 / this.fontheight) : 1)) - this.colpaddingleft); // We've already added colpaddingleft above +// } +// } else { +// // skipping rows we've removed while canvas was updating.... +// console.log('Skipped repainting a row as its data is missing, continuing anyway'); +// } +// if (this.showContentTextPreview) { +// const contentTextPreviewColumn = this.columns +// .find(col => col.getContentPreviewText ? true : false); +// if (contentTextPreviewColumn) { +// const contentPreviewText = contentTextPreviewColumn.getContentPreviewText(rowIndex); +// if (contentPreviewText) { +// this.ctx.save(); +// this.ctx.fillStyle = this.textColor; +// this.ctx.font = this.fontheightSmaller + 'px ' + this.fontFamily; +// const contentTextPreviewColumnPadding = this.rowWrapMode ? 2 : 10; // Increase left padding of content preview +// this.ctx.fillText(contentPreviewText, this.columns[0]. width + contentTextPreviewColumnPadding, +// rowy + halfrowheight + (this.rowWrapMode ? 18 : 15)); +// this.ctx.restore(); +// } +// } +// } + +// if (rowy > canvheight) { +// break; +// } +// this.ctx.fillStyle = this.textColor; + +// } + +// // Column separators + +// if (!this.rowWrapMode) { +// // No column separators in row wrap mode +// this.ctx.fillStyle = `rgba(166,166,166,${this.visibleColumnSeparatorAlpha})`; +// this.ctx.strokeStyle = `rgba(176,176,176,${this.visibleColumnSeparatorAlpha})`; + +// if (this.visibleColumnSeparatorAlpha < 1) { +// this.visibleColumnSeparatorAlpha += 0.01; +// setTimeout(() => this.hasChanges = true, 0); +// } + +// let x = 0; +// for (let colindex = 0; colindex < this.columns.length; colindex++) { +// if (colindex > 0 && this.visibleColumnSeparatorIndex === colindex) { +// // Only draw column separator near the mouse pointer +// this.ctx.beginPath(); +// this.ctx.moveTo(x - this.horizScroll, 0); +// this.ctx.lineTo(x - this.horizScroll, canvheight); +// this.ctx.stroke(); + +// this.ctx.fillRect(x - this.horizScroll - 5, this.lastClientY - 10, 10, 20); +// } +// x += this.columns[colindex].width; +// } +// } + +// // Scrollbar +// let scrollbarheight = (this.maxVisibleRows / this.rows.rowCount()) * canvheight; +// if (scrollbarheight < 20) { +// scrollbarheight = 20; +// } +// const scrollbarpos = +// (this.topindex / (this.rows.rowCount() - this.maxVisibleRows)) * (canvheight - scrollbarheight); + +// if (scrollbarheight < canvheight) { +// const scrollbarverticalpadding = 4; + +// const scrollbarx = canvwidth - this.scrollbarwidth; +// this.ctx.fillStyle = '#aaa'; +// this.ctx.fillRect(scrollbarx, 0, this.scrollbarwidth, canvheight); +// this.ctx.fillStyle = '#fff'; +// this.scrollBarRect = { +// x: scrollbarx + 1, +// y: scrollbarpos + scrollbarverticalpadding / 2, +// width: this.scrollbarwidth - 2, +// height: scrollbarheight - scrollbarverticalpadding +// }; + +// if (this.scrollbarDragInProgress) { +// this.ctx.fillStyle = 'rgba(200,200,255,0.5)'; +// this.roundRect(this.ctx, +// this.scrollBarRect.x - 4, +// this.scrollBarRect.y - 4, +// this.scrollBarRect.width + 8, +// this.scrollBarRect.height + 8, 5, true); + +// this.ctx.fillStyle = '#fff'; +// this.ctx.fillRect(this.scrollBarRect.x, +// this.scrollBarRect.y, +// this.scrollBarRect.width, +// this.scrollBarRect.height); +// } else { +// this.ctx.fillStyle = '#fff'; +// this.ctx.fillRect(this.scrollBarRect.x, this.scrollBarRect.y, this.scrollBarRect.width, this.scrollBarRect.height); +// } + +// } + +// } } @Component({ @@ -1371,7 +1371,7 @@ export class CanvasTableComponent implements AfterViewInit, DoCheck, OnInit { moduleId: 'angular2/app/canvastable/', styleUrls: ['canvastablecontainer.component.scss'] }) -export class CanvasTableContainerComponent implements OnInit { +export class CanvasTableContainerComponent { colResizeInitialClientX: number; colResizeColumnIndex: number; colResizePreviousWidth: number; @@ -1397,7 +1397,7 @@ export class CanvasTableContainerComponent implements OnInit { RowSelect = CanvasTable.RowSelect; private selectAllTimeout; - constructor(private renderer: Renderer2) { + constructor() { // const oldSavedColumnWidths = localStorage.getItem('canvasNamedColumnWidths'); // if (oldSavedColumnWidths) { // const colWidthSet = Object.keys(JSON.parse(oldSavedColumnWidths)).filter((col) => col.length > 0).join(','); @@ -1424,23 +1424,23 @@ export class CanvasTableContainerComponent implements OnInit { // localStorage.setItem('canvasNamedColumnWidthsBySet', JSON.stringify(this.columnWidths)); } - ngOnInit() { - this.renderer.listen('window', 'mousemove', (event: MouseEvent) => { - if (this.colResizeInitialClientX) { - event.preventDefault(); - event.stopPropagation(); - this.colresize(event.clientX); - } - }); - - this.renderer.listen('window', 'mouseup', (event: MouseEvent) => { - if (this.colResizeInitialClientX) { - event.preventDefault(); - event.stopPropagation(); - this.colresizeend(); - } - }); - } + // ngOnInit() { + // this.renderer.listen('window', 'mousemove', (event: MouseEvent) => { + // if (this.colResizeInitialClientX) { + // event.preventDefault(); + // event.stopPropagation(); + // this.colresize(event.clientX); + // } + // }); + + // this.renderer.listen('window', 'mouseup', (event: MouseEvent) => { + // if (this.colResizeInitialClientX) { + // event.preventDefault(); + // event.stopPropagation(); + // this.colresizeend(); + // } + // }); + // } colresizestart(clientX: number, colIndex: number) { if (colIndex > 0) { diff --git a/src/app/changelog/changes.ts b/src/app/changelog/changes.ts index ef793b0b6..7016d4ec3 100644 --- a/src/app/changelog/changes.ts +++ b/src/app/changelog/changes.ts @@ -71,5628 +71,10409 @@ export class ChangelogEntry { // BEGIN:AUTOGENERATED const changes = [ [ - "9b2a297", + "026ef8a0", + "1733405709", + "feat", + "a11y-table", + "Define a standalone table component" + ], + [ + "26e7663d", + "1732799602", + "build", + "xapian", + "Fixes bug with delayed markSeen on message-open" + ], + [ + "d010fae3", + "1732623464", + "fix", + "messagelist", + "Use user selected folder after leaving non-folder" + ], + [ + "47aa0601", + "1732617673", + "fix", + "messagelist", + "Show correct folder contents after Drafts view" + ], + [ + "28051879", + "1732554569", + "fix", + "maillist", + "Ensure we only switch to index data after its loaded" + ], + [ + "1d389a86", + "1732537528", + "fix", + "messagelist", + "Show Templates folder contents from API" + ], + [ + "d7b8a2cc", + "1732191814", + "fix", + "receipt", + "Ensure receipt displays all sections" + ], + [ + "d65a494b", + "1732121753", + "fix", + "folders", + "Update folder count on send or save" + ], + [ + "69be618f", + "1732109523", + "fix", + "account-details", + "Use phone icon instead of email icon near phone input" + ], + [ + "7603850d", + "1732033119", + "build", + "app", + "Update build ngsw.json with appData" + ], + [ + "28e8f919", + "1732031948", + "fix", + "receipt", + "Ensure receipt is loaded before checking its status" + ], + [ + "31b536d0", + "1732014061", + "fix", + "sidebar", + "Display Alt validation warning if unvalidated" + ], + [ + "3aacd30d", + "1731955737", + "fix", + "payment", + "Fix equality signs." + ], + [ + "7065f4cb", + "1731945803", + "build", + "app", + "Update file hashes after sentry instrumentation" + ], + [ + "a41529a3", + "1731880520", + "style", + "account", + "Add alternative email address validation indicator." + ], + [ + "86ed8e01", + "1731878876", + "style", + "payment", + "Include instructions about crypto addresses (replaces PR 1582)." + ], + [ + "b15bc277", + "1731324211", + "fix", + "account", + "Improve styling of resent alt email feature" + ], + [ + "4a7b911d", + "1731324211", + "feat", + "account", + "Allows resending of alt email validation" + ], + [ + "3f0b3330", + "1731067654", + "feat", + "templates", + "Start template draft in template edit mode" + ], + [ + "0647dc49", + "1730888746", + "feat", + "templates", + "Create draft on template item click" + ], + [ + "ce5015ae", + "1730888725", + "test", + "templates", + "Create draft on template click" + ], + [ + "bd55fc35", + "1730882594", + "test", + "templates", + "Test template creation in compose" + ], + [ + "3b19d166", + "1730823458", + "test", + "templates", + "Test template creation in compose" + ], + [ + "9b290cc7", + "1730823372", + "test", + "templates", + "Test template creation in compose" + ], + [ + "1e8ce63a", + "1730818658", + "feat", + "templates", + "Save draft as template" + ], + [ + "a6dc6ee8", + "1730722914", + "feat", + "a11y", + "Remove underline on anchor hover" + ], + [ + "22046e93", + "1730707796", + "feat", + "templates", + "Save draft as template" + ], + [ + "6a24e984", + "1730384172", + "feat", + "templates", + "Save draft as template" + ], + [ + "72d68cc9", + "1730384029", + "feat", + "templates", + "Create draft on template item click" + ], + [ + "b349ea9d", + "1730284299", + "feat", + "templates", + "Create draft on template item click" + ], + [ + "29471c80", + "1730284148", + "feat", + "templates", + "Create draft on template item click" + ], + [ + "eb577e61", + "1730284099", + "feat", + "templates", + "Save draft as template" + ], + [ + "20177bb0", + "1730283731", + "feat", + "templates", + "Create draft on template item click" + ], + [ + "e305d849", + "1730217112", + "feat", + "templates", + "Create draft on template item click" + ], + [ + "5912fb8c", + "1730217112", + "feat", + "templates", + "Save draft as template" + ], + [ + "77822af1", + "1730217112", + "feat", + "templates", + "Save draft as template" + ], + [ + "1778de13", + "1730217112", + "feat", + "templates", + "Create draft on template item click" + ], + [ + "a5c06b5c", + "1730217112", + "feat", + "templates", + "Create draft on template item click" + ], + [ + "12111794", + "1729607214", + "style", + "payment", + "Add 3-year promo image." + ], + [ + "35d2e111", + "1729345730", + "feat", + "welcome", + "Show improved 3-year plans on Welcome page upon signup." + ], + [ + "1e7d1de3", + "1729077815", + "fix", + "config", + "Opt-out of angular analytics" + ], + [ + "225b1eae", + "1728470512", + "fix", + "payment", + "In-page links for both dev and production use" + ], + [ + "3eed34f0", + "1728311149", + "fix", + "payment", + "Correct bookmark links for production." + ], + [ + "7cc7f60a", + "1728308683", + "style", + "payment", + "Re-add image file." + ], + [ + "7da88729", + "1728194978", + "fix", + "payment", + "Replace routerLink with regular link for bookmark links." + ], + [ + "d818d5a3", + "1728045080", + "style", + "payment", + "Correct bookmaark links and add image." + ], + [ + "71bc0281", + "1727866828", + "feat", + "mailviewer", + "Add sender/domain blocking functionality" + ], + [ + "3931d044", + "1725651724", + "style", + "install", + "Integrate \"synchronizing\" text string in modal." + ], + [ + "ba5c2529", + "1725553128", + "fix", + "messages", + "Use batch_delete for deleting multiple messages" + ], + [ + "622cc62b", + "1725458630", + "test", + "upgrades", + "Update ordering tests to match new upgrades table" + ], + [ + "8569eb17", + "1725443824", + "fix", + "upgrades", + "Make upgrades tables independent of product lists" + ], + [ + "ec2da8b6", + "1724848549", + "feat", + "index", + "Creates a browser-local index if not downloadable\"" + ], + [ + "8d4b4b6c", + "1723641508", + "style", + "payment", + "Fix typo." + ], + [ + "88560ea7", + "1723564320", + "test", + "payment", + "Fix test." + ], + [ + "f245090c", + "1723562794", + "test", + "payment", + "Fix test." + ], + [ + "404e057d", + "1723543147", + "build", + "deps", + "Upgrade to runbox-searchindex 0.24" + ], + [ + "ed0b0aa7", + "1723542201", + "test", + "payment", + "Fix test." + ], + [ + "5ba39b4e", + "1723496779", + "fix", + "payment", + "Update test." + ], + [ + "6e243628", + "1723472512", + "test", + "contacts", + "Tweak occasionally failing tests" + ], + [ + "05f0d1bd", + "1723472308", + "feat", + "index", + "Creates a browser-local index if not downloadable" + ], + [ + "8883a496", + "1723465515", + "fix", + "indexer", + "Close initial index sync prompt after download" + ], + [ + "6be4fdb4", + "1723127724", + "style", + "payment", + "Add recommended plans and make table more dynamic." + ], + [ + "84418e79", + "1722424478", + "test", + "recipients", + "Simplify recipients service tests" + ], + [ + "3523f68a", + "1720879445", + "style", + "payment", + "New layout on Plans & Upgrades." + ], + [ + "ede22381", + "1720796656", + "feat", + "indexer", + "WIP upgrades to newer searchindex/xapian" + ], + [ + "e35c199a", + "1720006791", + "fix", + "identities", + "Always displays Other identites section" + ], + [ + "2b443a1b", + "1718813412", + "feat", + "identities", + "Change of runbox domains for all identities" + ], + [ + "fd119b54", + "1718640165", + "fix", + "identities", + "Re-enable editing of preferred domain for aliases" + ], + [ + "c5898f8d", + "1718592105", + "style", + "install", + "Widen sync buttons on mobile and center sync message." + ], + [ + "a7bff5ac", + "1718031974", + "fix", + "updates", + "Only start update service if enabled in browser" + ], + [ + "33761a39", + "1718029524", + "fix", + "messagelist", + "Return json data from folder count in web worker" + ], + [ + "ffe20975", + "1718029179", + "fix", + "messagelist", + "Folder count refresh only check index if available" + ], + [ + "e2a2479f", + "1718028420", + "fix", + "mailviewer", + "Catches error when horizontal view is 0 height" + ], + [ + "1c167876", + "1717790571", + "fix", + "payment", + "Correct Coinbase logo path." + ], + [ + "a0c8a428", + "1717495556", + "fix", + "compose", + "Force cache refresh for new tinymce version" + ], + [ + "1eaba010", + "1717240467", + "fix", + "payment", + "Change from \"bitpay\" to \"coinbase\" payment method." + ], + [ + "cb7ae4b2", + "1717238623", + "fix", + "payment", + "Add instructions about informing us upon payment." + ], + [ + "1cfd0e7e", + "1716544083", + "fix", + "payment", + "Remove instructions about including details as that isn't possible." + ], + [ + "a646bbe7", + "1716468671", + "fix", + "index", + "Recover from index removal stalling" + ], + [ + "b23d99b8", + "1716463549", + "fix", + "compose", + "Remove img src when deleting attachment" + ], + [ + "4397acca", + "1716455478", + "fix", + "mailviewer", + "Display html multiple images using same attachment" + ], + [ + "ad619125", + "1716455478", + "fix", + "compose", + "Remove html img ref if image attachment removed" + ], + [ + "18142f4e", + "1716455478", + "feat", + "compose", + "Enable Drag&Drop images into compose" + ], + [ + "64690298", + "1716455478", + "fix", + "compose", + "Adds HTML editor paste plugin" + ], + [ + "fb6a7ad5", + "1716455478", + "refactor", + "compose", + "Upgrades TinyMCE to v6" + ], + [ + "342af3c5", + "1716358857", + "feat", + "payment", + "Remove unused component." + ], + [ + "82dcb5a5", + "1716300274", + "feat", + "payment", + "Improve cryptocurrency payment instructions." + ], + [ + "56dddd9c", + "1715869838", + "fix", + "messagelist", + "Ensures Threaded, Unread not set together" + ], + [ + "d895b906", + "1715680370", + "fix", + "subscriptions", + "Sub accounts quota isn't used for usage warning" + ], + [ + "d35fd895", + "1714578569", + "test", + "all", + "Update tests for revised localSearchPrompt setting" + ], + [ + "52177a61", + "1714563110", + "fix", + "compose", + "Swap identity signature in HTML mode" + ], + [ + "020b82f0", + "1714485641", + "fix", + "compose", + "Add plain text signatures when replying to HTML email" + ], + [ + "5ee4bc4c", + "1714472453", + "fix", + "compose", + "Reply-drafts also now sorted to the top" + ], + [ + "5b4a3df0", + "1714417539", + "style", + "install", + "Change wording of button to cancel index synchronization." + ], + [ + "0b4be052", + "1714417539", + "fix", + "index", + "Re-prompt local index download on each device" + ], + [ + "bbd9b020", + "1714392150", + "fix", + "compose", + "Current draft, newest drafts, are now at the top" + ], + [ + "c6d8df81", + "1714387430", + "fix", + "compose", + "Only add one signature to a composed email" + ], + [ + "141ce827", + "1714039007", + "fix", + "index", + "Re-prompt local index download on each device" + ], + [ + "d678df70", + "1713954097", + "fix", + "compose", + "URLs in messages and signatures should not be relative" + ], + [ + "782b1f58", + "1713796850", + "fix", + "compose", + "Use from-name when sending email, not identity name" + ], + [ + "22c1c13d", + "1713604651", + "style", + "mail", + "Improve unread count badges and icon alignments." + ], + [ + "a0f05f3a", + "1713352199", + "fix", + "compose", + "Display From name in from list (not description)" + ], + [ + "4cc3c8ba", + "1713268384", + "style", + "install", + "Improve formatting for mobile phones." + ], + [ + "c164b768", + "1713263203", + "style", + "Compose", + "Fix card and field widths." + ], + [ + "a722671e", + "1712176369", + "style", + "mail", + "Adjust icon/button alignment." + ], + [ + "4318dde9", + "1712142254", + "style", + "install", + "Improve formatting of buttons." + ], + [ + "3cd9bdeb", + "1712142254", + "style", + "install", + "Adjust introductory search index prompt." + ], + [ + "699c2c02", + "1712142254", + "test", + "search", + "Ensure search tests have known state before running" + ], + [ + "60895d96", + "1712142254", + "feat", + "index", + "Expand index sidebar dialog to cancel download" + ], + [ + "8aeeee3f", + "1712142254", + "feat", + "install", + "Replaces index dialog prompt with sidebar buttons" + ], + [ + "d7bccd0a", + "1712085578", + "test", + "install", + "Update test." + ], + [ + "816ce2bb", + "1712085315", + "feat", + "payment", + "Support direct cryptocurrency transfers." + ], + [ + "549a7955", + "1712068381", + "refactor", + "identities", + "Tidy up code/syntax as requested in review" + ], + [ + "b27f444f", + "1712068381", + "fix", + "identities", + "Ensure username identity email cannot be changed" + ], + [ + "f1aa7f8b", + "1712068381", + "fix", + "identities", + "\"other\" is non-aliases and non-default items" + ], + [ + "5ebc530f", + "1712068381", + "style", + "identities", + "Minor code styling tidy-up" + ], + [ + "c2e32d22", + "1712068381", + "fix", + "identities", + "Show main identity in \"other\" list, if not default" + ], + [ + "a1f3fdaa", + "1712068381", + "style", + "identities", + "Improve formatting of username identity and warnings." + ], + [ + "58be2fe5", + "1712068381", + "fix", + "identities", + "Compare used alias total correctly" + ], + [ + "2f5d42a1", + "1712068381", + "test", + "profiles", + "Only calls save button once in create profile" + ], + [ + "c8ed4248", + "1712068381", + "fix", + "aliases", + "Making aliases API endpoints do one job each" + ], + [ + "b6321112", + "1712068381", + "test", + "identities", + "Add some profile service / identity unit+e2e tests" + ], + [ + "46e69b8e", + "1712068381", + "fix", + "identities", + "More stability based on review" + ], + [ + "d44aeac8", + "1712068381", + "test", + "various", + "Fixup existing tests, upgrade Cypress to latest v12" + ], + [ + "7c3e3dcb", + "1712068381", + "style", + "identities", + "Clarify identity type." + ], + [ + "97fb3f84", + "1712068381", + "test", + "identities", + "Updates mock test data to reflect api changes" + ], + [ + "aedd58b7", + "1712068381", + "test", + "all", + "Enable e2e test debugging/logging" + ], + [ + "72905d10", + "1712068381", + "fix", + "drafts", + "Only display CurrentMax most recent Drafts" + ], + [ + "c06fbfff", + "1712068381", + "test", + "identities", + "Fixes unit tests after API changes" + ], + [ + "310f3f65", + "1712068381", + "fix", + "identities", + "Show identity type on edit, where no delete button" + ], + [ + "7d0dcb13", + "1712068381", + "fix", + "identities", + "Split out \"username\" profile and protect it" + ], + [ + "15e30512", + "1711996086", + "style", + "install", + "Adjust indexing prompt for mobile screens." + ], + [ + "35a92883", + "1711575174", + "style", + "payment", + "Add CSS for Coinbase payment modal." + ], + [ + "c746a182", + "1711358939", + "style", + "payment", + "Coinbase logo." + ], + [ + "497a4d27", + "1711324796", + "style", + "payment", + "Update Coinbase payment modal." + ], + [ + "c70da77f", + "1710520252", + "fix", + "payment", + "Correct typo." + ], + [ + "941e3345", + "1710476155", + "fix", + "payment", + "Simplify Sub-accounts link." + ], + [ + "da7a055d", + "1710019184", + "style", + "install", + "Improve formatting of buttons." + ], + [ + "09713f03", + "1710018510", + "style", + "install", + "Adjust introductory search index prompt." + ], + [ + "a523891d", + "1709554848", + "test", + "search", + "Ensure search tests have known state before running" + ], + [ + "ad2e8b71", + "1709210095", + "feat", + "index", + "Expand index sidebar dialog to cancel download" + ], + [ + "a9e5223b", + "1709205940", + "feat", + "install", + "Replaces index dialog prompt with sidebar buttons" + ], + [ + "abdd9b38", + "1709138471", + "fix", + "login", + "Stay on login screen if cache deleted / cookies expired" + ], + [ + "3a2f1c3c", + "1707850869", + "style", + "aliases", + "Use MD buttons correctly and improve domain list." + ], + [ + "713ebada", + "1707754329", + "fix", + "subscriptions", + "Cope with \"no active subscriptions\" with link" + ], + [ + "e6718e7d", + "1707752892", + "fix", + "account", + "Ensure upgrade/downgrade works when returning to page" + ], + [ + "02bcc472", + "1707396453", + "fix", + "payment", + "Correct downgrade check and fix lint errors." + ], + [ + "6980a879", + "1707311498", + "feat", + "upgrades", + "Sort subaccounts by size of account" + ], + [ + "33da9db6", + "1707310888", + "test", + "ordering", + "Ensure quantity increase test uses add-on product" + ], + [ + "bf3a7b52", + "1707220529", + "test", + "payment", + "Fix ordering test." + ], + [ + "b6500ac3", + "1706792784", + "fix", + "payment", + "Support multiple instances of purchaseButton." + ], + [ + "2af5945c", + "1706785281", + "style", + "payment", + "Order account plans by quota and further formatting adjustments." + ], + [ + "9601124e", + "1706616121", + "test", + "payment", + "Update test." + ], + [ + "4b4c15bb", + "1706525626", + "fix", + "payment", + "Determine subscription plan size by email storage space." + ], + [ + "b53004ed", + "1706521860", + "style", + "payment", + "Correctly handle trials, and clarify sub-accounts." + ], + [ + "4888a7f6", + "1706357870", + "style", + "payment", + "Add sidenav button and improve mobile formatting." + ], + [ + "374926a5", + "1706356101", + "style", + "payment", + "Improve text, formatting, and usage hints." + ], + [ + "981227d8", + "1706009299", + "feat", + "aliases", + "Show/Hide domains list on aliases page" + ], + [ + "4f422edb", + "1705580705", + "style", + "welcome", + "Always show note about alternative email validation." + ], + [ + "316766bf", + "1705488551", + "style", + "aliases", + "More adjustments of introductory text." + ], + [ + "4ec54667", + "1705488551", + "style", + "aliases", + "Update introductory text and link." + ], + [ + "2034b2a7", + "1705487544", + "fix", + "aliases", + "Remove \"limits\" change, should be in another branch" + ], + [ + "9063f05d", + "1705407874", + "test", + "ordering", + "Supply quotas in tests, fix id vs class" + ], + [ + "238575ae", + "1705328735", + "feat", + "subscriptions", + "Make possible to re-hide expired products" + ], + [ + "7df1bcb1", + "1705320847", + "test", + "aliases", + "Adds domains to alias tests" + ], + [ + "edb22830", + "1704994763", + "feature", + "products", + "Product pages show usage hints" + ], + [ + "43b910e6", + "1704974364", + "fix", + "aliases", + "Display list of runbox domains on aliases page" + ], + [ + "4f2bc7de", + "1704968431", + "style", + "aliases", + "Update introductory text and link." + ], + [ + "014bd901", + "1704802913", + "fix", + "identities", + "Fix links to aliases pages" + ], + [ + "50ff96c1", + "1704457694", + "style", + "aliases", + "Make design more consistent and update links." + ], + [ + "ec93e677", + "1704390123", + "fix", + "account", + "Refetch / check userinfo less when activating pages" + ], + [ + "f8569df1", + "1704372484", + "style", + "aliases", + "Add note about impersonation." + ], + [ + "0c0ab0f2", + "1704369792", + "style", + "aliases", + "Add introductory text and move alias creation to top." + ], + [ + "9007fbe2", + "1704369792", + "refactor", + "aliases", + "Splits the template and css out into own files" + ], + [ + "97738b87", + "1704369792", + "feat", + "aliases", + "Add alias page functionality" + ], + [ + "84844c0c", + "1704126705", + "style", + "payment", + "Remove Holiday Offer 2023." + ], + [ + "f2278220", + "1702985679", + "test", + "api", + "Amends tests to not expect rest/me, now supplied by login" + ], + [ + "816c95b1", + "1702981768", + "fix", + "login", + "Redirect to subscriptions after login if began there" + ], + [ + "a22b53e0", + "1702816337", + "style", + "payment", + "Update payment links." + ], + [ + "89090e06", + "1702815528", + "style", + "payment", + "Clarify Holiday Offer." + ], + [ + "14d338a4", + "1701961686", + "fix", + "messagecache", + "Load message contents whenever we encounter them" + ], + [ + "7fdeb2c5", + "1700735342", + "style", + "payment", + "Fix typo." + ], + [ + "06627b86", + "1700735139", + "style", + "payment", + "Holiday Offer 2023." + ], + [ + "99edfec0", + "1700616969", + "style", + "aliases", + "Add introductory text and move alias creation to top." + ], + [ + "ed6ed86d", + "1699445870", + "refactor", + "aliases", + "Splits the template and css out into own files" + ], + [ + "a8ebb111", + "1697644785", + "fix", + "search", + "Cope with no index, use websocket and message fetch" + ], + [ + "e4cd14c0", + "1697542531", + "feat", + "aliases", + "Add alias page functionality" + ], + [ + "7f0fba2f", + "1696934230", + "fix", + "welcome", + "Make image URL relative." + ], + [ + "9c0e550b", + "1696929947", + "test", + "welcome", + "Fix test spec." + ], + [ + "032a1937", + "1696531255", + "fix", + "welcome", + "Remove unused component." + ], + [ + "f2c324f9", + "1696224836", + "test", + "welcome", + "Fix tests." + ], + [ + "b8fca2c8", + "1695961553", + "test", + "welcome", + "Update test spec." + ], + [ + "a90ee2c4", + "1695492874", + "style", + "payment", + "Clarify offer in graphic and text." + ], + [ + "687f01b2", + "1695347562", + "feat", + "welcome", + "Add introductory offer to Welcome Desk." + ], + [ + "ac9942d4", + "1695091456", + "test", + "mail", + "Update test after adding persistent menu on regular screens." + ], + [ + "c4e681bd", + "1695052499", + "fix", + "payment", + "Remove superfluous closing tag." + ], + [ + "b3baa12f", + "1695052098", + "style", + "menu", + "Position message action submenus." + ], + [ + "8ae21e82", + "1695052098", + "feat", + "mail", + "Add persistent message action menu above the message list." + ], + [ + "3399684c", + "1695051699", + "fix", + "payment", + "Show products correctly in the receipt." + ], + [ + "3a449b3e", + "1695051612", + "fix", + "payment", + "Remove invalid img parameter." + ], + [ + "da08796e", + "1695047675", + "style", + "payment", + "Show PayPal more prominently and improve formatting." + ], + [ + "dcdc64fc", + "1695047675", + "fix", + "drafts", + "Use this.tinymce_plugin instead of tinymce" + ], + [ + "90e6657d", + "1695047675", + "fix", + "subscriptions", + "Allow expired accounts to access cart+payment" + ], + [ + "14a0aa3c", + "1695047675", + "style", + "tooltip", + "Move more tooltips to the left and improve formatting." + ], + [ + "6f3a389b", + "1695047675", + "fix", + "accounts", + "Subscriptions links on account expiry" + ], + [ + "0de89f8d", + "1695047675", + "fix", + "account", + "Only subscription/renewal pages for expired accounts" + ], + [ + "07984f5e", + "1695047675", + "refactor", + "distinctUntilChanged", + "Use objectEqual in distinct* functions" + ], + [ + "fd3157ce", + "1695047675", + "fix", + "draft", + "Add distinctUntilChanged for auto save" + ], + [ + "32022fd9", + "1695047675", + "fix", + "tooltips", + "Move context tool button tooltips to the left side" + ], + [ + "3d957fbc", + "1695047675", + "fix", + "payment", + "Update postal address." + ], + [ + "ac001b74", + "1695047675", + "style", + "compose", + "Fix tooltips on draft save icon." + ], + [ + "e5adba61", + "1695047675", + "fix", + "messagecache", + "Delete old message cache DB" + ], + [ + "1df185e6", + "1695047675", + "refactor", + "messagecache", + "Replace .toPromise to firstValueFrom in test" + ], + [ + "5411921d", + "1695047675", + "fix", + "messagecache", + "Change MessageCache to require a user ID" + ], + [ + "0bcdddba", + "1695047675", + "fix", + "payments", + "Change \"legacy\" to \"alternative\"" + ], + [ + "aeab90b7", + "1695047675", + "fix", + "payments", + "Change \"payment not loading\" to \"not working\"" + ], + [ + "c426707e", + "1695047675", + "fix", + "payments", + "Always show \"payment not loading\" for stripe" + ], + [ + "36c43848", + "1695047675", + "style", + "compose", + "Improve and fix recently used recipients list." + ], + [ + "13fdb980", + "1695047675", + "fix", + "preview", + "Add tooltip for all senders button." + ], + [ + "70e1ad2a", + "1695047675", + "feat", + "search", + "Enable date range searches" + ], + [ + "050c7de9", + "1695047675", + "feat", + "search", + "Enable Unread Only checkbox only if other options set" + ], + [ + "1112a82f", + "1695047675", + "test", + "preview", + "Update test." + ], + [ + "747320eb", + "1695047675", + "style", + "preview", + "Improve HTML display buttons." + ], + [ + "67ec5c6a", + "1695047675", + "feat", + "search", + "Add more options to advanced search pane." + ], + [ + "76382415", + "1695047675", + "style", + "mailviewer", + "Increase vertical flexibility of subject field." + ], + [ + "974e4658", + "1695047675", + "fix", + "changelog", + "Add missing categories and improve formatting." + ], + [ + "ea6686e5", + "1695047675", + "style", + "mail menu", + "Improve read and unread icons." + ], + [ + "e116ecea", + "1695047675", + "style", + "preview", + "Improve formatting of the empty preview pane." + ], + [ + "9a8eaa15", + "1694685633", + "feat", + "compose", + "Add email-safe font selection to HTML/compose editor" + ], + [ + "bbae421c", + "1694518952", + "ci", + "npm", + "Don't upgrade npm at all" + ], + [ + "c68ce8e5", + "1694518850", + "ci", + "npm", + "Revert to \"normal\" npm version" + ], + [ + "c66d6d4d", + "1694518717", + "ci", + "node", + "Upgrade node to get built-in timers/promises" + ], + [ + "0c91ac93", + "1694517095", + "ci", + "npm", + "Ensure we install latest npm for CI tests" + ], + [ + "c213568f", + "1692369910", + "fix", + "drafts", + "Use this.tinymce_plugin instead of tinymce" + ], + [ + "2863a4cd", + "1691743409", + "fix", + "subscriptions", + "Allow expired accounts to access cart+payment" + ], + [ + "4b035c5e", + "1691072540", + "fix", + "accounts", + "Subscriptions links on account expiry" + ], + [ + "a59f3017", + "1690968483", + "style", + "tooltip", + "Move more tooltips to the left and improve formatting." + ], + [ + "9c523494", + "1690291932", + "fix", + "account", + "Only subscription/renewal pages for expired accounts" + ], + [ + "a85c2f44", + "1690203369", + "refactor", + "distinctUntilChanged", + "Use objectEqual in distinct* functions" + ], + [ + "9ccad048", + "1689962178", + "fix", + "draft", + "Add distinctUntilChanged for auto save" + ], + [ + "21f1dbd0", + "1688551139", + "fix", + "tooltips", + "Move context tool button tooltips to the left side" + ], + [ + "cd8353e6", + "1688296006", + "fix", + "payment", + "Update postal address." + ], + [ + "5e800c8b", + "1687267281", + "style", + "compose", + "Fix tooltips on draft save icon." + ], + [ + "a9f5d3aa", + "1687257629", + "fix", + "messagecache", + "Delete old message cache DB" + ], + [ + "63741f6a", + "1686918340", + "refactor", + "messagecache", + "Replace .toPromise to firstValueFrom in test" + ], + [ + "7cffb4d0", + "1686917959", + "fix", + "messagecache", + "Change MessageCache to require a user ID" + ], + [ + "d6c1dca8", + "1685959868", + "fix", + "payments", + "Change \"legacy\" to \"alternative\"" + ], + [ + "e137b26a", + "1685959618", + "fix", + "payments", + "Change \"payment not loading\" to \"not working\"" + ], + [ + "ff53567a", + "1685706786", + "fix", + "payments", + "Always show \"payment not loading\" for stripe" + ], + [ + "ccbf85bc", + "1685557702", + "style", + "compose", + "Improve and fix recently used recipients list." + ], + [ + "d86b6ebb", + "1684954214", + "fix", + "preview", + "Add tooltip for all senders button." + ], + [ + "45ee5be7", + "1684768969", + "feat", + "search", + "Enable Unread Only checkbox only if other options set" + ], + [ + "6e8acdb0", + "1684236466", + "feat", + "search", + "Enable date range searches" + ], + [ + "7cfda5f5", + "1684152643", + "test", + "preview", + "Update test." + ], + [ + "16b7a77a", + "1683896487", + "style", + "preview", + "Improve HTML display buttons." + ], + [ + "f2104acd", + "1683627593", + "feat", + "search", + "Add more options to advanced search pane." + ], + [ + "fd5c9023", + "1683457705", + "fix", + "changelog", + "Add missing categories and improve formatting." + ], + [ + "93e75ae1", + "1683455798", + "style", + "mail menu", + "Improve read and unread icons." + ], + [ + "364272bb", + "1683455035", + "style", + "mailviewer", + "Increase vertical flexibility of subject field." + ], + [ + "60fcacf4", + "1683382181", + "style", + "preview", + "Improve formatting of the empty preview pane." + ], + [ + "cfb732d6", + "1683124948", + "fix", + "maillist", + "Store and reload column widths from preferences" + ], + [ + "c512556e", + "1683039071", + "fix", + "compose", + "Convert reply/fwd text if compose HTML default is on" + ], + [ + "7afcbf7b", + "1683037155", + "fix", + "compose", + "Generate Reply/Fwd header text only when needed" + ], + [ + "9cf42796", + "1683021769", + "build", + "deps", + "bump @npmcli/arborist and npm" + ], + [ + "f359c388", + "1682607591", + "test", + "lint", + "Only include src/ else we run out of js heap memory" + ], + [ + "4ba7f7e4", + "1682001285", + "build", + "deps", + "Update cypress to v12" + ], + [ + "da761235", + "1681995916", + "fix", + "preferences", + "Load screensize before settings" + ], + [ + "d8766f82", + "1681995916", + "fix", + "preferences", + "Ensure higher server version takes precedence" + ], + [ + "7ca13b7a", + "1681995916", + "fix", + "preferences", + "Remove old style local storage after conversion" + ], + [ + "035f28c1", + "1681995916", + "test", + "preferences", + "Ensures tests set defaults, test correct values" + ], + [ + "0d9085e8", + "1681995916", + "fix", + "mailview", + "Store \"prompted for local index\" preference on server" + ], + [ + "dc1a75e4", + "1681995914", + "feat", + "compose", + "Store last used HTML compose setting" + ], + [ + "1409515e", + "1681995862", + "feat", + "all", + "Store user preferences on the server" + ], + [ + "480b5061", + "1681910773", + "build", + "deps", + "Update cypress to v11" + ], + [ + "d350af9c", + "1681910708", + "build", + "deps", + "Update cypress to v10" + ], + [ + "4285f0dc", + "1681910708", + "build", + "deps", + "Update eslint and @typescript-eslint" + ], + [ + "ee777544", + "1681910708", + "build", + "deps", + "Update start-server-and-test" + ], + [ + "0b7abb4b", + "1681910708", + "build", + "deps", + "Update ts-* deps and node types" + ], + [ + "da8a1515", + "1681910707", + "build", + "deps", + "Upgrade karma to latest version" + ], + [ + "d0084df5", + "1681910707", + "build", + "deps", + "Update jasmine to latest version" + ], + [ + "5ed129b8", + "1681910707", + "build", + "deps", + "Update rest of dependencies (excluding timymce)" + ], + [ + "478eb5ad", + "1681910707", + "build", + "deps", + "Update moment-timezone and remove uneeded @types/moment-timezone" + ], + [ + "8c527d86", + "1681910707", + "build", + "deps", + "Remove array-flat-polyfill" + ], + [ + "d4850f0d", + "1681910704", + "build", + "deps", + "Upgrade @angular/pwa" + ], + [ + "a87c0b38", + "1681731680", + "build", + "deps", + "Update rxjs to v7" + ], + [ + "a2733766", + "1681724704", + "build", + "deps", + "Upgrade angular-calendar" + ], + [ + "b4479725", + "1681724423", + "build", + "deps", + "Remove unused ajv dependency" + ], + [ + "4cd4bc2c", + "1681478484", + "build", + "deps", + "Upgrade @angular-devkit/build-angular and move angular/compiler-cli to devDependencies" + ], + [ + "b7c312b0", + "1681478449", + "build", + "deps", + "Remove unused protractor files" + ], + [ + "7aa65d36", + "1681478435", + "build", + "deps", + "Upgrade core.js to v3" + ], + [ + "6e0f9352", + "1681467686", + "build", + "deps", + "Remove unused protractor dependency" + ], + [ + "8441a515", + "1681464990", + "build", + "tests", + "Add DISPLAY='' to ci-tests" + ], + [ + "ab8b4375", + "1681382889", + "build", + "deps", + "remove --browser firefox from e2e tests" + ], + [ + "ff13958f", + "1681297936", + "build", + "deps", + "Upgrade npm, comment out pre-build.js integrity check" + ], + [ + "ab522f6d", + "1681297936", + "build", + "deps", + "Change default browser for CI to firefox" + ], + [ + "b35a68a1", + "1681297936", + "build", + "deps", + "Remove duplicate @include from SCSS" + ], + [ + "6dc73b25", + "1681297936", + "build", + "deps", + "Upgrade to angular material v15" + ], + [ + "23743364", + "1681297936", + "build", + "deps", + "Upgrade to angular v15" + ], + [ + "8b0c16f6", + "1681297936", + "build", + "deps", + "Update @angular-eslint packages" + ], + [ + "6a916d57", + "1681297936", + "build", + "deps", + "Fix missing hues for SCSS" + ], + [ + "c280c395", + "1681297936", + "build", + "deps", + "Upgrade to material v14" + ], + [ + "38761110", + "1681297936", + "build", + "deps", + "Upgrade angular-datetime-picker to v14" + ], + [ + "3096d970", + "1681297936", + "build", + "deps", + "Upgrade material to v13" + ], + [ + "3e04baa9", + "1681297936", + "build", + "deps", + "Manually upgrade to angular material v12" + ], + [ + "97f89cc4", + "1681297936", + "build", + "deps", + "Revert to angular/material v11 for auto upgrade" + ], + [ + "adef6c35", + "1681297936", + "build", + "deps", + "Upgrade angular/material to v13" + ], + [ + "1b072336", + "1681297936", + "build", + "deps", + "Explicitly specify runbox7 for build" + ], + [ + "cddac423", + "1681297936", + "build", + "deps", + "Upgrade to angular v14" + ], + [ + "2c11d606", + "1681297936", + "build", + "deps", + "Update dependant packages" + ], + [ + "ee83c8f2", + "1681297936", + "build", + "deps", + "Fix selectFile file path" + ], + [ + "9bf6ddae", + "1681297936", + "build", + "deps", + "Update ical.js to 1.5.0 and use ES2020 modules" + ], + [ + "c38f460b", + "1681297936", + "build", + "deps", + "Upgrade cypress to 9.7" + ], + [ + "ddebc3f4", + "1681297936", + "build", + "deps", + "Remove SingleMailViewerComponent from rmm6.module.ts" + ], + [ + "08afe91b", + "1681297936", + "build", + "deps", + "Remove --aot for start-use-mockserver" + ], + [ + "c010fca3", + "1681297936", + "build", + "deps", + "Move from tslint to eslint" + ], + [ + "ad7b36a4", + "1681297936", + "build", + "deps", + "Change swupdate.available to swupdates.versionUpdates" + ], + [ + "4c744c15", + "1681297936", + "build", + "deps", + "Upgrade to v13" + ], + [ + "2a878174", + "1681297936", + "build", + "deps", + "Upgrade angular-datetime-picker" + ], + [ + "a5cd077c", + "1681297936", + "build", + "deps", + "Update nodejs version for CI" + ], + [ + "11b3aeb0", + "1681297936", + "build", + "deps", + "Upgrade to angular 12" + ], + [ + "85aee649", + "1681297936", + "build", + "deps", + "Update angular2-hotkeys to v13" + ], + [ + "c575f066", + "1680560116", + "style", + "security", + "Specify which special characters are allowed in passwords. (#1401)" + ], + [ + "e44fc63f", + "1680091123", + "fix", + "compose", + "More readable attachment file size display" + ], + [ + "926ab4b2", + "1680021013", + "feat", + "mailviewer", + "Display incoming attachment sizes" + ], + [ + "5e83f894", + "1678706580", + "fix", + "delete", + "Catch/Prevent more errors by ensuring defaults" + ], + [ + "a50aa6d6", + "1677496696", + "style", + "folders", + "Increase width of folders modal." + ], + [ + "f024af2a", + "1676998474", + "fix", + "drafts", + "Ensure we only refresh drafts once per folders update" + ], + [ + "a5c5f854", + "1676891559", + "style", + "login", + "Simplify and improve login screen. (#1377)" + ], + [ + "c13796f3", + "1675853121", + "refactor", + "api", + "Filter for successes in te API folder calls" + ], + [ + "5fd354a3", + "1675771110", + "fix", + "folders", + "Ensures we refresh the folder list on a name change" + ], + [ + "50d52189", + "1675509619", + "fix", + "overview", + "Make Inbox selection more lenient." + ], + [ + "6952fa63", + "1675498473", + "style", + "payment", + "Change description of pending/incomplete transactions. (#1371)" + ], + [ + "0f6c62f6", + "1675399766", + "build", + "deps", + "bump http-cache-semantics from 4.1.0 to 4.1.1" + ], + [ + "ef32ab1a", + "1675357969", + "build", + "deps", + "bump jszip from 3.7.1 to 3.10.1" + ], + [ + "75df34d7", + "1674829506", + "build", + "deps", + "bump ua-parser-js from 0.7.31 to 0.7.33" + ], + [ + "02358ef2", + "1673863319", + "feat", + "overview", + "Improve time span options." + ], + [ + "35164985", + "1673283588", + "build", + "deps", + "bump moment-timezone from 0.5.28 to 0.5.35" + ], + [ + "ac8703ca", + "1673281867", + "build", + "deps", + "bump qs from 6.5.2 to 6.5.3" + ], + [ + "fe0405e6", + "1673276376", + "build", + "deps", + "bump luxon and rrule" + ], + [ + "6b8514b6", + "1673044712", + "build", + "deps", + "bump json5 from 1.0.1 to 1.0.2" + ], + [ + "2384b37d", + "1672946843", + "style", + "payment", + "Add link to Sub-account section." + ], + [ + "034dbb9e", + "1672767344", + "fix", + "messagelist", + "Log errors thrown by postMessage, keep worker alive" + ], + [ + "509839a4", + "1672766561", + "fix", + "messagelist", + "Ensure user-actions don't stop the index updates" + ], + [ + "89499bd0", + "1671467278", + "fix", + "maillist", + "Add error catching for the mail list updating" + ], + [ + "42a7a721", + "1671194923", + "style", + "all", + "Decrease font size and use space more efficiently and consistently." + ], + [ + "42a7a721", + "1671194923", + "style", + "all", + "Adjust menu and search areas further." + ], + [ + "42a7a721", + "1671194923", + "fix", + "contacts", + "Ensure contacts-app close sidemenu works" + ], + [ + "42a7a721", + "1671194923", + "fix", + "accounts", + "Fix acccounts, contacts \"close sideMenu\" button" + ], + [ + "42a7a721", + "1671194923", + "style", + "all", + "Adjust main menu font size and Settings header alignment." + ], + [ + "42a7a721", + "1671194923", + "test", + "Compose", + "Update test." + ], + [ + "42a7a721", + "1671194923", + "test", + "Compose", + "Update test." + ], + [ + "42a7a721", + "1671194923", + "test", + "Contacts", + "Update test." + ], + [ + "42a7a721", + "1671194923", + "fix", + "Contacts", + "Remove margins to prevent items from covering each other." + ], + [ + "42a7a721", + "1671194923", + "style", + "all", + "Adjust folder pane elements." + ], + [ + "ef603eff", + "1671188790", + "fix", + "inbox", + "Show notification when new messages appear" + ], + [ + "8640b1d9", + "1671024024", + "style", + "all", + "Decrease font size and use space more efficiently and consistently." + ], + [ + "8640b1d9", + "1671024024", + "style", + "all", + "Adjust menu and search areas further." + ], + [ + "8640b1d9", + "1671024024", + "fix", + "contacts", + "Ensure contacts-app close sidemenu works" + ], + [ + "8640b1d9", + "1671024024", + "fix", + "accounts", + "Fix acccounts, contacts \"close sideMenu\" button" + ], + [ + "8640b1d9", + "1671024024", + "style", + "all", + "Adjust main menu font size and Settings header alignment." + ], + [ + "8640b1d9", + "1671024024", + "test", + "Compose", + "Update test." + ], + [ + "8640b1d9", + "1671024024", + "test", + "Compose", + "Update test." + ], + [ + "8640b1d9", + "1671024024", + "test", + "Contacts", + "Update test." + ], + [ + "8640b1d9", + "1671024024", + "fix", + "Contacts", + "Remove margins to prevent items from covering each other." + ], + [ + "db21e68d", + "1670950047", + "feat", + "messagelist", + "Allow drag&drop from more table columns" + ], + [ + "633515db", + "1670947882", + "fix", + "messagelist", + "Ensure drag&drop of selected emails moves them all" + ], + [ + "f257902f", + "1670846605", + "refactor", + "index", + "Remove some index/worker console logging" + ], + [ + "f173b2c3", + "1670690255", + "build", + "deps", + "bump express from 4.17.1 to 4.18.2" + ], + [ + "e0637d9f", + "1670546639", + "build", + "deps", + "bump tinymce from 5.10.0 to 5.10.7" + ], + [ + "f6d83a1b", + "1670006052", + "build", + "deps", + "bump decode-uri-component from 0.2.0 to 0.2.2" + ], + [ + "9f38fd3e", + "1669092088", + "build", + "deps", + "bump engine.io and socket.io" + ], + [ + "788fb1a3", + "1667297120", + "fix", + "compose", + "Remove drag&drop to compose html window" + ], + [ + "ea1d9890", + "1666798105", + "fix", + "compose", + "Enable inserting attached files into HTML compose" + ], + [ + "87a06f92", + "1666797753", + "fix", + "compose", + "Enable drag&drop of images into HTML compose window" + ], + [ + "007b2d56", + "1666714218", + "fix", + "calendar", + "Setting \"all day\" flag on events now saves properly" + ], + [ + "02332883", + "1666628175", + "fix", + "messaging", + "Ensure msg fetching works after network is restored" + ], + [ + "3769663e", + "1666284784", + "fix", + "compose", + "Adds code checks to fix issues from sentry reports" + ], + [ + "bf44d900", + "1666183309", + "fix", + "compose", + "Only run one draft saving attempt at a time" + ], + [ + "014c462a", + "1666174590", + "fix", + "compose", + "Ensure we can attach same file twice in compose" + ], + [ + "ee47246a", + "1666025272", + "style", + "payment", + "Payment interface updates. (#1319)" + ], + [ + "09ed2e45", + "1666023446", + "fix", + "changelog", + "Ensure typos in commit entries do not break the page" + ], + [ + "ddeaf335", + "1665592528", + "test", + "calendar", + "Correctly ad/modify events in calendar service tests" + ], + [ + "6c806158", + "1665582547", + "refactor", + "index", + "Tidy up some debugging code" + ], + [ + "5bcb46d1", + "1665570412", + "fix", + "test", + "Update tests." + ], + [ + "6ed082ac", + "1665570412", + "style", + "payment", + "Correct button style declarations." + ], + [ + "bf9d7a2a", + "1665570412", + "style", + "payment", + "Improve tables and buttons, and default to USD." + ], + [ + "f5bd0eff", + "1665570412", + "style", + "payment", + "Clarify main accounts vs sub-accounts." + ], + [ + "1830cbba", + "1665570412", + "fix", + "index", + "Ensure we verify folder counts against the api" + ], + [ + "0a9f4b0b", + "1665570412", + "test", + "index", + "Tweak tests (and code to pass tests) for web workers" + ], + [ + "0c5470a1", + "1665570334", + "feature", + "index", + "Separate message updating into its own thread" + ], + [ + "24ac6d04", + "1665476576", + "fix", + "test", + "Update tests." + ], + [ + "4f4f50a0", + "1665474159", + "style", + "payment", + "Correct button style declarations." + ], + [ + "84dc7e94", + "1665473615", + "style", + "payment", + "Improve tables and buttons, and default to USD." + ], + [ + "e7a2c18e", + "1663354325", + "style", + "payment", + "Clarify main accounts vs sub-accounts." + ], + [ + "d4156c8b", + "1662629090", + "fix", + "searchservice", + "Fix compilation error" + ], + [ + "ac236542", + "1662473264", + "fix", + "api", + "Don't show errors while loading data in the background" + ], + [ + "7f25bbc2", + "1659965522", + "build", + "deps", + "bump moment from 2.29.2 to 2.29.4" + ], + [ + "d96715c6", + "1659962876", + "fix", + "tests", + "Github actions tests failing, try more heap size" + ], + [ + "f4502205", + "1659605753", + "fix", + "2fa", + "QRCodes for 2fa should be readable in more browsers" + ], + [ + "42349747", + "1658280231", + "build", + "deps", + "bump terser from 4.8.0 to 4.8.1" + ], + [ + "cdbef1c5", + "1658154040", + "fix", + "account-security", + "Load App Passwords switch enabled, if in use" + ], + [ + "969cad61", + "1657206851", + "fix", + "compose", + "Refreshing drafts should not current edited draft" + ], + [ + "4c153b34", + "1656951251", + "fix", + "folders", + "Ensure creating a new folder does not show above Inbox" + ], + [ + "7bf4fd1b", + "1656500141", + "fix", + "compose", + "Keep edited draft open after refresh" + ], + [ + "cd320b78", + "1656499636", + "fix", + "compose", + "Display compose drag/drop zone (again)" + ], + [ + "987904c7", + "1656426335", + "fix", + "folderlist", + "Check current folder counts for all folder types" + ], + [ + "582dd97e", + "1655996625", + "fix", + "compose", + "Display drop zone (again) when files are dragged over" + ], + [ + "7d7a2014", + "1655975558", + "fix", + "maillist", + "Ensure we display errors when api returns them" + ], + [ + "def65063", + "1655310137", + "fix", + "compose", + "Speed up drag&drop of attachments onto compose editor" + ], + [ + "0f4f4c90", + "1655290162", + "fix", + "compose", + "Ensure drafts refresh keeps compose in-progress" + ], + [ + "6b0c16d7", + "1655216299", + "fix", + "compose", + "Ensure we don't lose content of in-progress drafts" + ], + [ + "fde01e47", + "1654783558", + "fix", + "compose", + "Tidy up navigate \"back\" from compose" + ], + [ + "34588b28", + "1654710177", + "fix", + "compose", + "Only create new draft (new=true) once" + ], + [ + "fbcc2626", + "1654701646", + "test", + "all", + "Ensure we wait long enough in e2e tests" + ], + [ + "0814ba9d", + "1654701596", + "fix", + "compose", + "Keep any in-progress drafts when refreshing all drafts" + ], + [ + "4a033504", + "1654695944", + "fix", + "mailviewer", + "Don't try to go to the url msg id if no rows loaded" + ], + [ + "8727e1ff", + "1654045848", + "build", + "deps", + "bump eventsource from 1.1.0 to 1.1.1" + ], + [ + "b1a6556d", + "1653564037", + "fix", + "all", + "Remove unused imports (lint complains)" + ], + [ + "84298554", + "1653562264", + "refactor", + "api", + "Improve error/catching debugging on msg updates" + ], + [ + "c4a0c466", + "1653562001", + "fix", + "messagelist", + "Enable showing empty folders, when index is off" + ], + [ + "22644402", + "1653561541", + "fix", + "drafts", + "Ensure drafts sent/edited outside runbox7 are updated" + ], + [ + "1759231e", + "1653336946", + "build", + "deps", + "bump dexie from 3.0.3 to 3.2.2" + ], + [ + "8c70f1ed", + "1652963004", + "test", + "all", + "Fix dev test e2e issue (mockserver crashing)" + ], + [ + "0f2de331", + "1652962943", + "test", + "mailviewer", + "Fix tests hanging" + ], + [ + "1729a19d", + "1652888343", + "fix", + "mailviewer", + "Cope with non-standard js errors on email fetch" + ], + [ + "f7b0f72b", + "1652797882", + "fix", + "mailviewer", + "Output mail load error message strings" + ], + [ + "c79f9f55", + "1652267887", + "refactor", + "mailviewer", + "Improving the errors when an email won't load" + ], + [ + "e6d91ce3", + "1652201326", + "fix", + "compose", + "Allow a mix of contact and typed recipients" + ], + [ + "f8e89def", + "1652094154", + "fix", + "print", + "Print full width email with non-full viewpane" + ], + [ + "8b01d162", + "1652076860", + "style", + "mailviewer", + "Adjust message display option style and formatting." + ], + [ + "ddb04405", + "1651774097", + "fix", + "mailviewer", + "HTML status buttons indicate state has been saved (#1254)" + ], + [ + "31b64403", + "1651664116", + "fix", + "mailviewer", + "Ensure \"with images\" always turns them off/on" + ], + [ + "94dbc1e8", + "1651659314", + "feature", + "mailviewer", + "Store \"show html images\" per sender" + ], + [ + "4e88aaba", + "1651171182", + "build", + "deps", + "bump async from 2.6.3 to 2.6.4" + ], + [ + "2e32a9f9", + "1651058291", + "feature", + "mailviewer", + "Store \"show html images\" per sender" + ], + [ + "e43f2d1b", + "1650872865", + "style", + "login", + "Reorder links." + ], + [ + "e43f2d1b", + "1650872865", + "style", + "changelog", + "Improve formatting and add links to Runbox 7 forum." + ], + [ + "0dcc9daf", + "1650629194", + "fix", + "mailviewer", + "Default dates to 1970 where not supplied" + ], + [ + "eb40fecf", + "1650470615", + "fix", + "index", + "Ensure deleting index and resyncing doesnt stop updates" + ], + [ + "ec7f92ae", + "1650461235", + "fix", + "mailviewer", + "All email links open in a new tab" + ], + [ + "1bc86ee0", + "1649964712", + "style", + "login", + "Reorder links." + ], + [ + "1bc86ee0", + "1649964712", + "style", + "mailviewer", + "Add spacing between HTML view options." + ], + [ + "080d6dfa", + "1649930166", + "fix", + "mailviewer", + "Ensure html-images-feature doesn't break existing" + ], + [ + "d176d1ce", + "1649849637", + "feature", + "mailviewer", + "Do not load external HTML images unless asked" + ], + [ + "6e7f18cd", + "1649764773", + "refactor", + "messagelist", + "Cache calculates values as well as formatted" + ], + [ + "ac1c39ca", + "1649763303", + "fix", + "maillist", + "Ensure correct icons are displayed for answered msgs" + ], + [ + "a8a48966", + "1649759626", + "refactor", + "messagelist", + "Remove unused canvastable code" + ], + [ + "e840d874", + "1649578456", + "build", + "deps", + "bump moment from 2.25.3 to 2.29.2 (#1235)" + ], + [ + "636afb93", + "1649542464", + "build", + "deps", + "bump minimist from 1.2.5 to 1.2.6" + ], + [ + "b471a7ec", + "1648649490", + "refactor", + "mailviewer", + "Added DOMPurify for html sanitising" + ], + [ + "dfbe00b1", + "1648591623", + "fix", + "login", + "Always show \"forgot password\" link." + ], + [ + "dfbe00b1", + "1648591623", + "style", + "login", + "Reorder links." + ], + [ + "dfbe00b1", + "1648591623", + "style", + "login", + "Correct parameter order." + ], + [ + "2f1947ed", + "1647588400", + "style", + "login", + "Reorder links." + ], + [ + "8777beea", + "1647550888", + "fix", + "canvastable", + "Correctly render wrap-mode view on initial load (#1227)" + ], + [ + "f199c20d", + "1647550765", + "fix", + "login", + "Always show \"forgot password\" link. (#1229)" + ], + [ + "05c19a96", + "1647550735", + "fix", + "payment", + "Remove International Money Order as payment method. (#1228)" + ], + [ + "3c02a288", + "1647443194", + "fix", + "index", + "Update attachment flag on changes" + ], + [ + "13e4f2b9", + "1646664714", + "feat", + "mailviewer", + "Add Attachment List at the top of message" + ], + [ + "36c21a93", + "1646664680", + "style", + "mailviewer", + "Reduce size of HTML toggle area" + ], + [ + "5d9c6515", + "1646664065", + "style", + "mailviewer", + "Improve visibility and styling of attachment grid" + ], + [ + "cf19be5b", + "1646189686", + "build", + "deps-dev", + "bump karma from 6.3.2 to 6.3.16" + ], + [ + "d0929eea", + "1646066449", + "build", + "deps", + "bump url-parse from 1.5.7 to 1.5.10 (#1219)" + ], + [ + "24ecf964", + "1646066423", + "fix", + "canvastable", + "Don't show column resizing cursor while in wrap mode (#1217)" + ], + [ + "da3d7ffa", + "1646066413", + "fix", + "canvastable", + "Properly align the Size column in Search Message Display" + ], + [ + "da3d7ffa", + "1646066413", + "fix", + "canvastable", + "Change order of the Folder column to allow resizing the Size column" + ], + [ + "bb9efdab", + "1646066402", + "fix", + "loading", + "Open/load message from url only after messages loaded" + ], + [ + "bb9efdab", + "1646066402", + "fix", + "maillist", + "Re-enable index content verification" + ], + [ + "bb9efdab", + "1646066402", + "fix", + "index", + "Do not try and mark msgs already deleted from the index" + ], + [ + "bb9efdab", + "1646066402", + "fix", + "mailviewer", + "Support loading new url fragment" + ], + [ + "bb9efdab", + "1646066402", + "perf", + "index", + "Stop repeatedly looking up missing content" + ], + [ + "eb9e4458", + "1645784793", + "fix", + "domreg", + "Remove domain registration temporarily. (#1218)" + ], + [ + "12d4056f", + "1645538911", + "build", + "deps", + "bump url-parse from 1.5.3 to 1.5.7 (#1214)" + ], + [ + "9679db0e", + "1645098465", + "fix", + "mailview", + "Do not jump to top of list after updating index" + ], + [ + "755fcc27", + "1645096632", + "refactor", + "all", + "Rename searchResultsSubject to something more obvious" + ], + [ + "35064ac5", + "1645030605", + "fix", + "loading", + "Only refresh message list after index loaded (#1211)" + ], + [ + "f311df1b", + "1645029488", + "fix", + "mailviewer", + "Correct alignment of full/half height preview pane button. (#1209)" + ], + [ + "75d2d2a8", + "1645020768", + "build", + "deps", + "bump ssri from 6.0.1 to 6.0.2 (#1207)" + ], + [ + "cb250298", + "1645020054", + "fix", + "loading", + "Only refresh message list after index loaded (#1208)" + ], + [ + "46306f72", + "1645015643", + "build", + "deps", + "bump nanoid from 3.1.22 to 3.2.0 (#1180)" + ], + [ + "df7af23a", + "1645015166", + "build", + "deps", + "bump follow-redirects from 1.14.7 to 1.14.8 (#1205)" + ], + [ + "e86f3476", + "1645015158", + "fix", + "welcome", + "Add link to help from welcome message. (#1204)" + ], + [ + "0613c23f", + "1645015074", + "build", + "deps", + "bump log4js from 6.3.0 to 6.4.0 (#1179)" + ], + [ + "b5f549a2", + "1644402073", + "fix", + "mailview", + "Display dates for future-email, not just times" + ], + [ + "f5b0ed75", + "1644326251", + "fix", + "loading", + "Complete index load before displaying list from index" + ], + [ + "2f746973", + "1644244540", + "fix", + "tests", + "Mock test message times are in seconds (not millis)" + ], + [ + "0009a4c3", + "1643971255", + "fix", + "searchindex", + "Keep fetching recent updates" + ], + [ + "4ea96885", + "1643914014", + "fix", + "foldercounts", + "Ensure all folderpath names used are consistant" + ], + [ + "a159344e", + "1643651641", + "fix", + "contacts", + "Use anchor instead of button for VCF export." + ], + [ + "81044631", + "1643133342", + "fix", + "mailview", + "Remove deleted/moved messages before mesg list redraw" + ], + [ + "e9339899", + "1642984514", + "style", + "mail", + "Replace \"mark unread\" icon." + ], + [ + "6869c0e3", + "1642983848", + "style", + "search", + "Format multiple search fields better and move to top of screen." + ], + [ + "128e0de6", + "1642969061", + "style", + "compose", + "Make header field padding more consistent." + ], + [ + "6d09a4d9", + "1642965965", + "style", + "compose", + "Fix width and decrease vertical space." + ], + [ + "16fe3673", + "1642887902", + "style", + "contacts", + "Improve formatting and wording." + ], + [ + "6152bf53", + "1642526337", + "fix", + "mailviewer", + "Redirect to Contacts only when clicking the add icon" + ], + [ + "338ee02f", + "1642521810", + "fix", + "singlemailviewer", + "Use TemplateRef instead of Span for ngIfElse" + ], + [ + "08e6aeba", + "1642519962", + "fix", + "payment-cards", + "Change URL for Payment Cards" + ], + [ + "655d7e2e", + "1642518864", + "fix", + "payment-history", + "Remove whitespace in payment rows" + ], + [ + "500c3a79", + "1642502346", + "fix", + "mailviewer", + "Ensure we reload Sent contents after using Compose" + ], + [ + "233047a6", + "1642092296", + "build", + "deps", + "bump engine.io from 4.1.1 to 4.1.2" + ], + [ + "5c3a7966", + "1642060486", + "build", + "deps", + "bump follow-redirects from 1.13.3 to 1.14.7" + ], + [ + "53ee96de", + "1641378993", + "fix", + "login", + "Reinstate authentication error messages." + ], + [ + "8a2e99b1", + "1641205605", + "feat", + "payment", + "Remove Holiday Offer 2021." + ], + [ + "8a2e99b1", + "1641205605", + "feat", + "payment", + "Re-enable tests." + ], + [ + "b1132fa4", + "1640184132", + "fix", + "payment", + "Fix linting." + ], + [ + "3faa49d2", + "1640183917", + "feat", + "payment", + "Improve formatting and logic of Holiday Offer." + ], + [ + "ce366788", + "1640022063", + "feat", + "payment", + "Holiday Offer 2021." + ], + [ + "9852e27c", + "1639789598", + "feat", + "payment", + "Add Holiday Offer 2021." + ], + [ + "77489182", + "1639497033", + "style", + "condensed-layout", + "Fix size of Compose text area and message header." + ], + [ + "375dfb2d", + "1639491829", + "fix", + "credit-cards", + "Improve the look of payment forms on mobile" + ], + [ + "a776ee41", + "1639090274", + "style", + "condensed-layout", + "Improve formatting of sidenav for small screens." + ], + [ + "9a5e7d6c", + "1639088846", + "style", + "condensed-layout", + "Improve layout of drafts." + ], + [ + "2949393f", + "1639076841", + "test", + "mailviewer", + "Update test to correspond with new canvastable row heights." + ], + [ + "fafe3613", + "1639061998", + "fix", + "mailviewer", + "Properly show reply and forward headers for HTML messages" + ], + [ + "774192dd", + "1638988461", + "style", + "condensed-layout", + "Make vertical spacing more consistent across all screens." + ], + [ + "864bf48f", + "1638800595", + "style", + "condensed-layout", + "Improve formatting of Contacts search field." + ], + [ + "d8692855", + "1638799591", + "build", + "deps", + "bump ws from 6.2.1 to 6.2.2" + ], + [ + "5a16ff79", + "1638799306", + "build", + "deps", + "bump lodash from 4.17.20 to 4.17.21" + ], + [ + "f6c6e280", + "1638641525", + "fix", + "lint", + "Fix lint errors." + ], + [ + "4b8338f6", + "1638543999", + "fix", + "login", + "Complete rest of status error messages." + ], + [ + "46b979d5", + "1638228780", + "style", + "condensed-layout", + "Decrease size of various elements." + ], + [ + "59c5dab1", + "1638227021", + "style", + "condensed-layout", + "Improve formatting of message list options menu." + ], + [ + "29bd2d29", + "1638220519", + "fix", + "login", + "Improve login error messages and logic." + ], + [ + "7395676b", + "1638055800", + "fix", + "login", + "Fix e2e test error. Needs logic to distinguish trial users." + ], + [ + "1071e55e", + "1638043385", + "fix", + "login", + "Fix typo." + ], + [ + "e0b77669", + "1637959011", + "fix", + "your-subscriptions", + "Make subscriptions table usable on small screens" + ], + [ + "0b8399b1", + "1637956427", + "fix", + "lint", + "Fix lint errors." + ], + [ + "93a377ea", + "1637939452", + "fix", + "login", + "Show payment text and link depending on trial status." + ], + [ + "062fa3d5", + "1637936182", + "style", + "login", + "Update payment link and description." + ], + [ + "192778a0", + "1637849899", + "refactor", + "account-app", + "Fix code formatting" + ], + [ + "1cb37707", + "1637778868", + "style", + "condensed-layout", + "Decrease font size and horizontal size of draft previews." + ], + [ + "2995ef33", + "1637774638", + "style", + "condensed-layout", + "Decrease height of search bar and message action menu." + ], + [ + "690d6e1b", + "1637612114", + "style", + "condensed-layout", + "Condense top welcome and search bar vertically." + ], + [ + "4e72df0b", + "1637593866", + "build", + "deps", + "bump @npmcli/git from 2.0.6 to 2.1.0" + ], + [ + "43b46ecd", + "1637593813", + "build", + "deps", + "bump hosted-git-info from 3.0.7 to 3.0.8" + ], + [ + "244ecbaf", + "1637593807", + "build", + "deps", + "bump browserslist from 4.16.3 to 4.18.1" + ], + [ + "0d75918b", + "1637593799", + "build", + "deps", + "bump dns-packet from 1.3.1 to 1.3.4" + ], + [ + "9efd3be0", + "1637593600", + "build", + "deps", + "bump color-string from 1.5.4 to 1.6.0" + ], + [ + "cd056626", + "1637593597", + "build", + "deps", + "bump url-parse from 1.5.1 to 1.5.3" + ], + [ + "5a9ebfe0", + "1637358922", + "style", + "condensed-layout", + "Improve alignment of Mail left pane elements." + ], + [ + "b97cac3d", + "1637356218", + "style", + "condensed-layout", + "Improve alignment of left pane elements." + ], + [ + "88c91758", + "1637343292", + "build", + "deps", + "bump @npmcli/arborist from 2.0.2 to 2.10.0" + ], + [ + "3512b8bf", + "1637343292", + "build", + "deps", + "bump tar from 6.0.5 to 6.1.11" + ], + [ + "62d7605f", + "1637343291", + "build", + "deps", + "bump jszip from 3.4.0 to 3.7.1" + ], + [ + "19df238b", + "1637343284", + "build", + "deps", + "bump path-parse from 1.0.6 to 1.0.7" + ], + [ + "d962c84b", + "1637250516", + "fix", + "mailviewer", + "Fix horizontal scroll for iframe in message view" + ], + [ + "f2411611", + "1636626358", + "style", + "settings", + "Improve text and formatting. (#1124)" + ], + [ + "8dec702c", + "1636562667", + "fix", + "mailviewer", + "Access messageHeaderHTML only when loaded" + ], + [ + "5a3f3eb0", + "1636466828", + "style", + "condensed-layout", + "Decrease font sizes for more efficient use of space." + ], + [ + "ecc4cb33", + "1635958896", + "fix", + "mailviewer", + "Change reply header for HTML messages" + ], + [ + "0391ed38", + "1635958422", + "fix", + "mailviewer", + "Improve header formatting for forwarded HTML messages" + ], + [ + "f43bf22c", + "1635942333", + "feat", + "account-app", + "Remove renewal options from storage space products" + ], + [ + "e001f245", + "1635867895", + "build", + "deps", + "bump tinymce from 5.9.0 to 5.10.0" + ], + [ + "66be4811", + "1635852976", + "fix", + "messagelist", + "Store col resizing per view/set of columns" + ], + [ + "583d8930", + "1635763209", + "fix", + "search", + "Allow resizing of folder column" + ], + [ + "8d922672", + "1635336469", + "fix", + "mailviewer", + "Add horizontal scroll to message view" + ], + [ + "badee30d", + "1634920038", + "build", + "deps", + "bump tinymce from 5.7.1 to 5.9.0" + ], + [ + "92ac9565", + "1634904452", + "fix", + "bug_report", + "Fixes issue https://github.com/runbox/runbox7/issues/1077" + ], + [ + "d67d3981", + "1634858754", + "fix", + "bug_report", + "Fixes issue https://github.com/runbox/runbox7/issues/1077" + ], + [ + "5ec1dd32", + "1634818159", + "fix", + "canvas", + "Ensure scaling (for HiDPI displays) is consistent" + ], + [ + "dc3f0dad", + "1634741737", + "refactor", + "account-details", + "Fix typos" + ], + [ + "622809fd", + "1634741381", + "fix", + "account-app", + "Change description for Account Password tile" + ], + [ + "3670a52c", + "1634741327", + "feat", + "account-app", + "Add Account Details tiles to the main Account Settings page" + ], + [ + "dd78c937", + "1634738971", + "refactor", + "account-details", + "Change path and naming of account preferences" + ], + [ + "4ab8fc9e", + "1634730147", + "refactor", + "account-details", + "Replace moment.js timezones with custom timezones endpoint" + ], + [ + "954cb3cc", + "1634727848", + "build", + "npm", + "Add package to list countries with their ISO codes" + ], + [ + "a88378d2", + "1634565955", + "fix", + "index", + "Cope with emails with no plain text part" + ], + [ + "786f6f29", + "1634329538", + "refactor", + "account-details", + "Add a password prompt for Account Details changes" + ], + [ + "41f93e6f", + "1634329501", + "refactor", + "account-details", + "Improve selection of Countries and Timezones" + ], + [ + "b02298c4", + "1634033037", + "fix", + "identities", + "Only display \"origin\" field for RMM6 folders" + ], + [ + "e4bacb79", + "1633606320", + "fix", + "foldercounts", + "Update counts on index refresh, only when changed" + ], + [ + "9964446b", + "1633518433", + "fix", + "identities", + "Ensure identities can be told apart" + ], + [ + "dee596d7", + "1632942379", + "fix", + "mailviewer", + "Remove big icons from print view" + ], + [ + "7128d940", + "1632133404", + "fix", + "mailviewer", + "Show conditional tooltips on view options menu" + ], + [ + "3d7bfa3a", + "1631706200", + "feature", + "mailviewer", + "Enable \"unread only\" list mode without the index" + ], + [ + "f1e1c2ac", + "1631705911", + "feature", + "mailviewer", + "Enable display of msg preview without index" + ], + [ + "c8dfefab", + "1631211154", + "fix", + "app", + "Fit message list option menu on small mobile screens" + ], + [ + "cc904674", + "1631184293", + "fix", + "mailviewer", + "Mention contact support when showing message snackbar" + ], + [ + "f1fcf3c0", + "1631125074", + "fix", + "mailviewer", + "Remove big icons from print view" + ], + [ + "313d6bf7", + "1630686953", + "fix", + "mailviewer", + "Refreshing a msg id url should jump to item in list" + ], + [ + "20f35a04", + "1630686747", + "feature", + "mailviewer", + "Store/display any error messages on msg load" + ], + [ + "424bc802", + "1630408603", + "fix", + "mailviewer", + "Display image attachments in HTML unless inline" + ], + [ + "cdd43048", + "1630009753", + "style", + "start", + "Improve formatting." + ], + [ + "ad5ab1c7", + "1629893164", + "fix", + "mailviewer", + "Speed up regular update by only redrawing changes" + ], + [ + "426d75ca", + "1629885741", + "fix", + "mailviewer", + "Speed up regular update by recounting only changes" + ], + [ + "abedd29f", + "1629209364", + "fix", + "mailviewer", + "Allow message scrolling while pane is open (oops)" + ], + [ + "5304bc54", + "1629135686", + "fix", + "mailviewer", + "Ensure mail pane menu always visible" + ], + [ + "a3d536af", + "1629135318", + "fix", + "mailviewer", + "Msg llist can scroll to bottom when pane is open" + ], + [ + "79f257b7", + "1628590800", + "fix", + "mailviewer", + "Show attachments (including internal) in plain text mode" + ], + [ + "226ee3cb", + "1628159803", + "fix", + "mailviewer", + "Update maillist after delete/move" + ], + [ + "14eb7241", + "1627926925", + "fix", + "settings", + "Add missing Domain Registration tile" + ], + [ + "72141297", + "1627925091", + "refactor", + "account-details", + "Fix account settings updating" + ], + [ + "a10852a8", + "1627913188", + "build", + "deps", + "bump tinymce from 5.6.2 to 5.7.1" + ], + [ + "0b1dd117", + "1627737084", + "feat", + "app-passwords", + "use monospaced fonts for app passwords listing" + ], + [ + "28462657", + "1627552611", + "test", + "system", + "Upgrade Cypress to 7.7.0" + ], + [ + "617b3038", + "1627471366", + "test", + "rmmapi", + "Update tests to reflect checking of error status" + ], + [ + "7f10dac0", + "1627380933", + "fix", + "indexer", + "Continue indexing if a missing-body fetch fails" + ], + [ + "4444e50a", + "1626952950", + "fix", + "indexer", + "Allow user-actions to interrupt current index syncing" + ], + [ + "a96596a7", + "1626873593", + "refactor", + "account-app", + "Fix document formatting" + ], + [ + "4e623b18", + "1626873233", + "refactor", + "account-details", + "Wire account-settings table to the API" + ], + [ + "fa4fcc60", + "1626684795", + "fix", + "maillist", + "Ensure actions involving 2 folders update counts on both" + ], + [ + "89752d80", + "1626267304", + "feat", + "account-security", + "Add possibility to change Password" + ], + [ + "bd509bc1", + "1626171241", + "fix", + "mailviewer", + "Prevent HTML view having multiple scroll layers" + ], + [ + "99950a4c", + "1626089514", + "fix", + "compose", + "Switch to download prompt for files, to mitigate XSS" + ], + [ + "1deacea9", + "1625149655", + "feat", + "onscreen", + "replace placeholder buttons with a (locally) persistent meeting list" + ], + [ + "11387b9d", + "1625149655", + "fix", + "onscreen", + "make the URL reflect the meeting state" + ], + [ + "7292fe8c", + "1625149655", + "fix", + "contacts", + "only allow videocalls for @runbox addresses" + ], + [ + "45590d59", + "1625149655", + "feat", + "contacts", + "allow inviting people to videocalls" + ], + [ + "b9e40dcb", + "1625149655", + "refactor", + "contacts", + "simplify formarray editors" + ], + [ + "574c72ad", + "1625145206", + "style", + "settings", + "Fix Settings Menu title styles" + ], + [ + "0426b3c5", + "1625138806", + "fix", + "mailviewer", + "make attachments show up again" + ], + [ + "35093f1d", + "1625083192", + "fix", + "account-security", + "fix a compilation error" + ], + [ + "cb56491b", + "1625082957", + "ci", + "build", + "make sure build job fails if the app did not compile correctly" + ], + [ + "c3309996", + "1625079999", + "fix", + "mailviewer", + "Pre-load and cache sanitized HTML email content" + ], + [ + "c3309996", + "1625079999", + "fix", + "mailviewer", + "Show inline HTML images again." + ], + [ + "c3309996", + "1625079999", + "fix", + "mailviewer", + "Ensure HTML email displays properly" + ], + [ + "c3309996", + "1625079999", + "feat", + "app", + "Pre-cached HTML loads emails faster, replies&fwds improved" + ], + [ + "406c5032", + "1625071805", + "fix", + "settings", + "Fix position of Settings Menu title for mobile" + ], + [ + "feb80701", + "1624961041", + "fix", + "folders", + "Display top-level folders in user-sorted order" + ], + [ + "90b65790", + "1624480348", + "style", + "settings", + "Center icons in the left-hand menu" + ], + [ + "6c137b6c", + "1624473128", + "refactor", + "account-security", + "Comment out Last Logins" + ], + [ + "2a7635e1", + "1624469490", + "refactor", + "settings", + "Split Security into separate items" + ], + [ + "9d20c349", + "1624469479", + "refactor", + "settings", + "Fix formatting of account-welcome file" + ], + [ + "45a98ffa", + "1624469479", + "style", + "account-security", + "Add icons to Security menu items" + ], + [ + "e37f14b3", + "1624469478", + "test", + "account-security", + "Fix tests" + ], + [ + "b5cc93c1", + "1624469433", + "feat", + "account-security", + "Split Security into separate items, make dedicated Security menu" + ], + [ + "e8896afc", + "1624035597", + "style", + "account", + "Adjust formatting." + ], + [ + "eeb9a61c", + "1624034018", + "style", + "menu", + "Re-apply main menu transition adjustment." + ], + [ + "8920637d", + "1623872859", + "fix", + "bug-report", + "Improve content and formatting of template." + ], + [ + "8920637d", + "1623872859", + "fix", + "bug-report", + "Add name to recipient address and remove line breaks in template." + ], + [ + "8920637d", + "1623872859", + "fix", + "bug-report", + "Change recipient email address and subject line." + ], + [ + "8920637d", + "1623872859", + "style", + "bug-report", + "Update report template regarding support inquiries." + ], + [ + "8920637d", + "1623872859", + "style", + "bug-report", + "Improve template text further." + ], + [ + "8920637d", + "1623872859", + "fix", + "bug-report", + "Add note about the use of submitted reports." + ], + [ + "8704ef50", + "1623870196", + "style", + "welcome", + "Improve layout and make it more consistent. (#1021)" + ], + [ + "9a7ec64c", + "1623867183", + "refactor", + "account-details", + "Fix units in Data Usage table" + ], + [ + "4f319ee8", + "1623866863", + "fix", + "welcome", + "Set target on link to documentation." + ], + [ + "00718fe8", + "1623864930", + "refactor", + "account-details", + "Remove password change from the form" + ], + [ + "5664f03b", + "1623864538", + "refactor", + "account-details", + "Add ability to change timezones and country" + ], + [ + "081a36a3", + "1623857438", + "fix", + "welcome", + "Add missing file." + ], + [ + "f3794868", + "1623856201", + "fix", + "welcome", + "Add missing file." + ], + [ + "1cd4e416", + "1623855474", + "fix", + "lint", + "Fix linting error." + ], + [ + "04096155", + "1623852960", + "fix", + "webmail", + "temporarily revert #1003 and #1011 to fix HTML email rendering" + ], + [ + "0e269786", + "1623760524", + "fix", + "contacts", + "allow saving unnamed contacts" + ], + [ + "81d4a253", + "1623758742", + "fix", + "contacts", + "fix unnamed contacts being accidentally filtered out" + ], + [ + "d8985dab", + "1623758716", + "fix", + "contacts", + "make sure we're not accidentally silencing errors when saving contacts" + ], + [ + "b47e6d0f", + "1623694195", + "style", + "account", + "Re-apply changes lost in merge." + ], + [ + "0447e965", + "1623691646", + "style", + "welcom", + "Make formatting more consistent." + ], + [ + "16341982", + "1623606155", + "fix", + "welcome", + "Ensure Runbox 6 links open in new tab." + ], + [ + "dd935ecd", + "1623605647", + "style", + "welcome", + "Make formatting more consistent." + ], + [ + "307cc50a", + "1623603750", + "style", + "welcome", + "Improve layout and make it more consistent." + ], + [ + "933d2f1d", + "1623600039", + "style", + "payment", + "Improve text and icons. (#996)" + ], + [ + "0376a57a", + "1623142878", + "fix", + "identitys", + "show the \"delete\" button for identitys except for \"main\" & \"aliases\"" + ], + [ + "bddf99dc", + "1623063493", + "fix", + "mailview", + "Ensure we invalidate the message cache for HTML update" + ], + [ + "59590235", + "1623060617", + "fix", + "contacts", + "Contacts with company-name only now create correctly" + ], + [ + "a8d88361", + "1622812636", + "refactor", + "settings", + "Add missing menu items and adjust items order" + ], + [ + "7500bbb2", + "1622731613", + "test", + "app", + "Fix tests after rewording items across the app" + ], + [ + "6273af22", + "1622659535", + "style", + "account-app", + "Fix capitalization and wording" + ], + [ + "03802d0a", + "1622657902", + "style", + "account-security", + "Fix capitalization" + ], + [ + "b05d7c90", + "1622657238", + "style", + "profiles", + "Fix capitalization" + ], + [ + "d174e8d6", + "1622656923", + "style", + "calendar-app", + "Fix capitalization" + ], + [ + "ded0ef77", + "1622656709", + "style", + "contacts-app", + "Fix capitalization in menu and contant information panel" + ], + [ + "a8ba57d3", + "1622566947", + "fix", + "bug-report", + "Improve content and formatting of template." + ], + [ + "6f7d9444", + "1622540275", + "fix", + "mailviewer", + "Load HTML view faster by pre-caching it" + ], + [ + "84b964cd", + "1622479617", + "style", + "overview", + "Change default sorting and unread selectors, and improve their formatting." + ], + [ + "52f50971", + "1622216313", + "refactor", + "account-details", + "Wire up storage details page with API" + ], + [ + "15533c4f", + "1622215543", + "refactor", + "account-details", + "Fix naming for address and phone variables" + ], + [ + "729a5149", + "1622215543", + "fix", + "menu", + "Fix Settings URL" + ], + [ + "7afbece1", + "1622188150", + "fix", + "menu", + "Link logo to Welcome screen." + ], + [ + "8d0256aa", + "1622113175", + "fix", + "payments", + "fix coinbase's external payment link" + ], + [ + "d2138d5d", + "1621962097", + "refactor", + "domainregister", + "bring domreg under the /account/ namespace" + ], + [ + "92f8a8b5", + "1621873714", + "style", + "help", + "Add a toolbar to the help component" + ], + [ + "eb7d2fe0", + "1621873714", + "style", + "app", + "make the toolbar always visible for a more consistent look" + ], + [ + "4a8b4f4b", + "1621873714", + "refactor", + "account", + "port account-app to the new runbox-container" + ], + [ + "3c396493", + "1621869684", + "refactor", + "components", + "Redo runbox-container so that it fits our needs" + ], + [ + "2ad6ad79", + "1621849787", + "test", + "calendar", + "Debugging runbox-calendar-event tests" + ], + [ + "aa4ec506", + "1621606848", + "fix", + "changelog", + "make the changelog builder look inside possible squash commits" + ], + [ + "f8af652a", + "1621508997", + "fix", + "calendar", + "Display ical events with non-user timezones correctly" + ], + [ + "9b2a297b", "1621446065", "refactor", "start", "remove unnecessary import" ], [ - "e3a6b2c", + "e3a6b2c0", "1621347643", "fix", "bug-report", "Improve content and formatting of template." ], [ - "e3a6b2c", + "e3a6b2c0", "1621347643", "fix", "bug-report", "Add name to recipient address and remove line breaks in template." ], [ - "08451fe", + "08451fe8", "1621172514", "style", "overview", "Improve formatting and add sorting selector." ], [ - "08451fe", + "08451fe8", "1621172514", "style", "overview", "Add Sender to sorting selector." ], [ - "08451fe", + "08451fe8", "1621172514", "feat", "startdesk", "Wire up sorting by count & sender" ], [ - "08451fe", + "08451fe8", "1621172514", "refactor", "startdesk", "Simplify event handling" ], [ - "08451fe", + "08451fe8", "1621172514", "refactor", "overview", "Update the sender hilights component and put it to use" ], [ - "08451fe", + "08451fe8", "1621172514", "feat", "overview", "Implement show more/less" ], [ - "08451fe", + "08451fe8", "1621172514", "fix", "overview", "Fix dashdeskCards for mobile" ], [ - "08451fe", + "08451fe8", "1621172514", "style", "overview", "Remove wrapper-mat-card" ], [ - "08451fe", + "08451fe8", "1621172514", "fix", "overview", "Make updateCommsOverview public." ], [ - "2906cca", + "2906cca6", "1620817962", "refactor", "app", "remove unused imports, fixing the scrict build" ], [ - "1d37a79", + "1d37a79f", "1620815396", "test", "e2e", "TODO e2e tests that fail in headless mode" ], [ - "410c597", + "410c597d", "1620815384", "fix", "changelog", "Make changelog more resilient against our tech debt" ], [ - "62f0ed9", + "62f0ed92", "1620662407", "fix", "menu", "Fix Settings URL" ], [ - "f21bae0", + "86b7048c", + "1620661987", + "feat", + "account-details", + "Add form controls" + ], + [ + "0e0150f5", + "1620659168", + "feat", + "account-details", + "Add Account Details content to Runbox7" + ], + [ + "d00f3bb5", + "1620659127", + "style", + "app", + "Remove styles for mat-select and mat-icon that are not needed" + ], + [ + "f21bae06", "1620464909", "fix", "help", "Remove unnecessary code." ], [ - "da27986", + "da279863", "1620393690", "fix", "welcome", "Fix 'mat-icon' is not a known element error." ], [ - "e55a183", + "e55a183d", "1620392847", "fix", "welcome", "Fix 'mat-icon' is not a known element error." ], [ - "12063c7", + "12063c7e", "1620392517", "fix", "welcome", "Fix 'mat-icon' is not a known element error." ], [ - "ff0ec1a", + "ff0ec1a5", "1620391093", "fix", "welcome", "Fix 'mat-icon' is not a known element error." ], [ - "4c4c643", + "4c4c6439", "1620387680", "fix", "help", "Fix 'mat-icon' is not a known element error." ], [ - "2a838c8", + "2a838c8e", "1620384875", "fix", "welcome", "Fix 'mat-icon' is not a known element error." ], [ - "0cf90ee", + "0cf90eeb", "1620156060", "test", "settings", "Fix URLs for tests" ], [ - "fd07b3d", + "fd07b3df", "1620154933", "fix", "settings", "Fix URLs pointing to Your Subscriptions" ], [ - "e7486f7", + "e7486f70", "1620154933", "fix", "settings", "Fix URLs pointing to Payment History" ], [ - "950c6e0", + "950c6e02", "1620154918", "fix", "settings", "Fix URLs pointing to Plans & Upgrades" ], [ - "9a3dfae", + "9a3dfaea", "1620124515", "test", "calendar", "Ensure cal test works at beginning of month too" ], [ - "4ff7972", + "4ff7972e", "1620120562", "test", "searchservice", "Ensure all tests catch the deletedmessages call" ], [ - "c94cf63", + "c94cf635", "1620120562", "refactor", "mailviewer", "Update actions to update local view then backend" ], [ - "980409c", + "980409c6", "1620120562", "fix", "mailviewer", "Delete messages from messagelist too (before backend)" ], [ - "046c0af", + "046c0afd", "1620120562", "fix", "mailviewer", "Remove \"ExpressionChanged\" issue with fragment handling" ], [ - "4221fc5", + "4221fc59", "1620120562", "refactor", "mailactions", "Ensure we update UI first then backend" ], [ - "d119bf8", + "d119bf86", "1620120562", "fix", "mailview", "Ensure we update everything when deleting messages" ], [ - "4b22a93", + "4b22a936", "1620060374", "style", "header", "Replace Account icon and add Subscribe/Renew icon." ], [ - "34c96aa", + "34c96aa4", "1619791845", "style", "header", "Improve gradient and positioning." ], [ - "9111b9b", + "9111b9b3", "1619784675", "fix", "account", "make account URLs work under prefix again" ], [ - "4604abd", + "4604abdc", "1619698860", "style", "header", "Adjust transitions and clean up code." ], [ - "950cd7b", + "950cd7bd", "1619697176", "refactor", "app", "clear unused imports" ], [ - "0d43870", + "0d43870b", "1619696562", "style", "settings", "Make the cards wider for the titles to fit" ], [ - "2dfe4b5", + "2dfe4b58", "1619696162", "refator", "settings", "Fix HTML formatting" ], [ - "25df41c", + "25df41cf", "1619695989", "test", "settings", "Fix integration tests" ], [ - "c12a5fe", + "c12a5fe7", "1619695988", "refactor", "settings", "Remove Addons component" ], [ - "c353720", + "c3537205", "1619695988", "style", "settings", "Rename \"Subscriptions\" to \"Subscriptions & Payments\"" ], [ - "ca9ed54", + "ca9ed541", "1619695966", "style", "settings", "Change order of the Subscriptions menu" ], [ - "9bae710", + "9bae710e", "1619695842", "feat", "settings", "Merge \"Main Account Plans\" and \"Add-ons & Sub-Accounts\" into \"Plans & Upgrades\"" ], [ - "e9bf5b6", + "e9bf5b6f", "1619695790", "style", "settings", "Rename \"Active Products and Renewals\" to \"Your Subscriptions\"" ], [ - "b68ae69", + "b68ae697", "1619695728", "style", "settings", "Rename Transactions to Payment History" ], [ - "6fe5f70", + "6fe5f708", "1619695670", "style", "settings", "Rename Card Details to Payment Cards" ], [ - "54c4efe", + "54c4efe5", "1619691519", "style", "settings", "Replace missing icons. (#968)" ], [ - "39137b5", + "39137b5d", "1619614673", "feat", "help", "New Help Center with common help destinations." ], [ - "d8e1203", + "d8e12034", "1619537972", "style", "header", "Add effects for logo and Subscribe link." ], [ - "05cf223", + "05cf223c", "1619091392", "fix", "compose", "Ensure draftdesk froms are updated when edited" ], [ - "67f6288", + "67f62882", "1619037386", "feat", "support", "Added bug report button with a compose template (#951)" ], [ - "7d95e30", + "7d95e302", "1618998939", "fix", "mailviewer", "Maintain selected messages when new mail appears" ], [ - "4f4d6f2", + "4f4d6f28", "1618957477", "style", "header", "Adjust menu item placement." ], [ - "7958c82", + "7958c828", "1618918201", "fix", "mailviewer", "Sent folder now ignores \"unread only\" setting" ], [ - "e97a974", + "e97a9745", "1618912664", "fix", "mailviewer", "Calculate threaded count properly (was missing)" ], [ - "8fb9d21", + "8fb9d212", "1618848655", "fix", "profiles", "Add warning if user reached maximum number of aliases" ], [ - "2931895", + "2931895a", "1618848173", "fix", "profiles", "Remove alias count from identities page" ], [ - "1e665c2", + "1e665c2b", "1618848162", "fix", "profiles", "Add links to alias management" ], [ - "3a7eabe", + "3a7eabe5", "1618495714", "fix", "profiles", "Remove the ability to delete aliases in identities page" ], [ - "cda4e05", + "cda4e05b", "1618465099", "fix", "account_security", "account security unlock code modal is missing material css" ], [ - "6b31621", + "6b31621a", "1618433620", "fix", "settings", "Add hints for password input, and dialog modal" ], [ - "291b7bf", + "291b7bfc", "1618419315", "fix", "account", "Add a redirect for the old /components path" ], [ - "4fd2eed", + "4fd2eedc", "1618354075", "style", "header", "Show menu titles on hover." ], [ - "b820887", + "b820887e", "1618325527", "fix", "canvastable", "Make inline message previews work again" ], [ - "8d6e37b", + "8d6e37b2", "1618316328", "refactor", "account", "Rename addNewCard() to setupCreditCard()" ], [ - "82e34e4", + "82e34e47", "1618316075", "refactor", "profiles", "Add/edit helpful text to Identities page" ], [ - "984c593", + "984c593f", "1618234750", "feat", "dev", "Add a demo for activity indicator" ], [ - "83d7875", + "83d7875e", "1618234750", "feat", "dev", "Add a demo for the loading indicator" ], [ - "0dcf409", + "0dcf409d", "1618234750", "refactor", "dev", "Split dev.component into separate demos components, improve routing" ], [ - "fa53bfc", + "fa53bfc0", "1618219462", "fix", "credit-cards", "Make error popups messages more informative" ], [ - "758443c", + "758443c9", "1618218854", "feat", "account", "Add a way to add a new credit card to account" ], [ - "4e3fd94", + "4e3fd949", "1617818380", "feat", "account_security", "Create and show an unlock code when enabling 2FA in Runbox 7 issue 411" ], [ - "b18195a", + "b18195ad", "1617706145", "build", "deps", "Update to Angular 11" ], [ - "e45ff49", + "e45ff49d", "1617576250", "style", "security", "Enlarge only section header toggles." ], [ - "bf79d72", + "bf79d72e", "1617575389", "style", "security", "Enlarge toggles and improve formatting." ], [ - "c3df28a", + "c3df28ad", "1617304283", "style", "header", "Add descriptions on hover." ], [ - "956202c", + "3a2a9603", + "1617205806", + "feat", + "account-details", + "Add API calls for account details" + ], + [ + "956202c8", "1617198186", "fix", "calendar", "Remove some excessive logging from calendar-app" ], [ - "5333f7e", + "5333f7ec", "1617144296", "style", "account-security", "Remove header styles and make toggle switches darker." ], [ - "8391ef5", + "8391ef59", "1616743399", "style", "header", "Condensed header using less vertical space." ], [ - "0ce808c", + "0ce808c3", "1616623398", "refactor", "profiles", "Remove commented code" ], [ - "9c2ea48", + "9c2ea48c", "1616607190", "test", "account", "Fix e2e tests for Payment Methods" ], [ - "724524b", + "724524be", "1616601290", "refactor", "settings", "Remove unused account/components page" ], [ - "edb935b", + "edb935b7", "1616601075", "feat", "menu", "Remove Settings menu, add router to the Settings Hub instead" ], [ - "2fdf662", + "2fdf6628", "1616600623", "refactor", "profiles", "Display field errors for from_priorities on page" ], [ - "70de821", + "70de8210", "1616596984", "refactor", "profiles", "Specify interface for fetched profiles" ], [ - "14bf8f9", + "14bf8f93", "1616594976", "refactor", "profiles", "Reduce updateFromPriorities timeouts" ], [ - "f678490", + "f6784906", "1616594976", "refactor", "profiles", "Make selectCurrentDefault better" ], [ - "1f63f52", + "1f63f522", "1616594976", "style", "profiles", "Fix text formatting" ], [ - "10b77be", + "10b77be3", "1616594975", "style", "profiles", "Edit description for Other Identities" ], [ - "976676f", + "976676f9", "1616594975", "style", "profiles", "Remove description for Aliases" ], [ - "f479e49", + "f479e49a", "1616594975", "style", "profiles", "Edit header and description for Default Identity" ], [ - "aec6529", + "aec65299", "1616594975", "feat", "profiles", "Update identity type when selecting Default Identity" ], [ - "700d9e4", + "700d9e41", "1616594975", "feat", "profiles", "Add a way to choose default identity for compose" ], [ - "478e375", + "478e3754", "1616594975", "style", "app", "Remove custom styles for mat-select content" ], [ - "ce095f8", + "ce095f8a", "1616594861", "feat", "settings", "Add content to the main Settings page" ], [ - "de4038d", + "de4038da", "1616517293", "feat", "messagecache", "Make iDB cache more resilient to imperfect environments" ], [ - "677c6d8", + "677c6d82", "1616517257", "test", "e2e", "Test that cached messages don't get redownloaded after a page reload" ], [ - "9e0435e", + "9e0435ef", "1616511578", "feat", "webmail", "add an LRU cache for messages, preventing excessive memory usage" ], [ - "e7a14ff", + "e7a14ffc", "1616511578", "feat", "serviceworker", "Update caching rules" ], [ - "73ba4bf", + "73ba4bf4", "1616511578", "feat", "app", "Cache message contents in indexedDB" ], [ - "4952711", + "49527115", "1616170132", "test", "e2e", "Make sure (sub)accounts have sensible access rights" ], [ - "a4e49b3", + "a4e49b33", "1616169642", "test", "e2e", "Update Cypress to 6.x and catch up with changes" ], [ - "1ac6d26", + "1ac6d267", "1616155412", "fix", "account", "Make sure subaccounts can access account security and other not payment related paths" ], [ - "1f11432", + "1f114323", "1616080197", "feat", "onscreen", "Hide onscreen behind the Konami code" ], [ - "2d28de9", + "2d28de9a", "1616080197", "style", "onscreen", "Positioning adjustments" ], [ - "dd4f1e8", + "dd4f1e81", "1616080197", "feat", "onscreen", "basic jitsi integration" ], [ - "35a34b2", + "35a34b23", "1616075506", "feat", "overview", "Add usage tracking to Overview" ], [ - "366118f", + "366118fb", "1615899933", "ci", "github", "Run CI against pull requests too" ], [ - "24d3d79", + "24d3d79a", "1615850683", "style", "payment", "Replace cryptocurrency logo and improve formatting." ], [ - "c6b9ec0", + "c6b9ec01", "1615287237", "test", "policy", "Be more lenient in commit message tests" ], [ - "658dfe5", + "658dfe57", "1615285768", "test", "e2e", "Fix ordering tests" ], [ - "d5f8f9f", + "d5f8f9fd", "1615213409", "fix", "account", "Re-enable cryptocurrency payment option" ], [ - "60bc0df", + "60bc0df0", "1615212655", "fix", "changelog", "support 'feature' in addition to 'feat'" ], [ - "e4ee4c5", + "e4ee4c5a", "1615209611", "fix", "calendar", "Remove unused variables and imports" ], [ - "2e6eccc", + "2e6ecccc", "1614866057", "fix", "payments", "Switch cryptocoin payments to a new, non-bitpay API" ], [ - "75b7e0e", + "75b7e0e9", "1614082491", "test", "calendar", "Don't select default calendar (somehow fails)" ], [ - "d606187", + "d6061879", "1613998030", "fix", "mailview", "Don't display select-all checkbox on top of mail pane" ], [ - "9edc34b", + "9edc34b9", "1613995735", "refactor", "calendar", "Fix a couple of code syntax niggles" ], [ - "cf39473", + "cf394736", "1613758045", "feat", "settings", "Expansion panel in side-pane navigation" ], [ - "0204cea", + "0204cead", "1613757899", "style", "app", "Adjust custom styles for mat-expansion-panel" ], [ - "5c6aa8c", + "5c6aa8ce", "1613654627", "feat", "app", "redirect standalone identities and accountsecurity to the new hub" ], [ - "c479aed", + "c479aed4", "1613591077", "style", "account", "Change font formatting for more minimal look" ], [ - "5829753", + "5829753b", "1613587101", "fix", "account-app", "Fix a typo in Subscritions" ], [ - "cee8fd6", + "cee8fd69", "1613586152", "test", "calendar", "Amend tests to compare using absolute times" ], [ - "9d790a2", + "9d790a2e", "1613584976", "feature", "calendar", "Save user timezones with new events" ], [ - "028493f", + "028493f8", "1613582400", "style", "account-security", "Add Sessions table for mobile" ], [ - "0f8e538", + "0f8e5385", "1613582330", "style", "account-security", "Fix responsivity, add last-logins table for mobile" ], [ - "59da4e0", + "59da4e07", "1613564574", "refactor", "account-security", "Format stylesheet" ], [ - "65b9836", + "65b9836b", "1613477305", "feat", "payments", "disable Bitpay payment option" ], [ - "1bad8dd", + "1bad8dd4", "1613002883", "fix", "account_security", "the app password main switch variable was incorrect" ], [ - "edacafd", + "edacafda", "1612877352", "refactor", "calendar", "Make recur save type into an enum" ], [ - "db4f6b2", + "db4f6b21", "1612874671", "fix", "calendar", "Enable support for hanging exceptions in recurring code" ], [ - "de65c13", + "de65c13a", "1612452685", "feat", "webmail", "track instances of index removal" ], [ - "ee3bbd8", + "ee3bbd8e", "1612445655", "fix", "contacts", "Improve error strictness/messaging on contacts import" ], [ - "8223546", + "8223546a", "1612378612", "fix", "calendar", "Tidy up code niggles from review" ], [ - "17b33e4", + "17b33e4b", "1612356590", "fix", "calendar", "Ensure events are displayed chronologically" ], [ - "f98446e", + "f98446ed", "1612181391", "test", "calendar", "Add tests for setting recurrence values" ], [ - "588145e", + "588145e8", "1612175847", "fix", "calendar", "Improve styling and default values for event editing" ], [ - "a03cdd3", + "a03cdd36", "1611849032", "test", "calendar", "Initial tests for recurrence/exceptions" ], [ - "7a90c50", + "7a90c500", "1611596371", "feat", "identity", "make main identity editable" ], [ - "23a112f", + "23a112f6", "1611573797", "feat", "calendar", "Add ability to edit/create detailed recurring events" ], [ - "19642c6", + "19642c69", "1611332664", "fix", "tinymce", "Make icons load again" ], [ - "4e17ab1", + "4e17ab15", "1611157504", "fix", "tinymce_spellcheck", "fixes issue 480 where TinyMCE intercept right-click and breaks spell check" ], [ - "6dc2f01", + "6dc2f019", "1611050726", "feat", "webmail", "Monitor users' local index dialog decision" ], [ - "b533fbd", + "b533fbd7", "1611049274", "test", "calendar", "Ensure recurring event tests work at any time" ], [ - "34bfd43", + "34bfd434", "1611049274", "fix", "calendar", "Speed up generation/ui update of events into calendar" ], [ - "8a16819", + "8a16819b", "1611049274", "test", "calendar", "Update calendar tests to reflect code rewrite" ], [ - "5856051", + "58560516", "1611049274", "fix", "calendar", "Refactor ical event handling to treat each uid as one item" ], [ - "ad345bf", + "ad345bfc", "1611012181", "fix", "compose", "always ser a reply to when the \"From\" changes" ], [ - "8fb2dc4", + "8fb2dc4f", "1610640162", "feat", "account", "Include identities in the account settings sidemenu" ], [ - "6ee6b69", + "6ee6b69c", "1610639523", "feat", "account", "Integrate account security in the account sidemenu" ], [ - "46a0085", + "46a00850", "1610041071", "fix", "searchservice", "Fix test failures that Angular 10 uncovered" ], [ - "aae72df", + "aae72dfe", "1610036848", "fix", "mailviewer", "Remove an excessive icon description" ], [ - "d0e7013", + "d0e7013d", "1610036430", "build", "deps", "Update TinyMCE" ], [ - "c705941", + "c7059410", "1609956468", "style", "profiles", "Reposition warning message and make it more visible" ], [ - "5c33bf3", + "5c33bf33", "1609955994", "style", "profiles", "Make editor modal mobile friendly" ], [ - "d662503", + "d662503b", "1609940500", "refactor", "profiles", "Clean up code for editor modal" ], [ - "baaacd4", + "baaacd41", "1609865384", "test", "e2e", "Chase Angular 10 API (?) changes" ], [ - "0d2e26a", + "0d2e26ab", "1609865384", "build", "deps", "Update npm and some minor security vulns in dependencies" ], [ - "ac848ee", + "ac848eee", "1609865384", "build", "deps", "Update us to Angular 10" ], [ - "fae465f", + "fae465f2", "1609861816", "test", "cypress", "Retry failed tests to minimize disruptions caused by flapping tests" ], [ - "54a1566", + "54a15663", "1609861265", "refactor", "mobilequery", "simplify the backwards compatibility bits" ], [ - "3ab98a8", + "3ab98a8e", "1609860014", "style", "mobilequery", "Allow for phone/tablet/desktop layouts" ], [ - "87dc8a7", + "87dc8a76", "1609856081", "style", "profiles", "Make Identities page mobile friendly" ], [ - "3a3d09d", + "3a3d09d4", "1609784005", "style", "account-security", "Make app passwords usable on mobile" ], [ - "c042eae", + "c042eae4", "1609783457", "style", "account-security", "Fix name and mobile alignment for the status column in Services table" ], [ - "1a0840c", + "1a0840c1", "1609782142", "style", "account-security", "Fix minor alignment issue in trusted browsers form" ], [ - "6bf73d5", + "6bf73d54", "1609779851", "style", "account-security", "Don't wrap dates in Trusted Browsers table" ], [ - "0af9f4f", + "0af9f4fa", "1609779810", "style", "account-security", "Make Services table more mobile-friendly" ], [ - "c86b6d5", + "c86b6d59", "1609775016", "style", "account-security", "Replace trusted browsers table with a more responsive one" ], [ - "d15ecb2", + "d15ecb24", "1609775009", "refactor", "styles", "Move mobiletables.scss to regular styles" ], [ - "5a5ac23", + "5a5ac23a", "1609770328", "refactor", "profiles", "Clean up code for profiles lister" ], [ - "ede432c", + "ede432ca", "1609755653", "fix", "domreg", "update domain registration email quota messages" ], [ - "15110a4", + "15110a4d", "1609425336", "style", "account-security", "Improve responsiveness of trusted browser entry" ], [ - "bdfb472", + "bdfb472e", "1609424257", "style", "account-security", "Improve layout responsiveness for TOTP instructions" ], [ - "ae56892", + "ae56892b", "1609422822", "style", "account-security", "Make HTML code formatting consistent" ], [ - "c02a6b9", + "c02a6b9c", "1609422361", "style", "account-security", "Make section toggles lay out better on narrow screens" ], [ - "9fe8862", + "9fe88629", "1609419157", "style", "account", "Improve payment form responsiveness" ], [ - "99124e9", + "99124e94", "1609416009", "style", "account", "Make payment methods more responsive" ], [ - "54a89ac", + "54a89acb", "1609410939", "fix", "canvastable", "Be a bit more lenient when detecting message selection" ], [ - "0d9005d", + "0d9005df", "1609352997", "style", "account", "Make product cards more responsive" ], [ - "29af86d", + "29af86d0", "1609351039", "ci", "build", "Skip changelog generation in test builds" ], [ - "6c94147", + "6c941475", "1609350824", "style", "account", "Improve the cart listing on mobile screens" ], [ - "d194ed2", + "d194ed22", "1609347134", "style", "account", "Move the shopping cart icon to the toolbar in mobile view" ], [ - "c86f18e", + "c86f18e0", "1609346325", "feat", "account", "Make transactions page usable on mobile" ], [ - "cf35148", + "cf351482", "1609179530", "ci", "github", "Switch our CI to Github Actions" ], [ - "6de5d05", + "6de5d05e", "1608958300", "feat", "domreg", "In domain registration, check that user has domain quota available or show a warning" ], [ - "b850944", + "b8509449", "1608659654", "test", "mailviewer", "Fix horizontal pane tests" ], [ - "c6e471c", + "c6e471c3", "1608657921", "fix", "app", "Hide the overview button if no local index is available" ], [ - "ca6df8e", + "ca6df8e5", "1608654926", "feat", "usage", "Add usage stats for tracking the popularity of components/settings" ], [ - "a3e70ed", + "a3e70ed2", "1608653992", "fix", "mailviewer", "Fix disappearing mail viewer menu" ], [ - "eb81248", + "eb812488", "1608653543", "fix", "mailview", "Delete trash more efficiently (with less errors)" ], [ - "7aa415d", + "7aa415d7", "1607719516", "fix", "recursive_dynamic_builder", "lint errors fix" ], [ - "024cdd8", + "024cdd88", "1607707888", "style", "start", "Fix breakpoints for mobile" ], [ - "16dc144", + "16dc1441", "1607707352", "style", "start", "Condense layout in heading area" ], [ - "719dca1", + "719dca1e", "1607707283", "style", "start", "Move section title to the top bar for mobile" ], [ - "4b0ee07", + "4b0ee072", "1607707278", "style", "start", "Clean up and remove unused code" ], [ - "c06d088", + "c06d088e", "1607706971", "style", "start", "Remove heading and adjust the space in top area" ], [ - "ab921f3", + "ab921f39", "1607706970", "feat", "webmail", "Integrate startdesk as a webmail \"folder\"" ], [ - "d06c918", + "d06c918f", "1607706898", "fix", "startdesk", "Remove timeperiod-specific wording" ], [ - "4a6d927", + "4a6d927e", "1607346484", "fix", "calendar", "Ensure we show recurring events correctly color-tagged" ], [ - "4eb0dbb", + "4eb0dbbb", "1606932768", "feat", "builder", "remove unused var" ], [ - "9df845e", + "9df845ea", "1606932589", "feat", "builder", "runbox dynamic builder reserach" ], [ - "604b080", + "604b0806", "1606755348", "style", "startdesk", "Make folder selector a little more bearable on mobile" ], [ - "6456e69", + "6456e69c", "1606754979", "feat", "startdesk", "Implement folder selectors" ], [ - "8c630c8", + "8c630c83", "1606754903", "feat", "start", "Add folder selector." ], [ - "ff02e44", + "ff02e44d", "1606491896", "fix", "start", "Fix case sensitivity for address matching" ], [ - "05cf375", + "05cf375b", "1606410889", "style", "start", "Improve responsivity for mobile screens" ], [ - "d4c0d1e", + "d4c0d1ec", "1606304315", "fix", "contacts", "Only sync once during import of many contacts" ], [ - "4f2ce88", + "4f2ce882", "1605631408", "feat", "sentry", "Include user data in error reports" ], [ - "e7869bd", + "e7869bdc", "1605184489", "fix", "account_security", "fix modal typo s/reasions/reasons/" ], [ - "31550da", + "31550dab", "1605105624", "fix", "account", "fixup for 3679dc834adadad2ddb5a47f19c95c2d4e756785" ], [ - "3679dc8", + "3679dc83", "1605104411", "feat", "account", "Make renewals page usable on mobile" ], [ - "757c8c6", + "757c8c6f", "1604980728", "fix", "account_security", "total number of cols is 6 and not 7" ], [ - "e74b126", + "e74b1261", "1604935594", "feat", "account", "Add a way to view subaccounts associated with a product" ], [ - "047d954", + "047d9544", "1604915075", "fix", "accsec", "List FTP last on services" ], [ - "f8fd2d7", + "f8fd2d70", "1604912500", "feat", "account_security", "add missing main app password toggle" ], [ - "6642959", + "6642959b", "1604675325", "feat", "account", "Add a way to access account settings on mobile" ], [ - "f4200fc", + "f4200fc6", "1604634219", "fix", "account_security", "supress always block button" ], [ - "1202f29", + "1202f29f", "1604634135", "feat", "account_security", "update last logins labels. show success/fail insteac of 1/0" ], [ - "5b02e82", + "5b02e828", "1604631276", "feat", "account_security", "hide account security access control" ], [ - "2510cce", + "2510cce1", "1604506219", "fix", "mailview", "Show missing From content when not using local index" ], [ - "15ec137", + "15ec1376", "1604338572", "fix", "account", "Invert the condition checking for the limited time offer" ], [ - "c3ade40", + "c3ade40d", "1604338497", "fix", "account-timer", "Make sure RunboxTimer is always properly initialized" ], [ - "f7eecff", + "f7eecff9", "1604337889", "style", "payment", "Remove 20th Anniversary special offer promotion." ], [ - "5327a71", + "5327a71d", "1603988531", "test", "policy", "Relax commit log tests" ], [ - "6719171", + "6719171c", "1603987854", "test", "mailviewer", "Move the delays around to hopefully mitigate Travis fails" ], [ - "68ff6e6", + "68ff6e62", "1603986018", "test", "webmail", "Ensure emails can actually be read using URL navigation" ], [ - "0b50b53", + "0b50b536", "1603985060", "refactor", "calendar", "Remove and unused variable that broke the build" ], [ - "fa3a3fe", + "fa3a3fea", "1603985016", "fix", "webmail", "Fix URL navigation" ], [ - "832f150", + "832f150e", "1603810531", "fix", "cart", "Allow purchases (with warnings) even if unavailable products are in the cart" ], [ - "dca0fb5", + "dca0fb56", "1603810461", "refactor", "calendar", "Remove and unused variable that broke the build" ], [ - "d33129e", + "d33129e2", "1603369232", "fix", "contacts, calendar", "Warn user on incorrect import file types" ], [ - "957009b", + "957009bb", "1603361898", "refactor", "mailview", "Tweak to work with MessageDisplay in master" ], [ - "4039a50", + "4039a505", "1603359479", "refactor", "mailviewer", "Ensure we only get one copy of the select-menu" ], [ - "c4b78e8", + "c4b78e8c", "1603359479", "fix", "mailviewer", "Stop select-all-menu from jamming" ], [ - "32341f7", + "32341f7b", "1603359479", "feat", "mailviewer", "Add select-all option for selecting messages" ], [ - "300b1a9", + "300b1a9e", "1602510198", "fix", "cart", "Fix an exception upon viewing an empty cart" ], [ - "5cd0c1b", + "5cd0c1bb", "1602510057", "fix", "cart", "Fix a cart bug when two separate, identical products appear in it" ], [ - "d47ef2c", + "d47ef2cd", "1602150157", "fix", "calendar", "Editing an item displays the correct times" ], [ - "812a8a8", + "812a8a84", "1601594630", "style", "payment", "Add 20th Anniversary special offer promotion." ], [ - "4010a4b", + "4010a4b5", "1601497314", "fix", "account_security", "dont display bottom pane \"invalid password\" msg" ], [ - "4910ee7", + "4910ee7f", "1601485244", "fix", "start", "Comment out panel mockups from the view" ], [ - "5860a7f", + "5860a7f6", "1601482817", "test", "mailviewer", "Add delays for canvas to load" ], [ - "635e2e0", + "635e2e01", "1601480213", "fix", "webmail", "Select loaded row url correctly on refresh" ], [ - "ec7c0dd", + "ec7c0dd3", "1601351861", "fix", "account security", "use routerLink in the account security link" ], [ - "5fd31c5", + "5fd31c5d", "1601334105", "feat", "account_security", "Update account security menu link" ], [ - "2fd7b08", + "2fd7b08d", "1601307552", "build", "config", "Disable noUnusedLocals in development builds" ], [ - "cedbecd", + "cedbecd1", "1601304491", "fix", "account", "Don't require micro accounts to purchase addons for own domain usage" ], [ - "5af68c5", + "5af68c51", "1600879761", "test", "mailviewer", "Add a delay for reply/forwarding emails" ], [ - "00becc2", + "00becc21", "1600878885", "feat", "profiles", "Order Froms by from_priority" ], [ - "31cf724", + "31cf7240", "1600862266", "feat", "dkim", "added a \"note\" about selector2 and when it will become active" ], [ - "c6773e9", + "c6773e98", "1600148534", "fix", "accsec", "lint fix" ], [ - "ccceb43", + "ccceb434", "1600147633", "feat", "accsec", "improve password validation and error messages on Account Security to avoid confusion" ], [ - "e044ba5", + "e044ba51", "1600113475", "feat", "dialog", "Allow submitting dialogs with Enter/Return key" ], [ - "ddc5511", + "ddc5511e", "1599745548", "fix", "canvastable", "Make sort icons show actual sorting direction" ], [ - "13882ff", + "13882ff5", "1599738467", "fix", "webmail", "Select correct row when refreshing page using fragment" ], [ - "b166418", + "b1664180", "1599738467", "refactor", "all", "Retrieve MessageInfo and MailAddressInfo" ], [ - "60d7b45", + "60d7b450", "1599646104", "refactor", "mockserver", "Remove code related to defalt profile" ], [ - "cfaf9ea", + "cfaf9ea7", "1599645561", "refactor", "rmmapi", "Remove unused call for getting default profile" ], [ - "79ac35e", + "79ac35e4", "1599154045", "refactor", "app", "Remove some unused variables" ], [ - "99465ce", + "99465ce8", "1599153351", "style", "app", "Remove obsolete instances of s" ], [ - "4fede1d", + "4fede1d3", "1599153351", "feat", "webmail", "add a way to save and reuse searches" ], [ - "ac6a359", + "ac6a359a", "1599055399", "build", "deps", "Switch to a more official runbox-searchindex" ], [ - "769c64a", + "769c64a3", "1598970361", "refactor", "app", "Remove some unused variables" ], [ - "09186ed", + "09186ed6", "1598957729", "refactor", "folderlist", "Removed an unused variable" ], [ - "65b6505", + "65b65053", "1598944160", "fix", "startdesk", "Fix linter and policy errors" ], [ - "170747b", + "170747b6", "1598938629", "feat", "startdesk", "Make the startdesk available with the Konami code" ], [ - "c635822", + "c6358222", "1598880109", "refactor", "app", "Get rid of unused variables, add a compiler rule for them" ], [ - "961c40e", + "961c40e1", "1598876627", "test", "unit", "Remove deprecated injector usage" ], [ - "4284fa6", + "4284fa6b", "1598871013", "refactor", "xapian", "Simplify the discrepancy check logic" ], [ - "017e5e6", + "017e5e68", "1598869455", "fix", "folders", "Improve folder count reliability in some edge cases" ], [ - "9d7a7dd", + "9d7a7dd3", "1598563420", "fix", "canvastable", "Change cursor style to \"pointer\"" ], [ - "adf680c", + "adf680cf", "1598558049", "fix", "canvastable", "Show \"select all messages\" button in wrapped view" ], [ - "5ce08b2", + "5ce08b25", "1598546459", "fix", "compose", "Pick correct default email for compose" ], [ - "a10443d", + "a10443d5", "1598537339", "fix", "menu", "Hide account security until its backend issues are resolved" ], [ - "0c25bdb", + "0c25bdb4", "1598519677", "fix", "accounts", "Code typo" ], [ - "86efb4f", + "86efb4f0", "1598459384", "fix", "startdesk", "Fix sidemenu toggle" ], [ - "69726b9", + "69726b9a", "1598458657", "feat", "startdesk", "Add timespan and unread selectors" ], [ - "c8076c9", + "c8076c9b", "1598444183", "fix", "mailviewer", "Load mailpane status (vert/horiz) on page load" ], [ - "6d49c6b", + "6d49c6bd", "1598444144", "test", "mailviewer", "Ensure reload of page in horizontal mode works" ], [ - "22df543", + "22df5438", "1598372803", "refactor", "accounts", "Timer compiles/runs" ], [ - "47f8c3a", + "47f8c3a0", "1598369344", "feat", "startdesk", "Populate Communication Overview with search index data" ], [ - "c46a63b", + "c46a63b0", "1598300484", "fix", "compose", "Set focus in textarea for replies" ], [ - "4f97ab5", + "4f97ab59", "1598280903", "feat", "startdesk", "Promote startdesk to its own page" ], [ - "295a89d", + "295a89dd", "1598025899", "style", "start", "Improve layout and CSS formatting." ], [ - "751af3b", + "751af3b7", "1598021730", "fix", "contacts-app", "Correct filtering options background color" ], [ - "c323057", + "c323057e", "1598020945", "fix", "contacts-app", "Fix positioning of email contact icon" ], [ - "3af5cbb", + "3af5cbb6", "1598020438", "fix", "contacts-app", "Make middle column width adjust to its content" ], [ - "202c908", + "202c9083", "1597931854", "feat", "account-security", "add account security" ], [ - "e41fde4", + "e41fde48", "1597748364", "test", "all", "Silence test issues with icons" ], [ - "71805fb", + "71805fb5", "1597675276", "fix", "mailview", "Correct \"show all headers\" icon" ], [ - "2ee86c9", + "2ee86c98", "1597664495", "fix", "webmail", "Update local (non-index) folder counts on refresh" ], [ - "1585750", + "15857507", "1597492256", "feat", "login", "Set inputmode to show email and numeric keyboards on mobile as appropriate" ], [ - "8f11185", + "8f11185a", "1597428893", "feat", "webmail", "Always show popular recipients component if enabled, even with no local index" ], [ - "1d8be95", + "1d8be959", "1597427348", "fix", "account", "Make /account show the same thing as /account/components" ], [ - "9e3d6bd", + "9e3d6bdb", "1597425260", "fix", "account", "Redirect domain renewals to domain registration app" ], [ - "8fe151c", + "8fe151c6", "1597343237", "feat", "webmail", "Change icons and tooltips when a message is deleted from trash" ], [ - "1ea546f", + "1ea546f7", "1597342831", "refactor", "app", "Rename trashMessages() to deleteMessages()" ], [ - "604cc2a", + "604cc2a6", "1597322208", "feat", "compose", "Add debug logs for measuring impact of recipient loading" ], [ - "af62c83", + "af62c830", "1597250789", "feat", "folder", "Speed up folder size calculations" ], [ - "fe67516", + "fe67516e", "1597228540", "fix", "webmail", "Ensure folder counts are updated after read/unread" ], [ - "871cd9f", + "871cd9ff", "1597157246", "style", "start", "Improve vertical spacing." ], [ - "4c18dbe", + "4c18dbe8", "1597073799", "test", "cypress", "Fix tests broken by icon changes" ], [ - "cbc56b7", + "cbc56b73", "1597070559", "feat", "webmail", "Separate read/unread, flag/unflag in multi-menu" ], [ - "537ab85", + "537ab856", "1597062038", "refactor", "all", "Switch to using Material Design Icons SVG font" ], [ - "39f6e0e", + "39f6e0e7", "1596739081", "build", "xapian", "Evict runbox7lib to runbox-searchindex" ], [ - "d3f02f6", + "d3f02f60", "1596647200", "style", "start", "Improve layout of communication overview." ], [ - "f92cdbf", + "f92cdbf7", "1596627729", "feat", "app", "Speed up the way sidemenu appears on the webmail page" ], [ - "1071075", + "10710759", "1596616531", "test", "activity-indicator", "Add unit tests for " ], [ - "cbb406c", + "cbb406c6", "1596616531", "feat", "app", "Implement indicators for multipart background activities" ], [ - "90b1a7b", + "90b1a7b4", "1596616531", "feat", "contacts", "Add background activity indicator" ], [ - "034521f", + "034521f2", "1596616478", "refactor", "calendar", "Tear out activity tracker and component into something reusable" ], [ - "151d5f4", + "151d5f49", "1596455161", "fix", "mailviewer", "React to avatar settings as soon as they change" ], [ - "0d6e77c", + "0d6e77c1", "1596193074", "refactor", "app", "Introduce AppSettingsService to reduce settings-related code duplication" ], [ - "1366014", + "1366014c", "1596190147", "feat", "contacts", "Add a tooltip to picture upload button if gravatars are disabled" ], [ - "21783a6", + "21783a62", "1596189697", "fix", "contacts", "Hide pictures in contact details if they're disabled" ], [ - "e2d1e4c", + "e2d1e4cf", "1596130402", "fix", "identitys", "make main identity email field read only" ], [ - "36030eb", + "36030eba", "1596099373", "feat", "contacts", "Add avatar settings to Contacts settings" ], [ - "29347eb", + "29347ebb", "1596035729", "style", "compose", "Show suggested recipients with light gray background. (#693)" ], [ - "c896536", + "c8965367", "1595948097", "feat", "mailviewer", "Separate mark unread/read, flag/unflag actions" ], [ - "a0f7111", + "a0f71116", "1595942263", "feat", "start", "Add Communication Overview." ], [ - "58ea3a0", + "58ea3a0a", "1595927355", "refactor", "mailviewer", "Fix a minor Angular whinge in mailviewer" ], [ - "8e514c5", + "8e514c57", "1595927339", "fix", "webmail", "Redraw folders properly after new item completed." ], [ - "1149cc3", + "1149cc39", "1595856895", "fix", "contacts", "Scroll details to top when new contact is selected" ], [ - "a53e069", + "a53e0699", "1595604705", "feat", "contacts", "Add settings to adjust avatar use in the app" ], [ - "02eb0ec", + "02eb0ec1", "1595590212", "feat", "mailviewer", "Use pictures from contacts when available" ], [ - "bb01f71", + "bb01f71f", "1595590212", "feat", "contacts", "Allow uploading/deleting contact pictures" ], [ - "830e88c", + "830e88c9", "1595590212", "feat", "contacts", "Show pictures/gravatars on contact details page" ], [ - "5a156c1", + "5a156c11", "1595590212", "feat", "mailviewer", "Show gravatars when available" ], [ - "81b0009", + "81b0009e", "1595589119", "style", "webmail", "Add a tooltip for webmail settings button" ], [ - "10e27fb", + "10e27fbf", "1595588500", "fix", "webmail", "Make sure we can still use saved searches when no folder is selected" ], [ - "d7a82de", + "d7a82de4", "1595512184", "fix", "contacts", "Make contacts draggable again" ], [ - "a16b522", + "a16b522c", "1595509462", "test", "contacts/e2e", "Fix contacts tests for the new layout" ], [ - "8f3cf1d", + "8f3cf1d7", "1595506723", "fix", "webmail", "Fix switching folders not working in some cases" ], [ - "8cb21d3", + "8cb21d3a", "1595435877", "feat", "contacts", "Add hints to columns indicating what they're for when they're empty" ], [ - "432520e", + "432520ee", "1595435849", "refactor", "contacts", "Move contacts-related CSS to contacts-specific file" ], [ - "3964112", + "3964112e", "1595432985", "style", "contacts", "Minor layout fixes to contactlist" ], [ - "86085b4", + "86085b42", "1595432951", "style", "contacts", "Make the 3-column layout more rigid" ], [ - "11129d0", + "11129d01", "1595421885", "fix", "contacts", "Fix a template crash when deleted contacts exist as group members" ], [ - "78b5e04", + "78b5e043", "1595350931", "style", "calendar-app", "Side-nav menu styles fix" ], [ - "ceaf757", + "ceaf757a", "1595350897", "style", "contacts-app", "Side-nav menu styles fix" ], [ - "958d365", + "958d3658", "1595343111", "fix", "contacts", "Make contactlist scroll independently of contact details" ], [ - "3b74494", + "3b74494e", "1595341846", "feat", "webmail", "Show folder count for drafts" ], [ - "c00b4e5", + "c00b4e51", "1595331197", "feat", "webmail", "Add webmail settings, allowing the disabling of popular recipients" ], [ - "26f1f8a", + "26f1f8a5", "1595323849", "refactor", "storageservice", "Add an API for observing storage changes" ], [ - "9bf6be9", + "9bf6be9e", "1595003328", "style", "webmail", "Move message action menu to middle column" ], [ - "56e052b", + "56e052b2", "1594999107", "style", "compose", "Differentiate \"Recently used\" from recipients" ], [ - "5b41170", + "5b411708", "1594650444", "style", "compose", "Differentiate \"Recently used\" from recipients" ], [ - "537ad5a", + "537ad5aa", "1594292320", "fix", "compose", "From-specific reply-to addresses saved/stored if setup" ], [ - "498ee47", + "498ee478", "1594052876", "feat", "contacts", "Add a way to edit group members from the group page in mobile view" ], [ - "5c737b4", + "5c737b40", "1594037082", "fix", "mailviewer", "Remove empty menu entry for non-HTML messages" ], [ - "f0da763", + "f0da763f", "1594033287", "fix", "compose", "CC field is expanded on reply-all, if it has content" ], [ - "1d6dfd6", + "1d6dfd61", "1594028675", "refactor", "compose", "MailAddressInfo is now in its own file" ], [ - "35da356", + "35da3561", "1594027902", "feat", "compose", "Compose now accepts pasting email lists recipients" ], [ - "3a1865c", + "3a1865cd", "1594027902", "fix", "common", "Fix edge-case email address (list) parsing" ], [ - "6839f02", + "6839f028", "1594027902", "refactor", "xapian", "Extract MailAddressInfo class and add tests" ], [ - "a458ba3", + "a458ba33", "1593625110", "fix", "compose", "Different positions for action buttons for mobile and desktop" ], [ - "b235d32", + "b235d326", "1593622676", "fix", "compose", "Push draft action buttons further apart" ], [ - "7613b4b", + "7613b4b0", "1593519445", "fix", "webmail", "Only recount folder unread counts after content change" ], [ - "45f4214", + "45f42148", "1593512756", "refactor", "contacts", "Pull out contactlist to its own component" ], [ - "77fe7da", + "77fe7da1", "1593509969", "fix", "reply", "Reply to all uses reply-to header if present" ], [ - "36d173c", + "36d173c1", "1593457571", "feat", "webmail", "Reset search when switching folders" ], [ - "1b88947", + "1b88947c", "1593452340", "feat", "contacts", "Implement a 3-column layout for contacts" ], [ - "d70d39e", + "d70d39e6", "1593369552", "style", "start", "Add examples and indicators" ], [ - "28f10dc", + "28f10dcb", "1593118616", "fix", "compose", "Make layout more responsive" ], [ - "33f0ba0", + "33f0ba0d", "1593000221", "feat", "webmail", "Add a list of popular email recipients to the sidebar" ], [ - "5e700dc", + "5e700dc1", "1593000221", "fix", "compose", "Update recipient suggests whenever searchindex is updated" ], [ - "478fa35", + "478fa358", "1592941996", "fix", "compose", "update angular deprecated recommendations" ], [ - "3fd771e", + "3fd771e7", "1592941481", "fix", "mark_multiple_msgs", "update before the request is completed" ], [ - "d2bbc5d", + "d2bbc5d9", "1592940282", "fix", "mark_multiple_messages", "try to use messageFlagChangeSubject.next to fix e2e errors" ], [ - "82339bf", + "82339bf1", "1592940220", "fix", "compose", "update angular deprecated recommendations" ], [ - "94c1108", + "94c11088", "1592931581", "feat", "multiple_msg_unread", "replace endpoint that marks multiple messages as unread/unflag" ], [ - "b70c859", + "b70c8591", "1592928828", "refactor", "app", "Rename searchFieldKeyUp to searchFor to better reflect what it's doing" ], [ - "381981c", + "381981c1", "1592919068", "fix", "compose", "Make sure suggested contacts are shown with their names" ], [ - "93f7fe4", + "93f7fe4b", "1592911608", "fix", "compose", "Make sure we can still drag and drop suggestions to CC/BCC and have them show up" ], [ - "aacc7ae", + "aacc7aeb", "1592827606", "fix", "compose", "Reload CC and BCC contents properly" ], [ - "5bd1218", + "5bd1218d", "1592820146", "style", "compose", "Tidy up left over debugging, reduce test repetition" ], [ - "90808e9", + "90808e93", "1592819551", "test", "compose", "Sort out indentation in draftdesk test" ], [ - "4a70d4d", + "4a70d4d6", "1592753898", "style", "start", "Improve layout and formatting." ], [ - "2b4dc8f", + "2b4dc8f4", "1592578233", "feat", "compose", "Allow drag-and-drop for suggested contacts" ], [ - "6469e47", + "6469e47a", "1592573892", "feat", "compose", "Keep feeding the suggestion list after some contacts are selected" ], [ - "4f6ad10", + "4f6ad105", "1592568593", "fix", "webmail", "Make switching to the current folder a no-op" ], [ - "c5d7401", + "c5d74010", "1592565113", "fix", "compose", "Show only one suggestions bar per compose window" ], [ - "fc798f2", + "fc798f20", "1592565113", "fix", "compose", "make sure profiles are loaded correctly regardless of races" ], [ - "c86818a", + "c86818a7", "1592561084", "fix", "compose", "Cope with reply-to field in new TO format" ], [ - "78f7d19", + "78f7d191", "1592500550", "fix", "compose", "Ensure we cope with CC/BCC emails containing a comma" ], [ - "aae50ff", + "aae50ffd", "1592479317", "fix", "compose", "Re-add code lost in cherry picking/merging" ], [ - "245bb00", + "245bb00b", "1592474886", "test", "reply", "Correct reply test" ], [ - "0b6dd52", + "0b6dd525", "1592474855", "refactor", "tests", "Split tests for better debugging" ], [ - "044cee5", + "044cee5a", "1592474818", "fix", "compose", "Cope with replying to emails where From name contains a comma" ], [ - "6a99280", + "6a992801", "1592297935", "test", "policy", "Workaround --no-merges not doing what it advertises" ], [ - "28fbc1b", + "28fbc1b7", "1592224277", "fix", "lint", "fix lint errors" ], [ - "8c186e9", + "8c186e99", "1591973240", "fix", "folderlist", "Make sure we show folder counts correctly for folders not available in local index" ], [ - "a81661c", + "a81661c1", "1591959261", "test", "compose", "Basic tests for suggested recipient list" ], [ - "fb10ce2", + "fb10ce21", "1591956956", "fix", "compose", "Tune the amount of suggested recipients" ], [ - "ea7e514", + "ea7e5149", "1591868555", "build", "deps", "npm audit fix" ], [ - "7c293b0", + "7c293b00", "1591810664", "fix", "rmmapi", "Better formatting of backend errors" ], [ - "88e7df9", + "88e7df9d", "1591797637", "test", "e2e", "Ensure localSearchPromptDisplayed is set upon closing the dialog" ], [ - "fdc5fc5", + "fdc5fc5d", "1591792226", "feat", "compose", "Show recently used recipients under input forms" ], [ - "6173887", + "61738872", "1591777046", "refactor", "searchservice", "Keep recipients as a list as long as possible" ], [ - "6006fe2", + "6006fe29", "1591620168", "fix", "webmail", "Make sure the URL fragment updates after closing an email" ], [ - "3eb715b", + "3eb715b7", "1591612044", "fix", "webmail", "Prefer contact recipients over searchindex recipients" ], [ - "3bc9824", + "3bc9824e", "1591302164", "feat", "multiple_msg_unread", "replace endpoint that marks multiple messages as unread/unflag" ], [ - "e167331", + "e1673313", "1591299229", "feat", "multiple_msg_unread", "replace endpoint that marks multiple messages as unread/unflag" ], [ - "90ceca0", + "90ceca0d", "1591288175", "fix", "webmail", "Visually scroll the message list when using the up/down keys" ], [ - "a54eed7", + "a54eed7f", "1591284035", "refactor", "webmail", "Remove unneeded attempt to protect variable access" ], [ - "9d84946", + "9d849461", "1591284035", "test", "webmail", "Adapt test to new structure. Prefer contacts over searchindex" ], [ - "f61f741", + "f61f7419", "1591284035", "style", "webmail", "Satisfy the linter with some whitespace" ], [ - "f3e796a", + "f3e796a4", "1591284035", "fix", "contacts", "Contact updates now appear in compose window directly after update/addition" ], [ - "a816645", + "a8166450", "1591284035", "fix", "webmail", "Update contacts cache separately from search index contacts" ], [ - "6f3dcce", + "6f3dcce7", "1591284035", "style", "compose", "Remove dead code / simplify code" ], [ - "e3420ba", + "e3420ba6", "1591022617", "style", "draftdesk", "Improvements to the draft list view" ], [ - "5b7d8ed", + "5b7d8edb", "1591022414", "fix", "draftdesk", "Fix text overflowing after closing draft" ], [ - "c382861", + "c3828614", "1591021752", "test", "webmail", "Correct tests to reflect fixes in webmail navigation" ], [ - "e652aae", + "e652aae7", "1591021752", "fix", "webmail", "Fix up/down navigation in maillist" ], [ - "8122826", + "81228266", "1591021752", "feat", "webmail", "Highlight currently \"opened\" email in mail list" ], [ - "863f8bf", + "863f8bf4", "1591021752", "fix", "webmail", "Close mailviewer when email is deleted via multi-select operation" ], [ - "6fa4381", + "6fa4381c", "1591021752", "fix", "webmail", "Don't \"check\" emails in folder view unless actually clicking on the checkbox" ], [ - "a0c2d06", + "a0c2d06e", "1591021752", "fix", "webmail", "Display selected-mail operations whenever more than one message is selected" ], [ - "9f1ac08", + "9f1ac088", "1590678666", "fix", "messagetable", "Display time instead of the date for messages received after midnight" ], [ - "e5b932f", + "e5b932fc", "1590591721", "test", "e2e", "Use direct URLs when testing mailviewer scenarios" ], [ - "2e963ce", + "2e963ce4", "1590590806", "feat", "webmail", "Give folders and messages URL fragments" ], [ - "ad923af", + "ad923afe", "1590590695", "refactor", "webmail", "Fix indentation and naming of hotkeysService to conform to the rest of the file" ], [ - "7561681", + "75616816", "1590590522", "refactor", "webmail", "Remove some dead code related to expectedMessageSize" ], [ - "b637092", + "b6370923", "1590570967", "fix", "mailviewer", "Store message list view settings in browser" ], [ - "8fe1f45", + "8fe1f459", "1590536744", "fix", "mailviewer", "Grow HTML view to proper size right away" ], [ - "efa6808", + "efa68085", "1590414881", "fix", "contacts", "Make sure we're not adding duplicate contacts to groups" ], [ - "b91a8a5", + "b91a8a5c", "1590410825", "refactor", "contacts", "Return the new contact ID from saveContact()" ], [ - "8f84827", + "8f84827c", "1590408234", "style", "mailviewer", "Increase the minimal width of canvastable columns" ], [ - "6b72d24", + "6b72d243", "1590100526", "style", "welcome", "Add note about how to return to Welcome Desk." ], [ - "4292cf1", + "4292cf13", "1590099321", "style", "welcome", "Make Welcome Desk a flexbox. Use routerlinks where applicable." ], [ - "7a95791", + "7a957914", "1590096668", "fix", "payment", "Bug fixes." ], [ - "0c95336", + "0c95336a", "1590093512", "fix", "styling", "Fix breakpoints for iPad Pro" ], [ - "7c33d93", + "7c33d93a", "1590063236", "fix", "compose", "Ensure we can forward emails with no To or Subject" ], [ - "ae0d973", + "ae0d973c", "1590063193", "test", "mailviewer", "Add tests for reply/forwarding emails with empty To/Subject" ], [ - "d2bf73e", + "d2bf73e3", "1590050254", "feat", "login", "Add password reset link to login window" ], [ - "985651c", + "985651c7", "1590007934", "fix", "canvastable", "Make it possible to open email from the bottom of the screen" ], [ - "132e036", + "132e036f", "1590004531", "feat", "login", "Add password reset link to login window" ], [ - "dc55e89", + "dc55e89b", "1590004113", "style", "login", "More modern look to the login window" ], [ - "3d5415a", + "3d5415a1", "1589987651", "fix", "contacts", "Make sure we can store freeform birthdays" ], [ - "89269b1", + "89269b1b", "1589983030", "test", "folderlist", "Adapt tests to the new API, make them more unit-y" ], [ - "7808298", + "78082986", "1589977931", "fix", "folders", "Refresh folder counts as soon as message seen status changes" ], [ - "104f3ee", + "104f3eee", "1589966434", "fix", "mailviewer", "Ensure default horizontal pane is not invisible" ], [ - "f10d923", + "f10d923a", "1589963022", "build", "calendar", "Update to the newest angular-calendar" ], [ - "e72ba4c", + "e72ba4cb", "1589899727", "feat", "contacts", "Allow adding newly imported contacts to groups" ], [ - "581a290", + "581a290f", "1589897662", "fix", "compose", "Make it possible to reply to emails with no plaintext" ], [ - "4271477", + "42714770", "1589894814", "feat", "contacts", "Add optional drag helpers for problematic browsers" ], [ - "ecf43cd", + "ecf43cd3", "1589892142", "refactor", "contacts", "Load contact settings only in the settings component" ], [ - "8831b55", + "8831b558", "1589889199", "fix", "contacts", "Make sure contacts with excessive backslashes don't \"grow\"" ], [ - "883545a", + "883545a8", "1589881829", "test", "e2e", "Deduplicate closeWelcomeDialog() and make it a bit less sensible" ], [ - "353b2c3", + "353b2c38", "1589879270", "test", "e2e", "Make a flapping compose test more consistent" ], [ - "6594663", + "65946631", "1589824993", "style", "welcome", "Improve formatting. Add license." ], [ - "f46f6f4", + "f46f6f4e", "1589815405", "feat", "contacts", "Allow adding selected contacts to a group" ], [ - "f287c87", + "f287c871", "1589809693", "feat", "compose", "Allow emailing groups of contacts" ], [ - "139c1ee", + "139c1eec", "1589809643", "refactor", "contacts", "Rename Recipient.fromGroup to a more appropriate fromfromCategory" ], [ - "344aa7f", + "344aa7fd", "1589809546", "refactor", "contacts", "Fix linter errors" ], [ - "a96fd92", + "a96fd92a", "1589804266", "fix", "contacts", "Make sure something relevant is displayed even when no names are available" ], [ - "bf2f951", + "bf2f9514", "1589802856", "feat", "contacts", "parse grouped properties correctly" ], [ - "2f7dd1d", + "2f7dd1dd", "1589800476", "build", "app", "Enable sourcemaps for production builds" ], [ - "7c8ce14", + "7c8ce14c", "1589479155", "style", "welcome", "Add links and improve layout and formatting." ], [ - "0a4394b", + "0a4394bc", "1589469147", "fix", "contacts", "Make sure we parse company/department correctly" ], [ - "2c50497", + "2c504972", "1589468941", "fix", "contacts", "Don't import contact if already present" ], [ - "59ccb50", + "59ccb505", "1589467420", "fix", "contacts", "Support multiple types per contact property" ], [ - "e7e0457", + "e7e0457a", "1589458371", "refactor", "contacts", "Pull out category editor to its own component" ], [ - "5145b24", + "5145b244", "1589454453", "test", "contacts", "Add more tests for parsing property types" ], [ - "60bfc89", + "60bfc89c", "1589451674", "fix", "contacts", "Prevents groups from being dragged into other groups" ], [ - "dbd5ab0", + "dbd5ab02", "1589449478", "fix", "contacts", "Improve error reporting when an invalid contact is being saved" ], [ - "39bc529", + "39bc529e", "1589382702", "feat", "folderlist", "Calculate folder counts from xapian if available" ], [ - "5675ee4", + "5675ee4c", "1589379380", "style", "mailviewer", "Make in-message notifications more visible" ], [ - "eaaac9f", + "eaaac9f3", "1589367822", "refactor", "app", "Rename folderCount* to folderList where needed" ], [ - "7be4494", + "7be44942", "1589362680", "build", "debugging", "Add terminal debugging for headless systems" ], [ - "725f1dd", + "725f1dd9", "1589295959", "test", "e2e", "Test picking the correct address for Reply" ], [ - "1d3f7f0", + "1d3f7f0b", "1589290135", "build", "deps", "Update dependencies" ], [ - "1f76dda", + "1f76dda4", "1589216442", "feat", "contacts", "Show groups first before we build a new UI for them specifically" ], [ - "17ca603", + "17ca6031", "1589214852", "fix", "compose", "Correct default \"From\" email for replies" ], [ - "7beb3fe", + "7beb3fef", "1589214777", "feat", "contacts", "Allow removing group members with drag-and-drop" ], [ - "6772001", + "67720019", "1589211464", "fix", "contacts", "Fix a regression in \"Discard changes\" button" ], [ - "4e35530", + "4e355302", "1589211291", "style", "contacts", "Always show the contact drop area, but make it more subtle if the group isn't empty" ], [ - "0579bad", + "0579bad7", "1589210000", "test", "mailviewer", "Make tests confirm changes to height in storage." ], [ - "00f729f", + "00f729f1", "1589044810", "feat", "welcome", "Add content to Welcome Desk." ], [ - "4df1ec7", + "4df1ec78", "1589043055", "feat", "start", "Replace content of Start Desk. Rename components and modules." ], [ - "dd4c090", + "dd4c0907", "1588968516", "feat", "contacts", "Implementing adding contacts to groups by drag-and-drop" ], [ - "33cbd08", + "33cbd082", "1588963959", "feat", "contacts", "Parse non-standard member properties in groups" ], [ - "dcd2d37", + "dcd2d370", "1588963932", "fix", "contacts", "Fix setting addresses for contacts" ], [ - "c3d6c04", + "c3d6c042", "1588955104", "test", "e2e", "Fix contacts-related tests after the lowercase UUID change" ], [ - "30e7427", + "30e7427a", "1588954490", "feat", "contacts", "List group members on group details page" ], [ - "10e35ff", + "10e35ff8", "1588954390", "refactor", "contacts", "Pull our contact button into its own component" ], [ - "873d9a8", + "873d9a83", "1588954390", "fix", "contacts", "Fix importing vcfs with a single contact in them" ], [ - "20bca20", + "20bca20e", "1588954390", "feat", "contacts", "Distinguish between idividuals and groups, allow creation of the latter" ], [ - "839d7c2", + "839d7c22", "1588954390", "refactor", "contacts", "Rename Contact.full_name() to first_and_last_name()" ], [ - "34539bd", + "34539bd4", "1588946694", "test", "folders", "Handle creating root folders" ], [ - "3f845a6", + "3f845a64", "1588856250", "fix", "payment", "Bug fixes." ], [ - "5965014", + "59650140", "1588852021", "test", "compose", "Mock ContactsService rather than RunboxWebmailAPI" ], [ - "4b58a2f", + "4b58a2fb", "1588850209", "fix", "cart", "Remove old debug logs" ], [ - "f2dabdf", + "f2dabdfd", "1588787137", "feat", "Welcome", "Welcome Desk displaying common tasks for new users." ], [ - "409f460", + "409f460d", "1588785349", "feat", "start", "Add content to Start Desk." ], [ - "8f0ff26", + "8f0ff261", "1588780534", "fix", "contacts", "Reimplement importing from VCF, this time parsed locally" ], [ - "33471f3", + "33471f3f", "1588780472", "fix", "contacts", "Fix a regression when setting categories for a contact" ], [ - "d8e4f68", + "d8e4f681", "1588776098", "fix", "folders", "Change name of the button to \"New Subfolder\"" ], [ - "9d5577a", + "9d5577ac", "1588765586", "ci", "config", "Run CI tests on separate port" ], [ - "eba1caf", + "eba1caf8", "1588765453", "test", "compose", "Fix e2e tests for use of Location.back" ], [ - "4372008", + "43720082", "1588693827", "feat", "folders", "Create sub-folders via individual folder menus" ], [ - "bff5e43", + "bff5e43c", "1588689117", "feat", "contacts", "Update the contacts cache format and version" ], [ - "d132fc9", + "d132fc9e", "1588687475", "feat", "contacts", "Switch to the new contacts API" ], [ - "8963bc2", + "8963bc21", "1588606940", "style", "compose", "Remove tabs from e2e compose text" ], [ - "0b37f13", + "0b37f13b", "1588604350", "fix", "compose", "Simplify \"back\" functionality from drafts" ], [ - "1b44d6a", + "1b44d6ac", "1588260311", "feat", "mailviewer", "Store mailviewer pane height across sessions" ], [ - "2cb191b", + "2cb191b9", "1588159932", "fix", "draftdesk", "Ensure we store Draft data correctly post save" ], [ - "913dbe5", + "913dbe5d", "1588120099", "fix", "payment", "Bug fixes." ], [ - "8d80641", + "8d80641d", "1588084643", "fix", "compose", "Display email localpart for contact with no name details" ], [ - "f3686a8", + "f3686a8a", "1587991994", "fix", "account", "Fix an edgecase where payment would not start due to currency mismatch" ], [ - "6a45174", + "6a45174b", "1587988608", "fix", "compose", "More fixing of failing tests" ], [ - "c445f64", + "c445f64d", "1587988608", "fix", "compose", "Correct new draftdesk tests" ], [ - "6801d87", + "6801d87a", "1587988608", "test", "compose", "Add e2e tests for \"close draft\" functionality" ], [ - "ed336df", + "ed336dff", "1587988608", "fix", "compose", "Fix bugs thrown up by testing" ], [ - "272ce25", + "272ce256", "1587988608", "fix", "compose", "Correct arguments passed to create() in new compose tests" ], [ - "3157bed", + "3157bed8", "1587988608", "fix", "compose", "Fix code layout to satisfy linting checks" ], [ - "d62bb0f", + "d62bb0f7", "1587988608", "fix", "compose", "Return user to origin when closing/trashing new drafts" ], [ - "3741cea", + "3741ceac", "1587734424", "fix", "contacts", "fix displaying addressses and relatives" ], [ - "c87e79b", + "c87e79b5", "1587728977", "fix", "domreg", "Eliminate rmmapi dependency in domreg" ], [ - "5cc8299", + "5cc82999", "1587728584", "feat", "contacts", "parse vcard addresses" ], [ - "30a6fd6", + "30a6fd68", "1587724571", "fix", "contacts", "Workaround some wacky vCard formats, remove previous incorrect hacks" ], [ - "440a78e", + "440a78e3", "1587649788", "feat", "contacts", "switch to vcard-based sync API" ], [ - "7a79912", + "7a799125", "1587636171", "build", "deps", "Update cypress and some other deps" ], [ - "37a5491", + "37a5491c", "1587633893", "feat", "contacts", "Make our Contacts vCard-backed" ], [ - "2350c73", + "2350c73b", "1587554774", "fix", "compose", "Only complain about invalid/missing TO/CC/BCC on send" ], [ - "1c99d5d", + "1c99d5db", "1587406892", "fix", "domreg", "Ban trial users from using domain registration" ], [ - "6522af3", + "6522af33", "1587398224", "fix", "cart", "Use the currency provided by the API, over user's default currency" ], [ - "0ce84ad", + "0ce84ad0", "1587375986", "refactor", "compose", "Fix code indentation errors" ], [ - "0a01e23", + "0a01e232", "1587160377", "fix", "menu", "Change link to routerLink." ], [ - "a9c0352", + "a9c03522", "1587159933", "fix", "payment", "Typo." ], [ - "161b66f", + "161b66f5", "1587136653", "ci", "deps", "Use Firefox rather than Chromium for unit tests" ], [ - "dfd656a", + "dfd656ae", "1587136067", "fix", "changelog", "Skip badly formed commits" ], [ - "ca22d9f", + "ca22d9f0", "1587136067", "fix", "app", "Prevent sentry error handler from silencing errors completely" ], [ - "1caea8e", + "1caea8e9", "1586962439", "fix", "compose", "Validate TO/CC/BCC fields on save/send" ], [ - "1450bfe", + "1450bfeb", "1586930549", "fix", "identity", "check if parent attributes of the object are defined" ], [ - "a310661", + "a3106611", "1586923263", "fix", "identitys", "list aliases that are owned by other users and mark them as \"read only\"" ], [ - "5c0d75f", + "5c0d75f2", "1586905741", "feat", "start", "Start desk for common tasks." ], [ - "04d6bdf", + "04d6bdf3", "1586896793", "fix", "menu", "Correct Subscribe link in header." ], [ - "04d6bdf", + "04d6bdf3", "1586896793", "fix", "menu", "Change link to routerLink." ], [ - "860c537", + "860c537f", "1586446821", "fix", "contacts", "Rename \"groups\" to \"categories\"" ], [ - "1a4f500", + "1a4f5005", "1586180040", "refactor", "folder", "Eliminate folderlist's dependency on messagelistservice" ], [ - "f902aec", + "f902aecb", "1586120235", "fix", "menu", "Correct Subscribe link in header." ], [ - "35d67ac", + "35d67ac9", "1585913496", "refactor", "folder", "Make folderlist dumber, part 2" ], [ - "c84376e", + "c84376ee", "1585907611", "refactor", "folder", "Make folderlist dumber, part 1" ], [ - "ea292db", + "ea292db3", "1585905804", "refactor", "folder", "Reindent folder to fit the rest of the code" ], [ - "dccaf8e", + "dccaf8ec", "1585299901", "fix", "changelog", "Use timestamps instead of commit hashes to determine recent changes" ], [ - "de67b52", + "de67b528", "1585239503", "feat", "build", "Include build timestamp in ngsw metadata" ], [ - "db15798", + "db157983", "1585222828", "fix", "contacts", "Make sure we pick up Runbox6 contacts when syncing" ], [ - "404c107", + "404c1079", "1584972952", "feat", "sentry", "Enable Sentry for calendar and account apps" ], [ - "9a07cca", + "9a07ccae", "1584972185", "fix", "calendar", "Fix calendar import dialog" ], [ - "b2b04dd", + "b2b04dd5", "1584453055", "feat", "compose", "Add a button to cancel attachment upload" ], [ - "0c4a437", + "0c4a4375", "1584444300", "fix", "calendar", "Make short events appear to be longer" ], [ - "412677d", + "412677d6", "1584372125", "fix", "contacts", "Catch a wider variety of backend errors" ], [ - "08ce151", + "08ce151b", "1584356129", "build", "deps", "Update some dependencies (thanks npm audit)" ], [ - "232c061", + "232c0618", "1584354801", "fix", "rmmoffline", "Close the offline notification snackbar after 10 seconds" ], [ - "1d63e30", + "1d63e307", "1583938904", "fix", "contacts", "Add a missing button to delete contacts' address" ], [ - "0358441", + "0358441a", "1583325662", "fix", "calendar", "Load timezones data from Icals instead of moment.js" ], [ - "7f17297", + "7f172974", "1583248030", "fix", "compose", "remove initialfocus from textarea" ], [ - "8938ff0", + "8938ff06", "1583246370", "fix", "compose", "remove focus handling from compose.ts and handle it in compose.html so e2e tests pass" ], [ - "03981c8", + "03981c82", "1583244340", "feat", "tinymce", "enable default browser spell check in tinymce" ], [ - "41e2a7c", + "41e2a7c5", "1583240961", "fix", "identity", "fix lint" ], [ - "9d332d5", + "9d332d5c", "1583202675", "fix", "identity", "list compose \"From\" sorted in the order: main, others and aliases" ], [ - "a87680e", + "a87680ef", "1582887161", "fix", "compose", "handle ajax_send_msg errors" ], [ - "faa79e6", + "faa79e6c", "1582884722", "fix", "calednar", "Always set default dates when creating new calendar events" ], [ - "7e6fe14", + "7e6fe140", "1582803668", "fix", "calendar", "When displaying events, convert times to local timezone" ], [ - "c084907", + "c0849075", "1582803255", "fix", "sentry", "Add license to sentry.ts" ], [ - "d399270", + "d399270b", "1582803126", "feat", "app", "Add an indicator if the API connection is lost" ], [ - "bc4f9f3", + "bc4f9f3c", "1582737676", "feat", "build", "Add an option to enable Sentry monitoring" ], [ - "ef90f54", + "ef90f54b", "1582728986", "fix", "shopping-cart", "Increase quantities in cart when adding duplicate products" ], [ - "74203f6", + "74203f6c", "1582660786", "fix", "payment", "Bugfixes." ], [ - "a9d76e0", + "a9d76e03", "1582653334", "fix", "payment", "Various bugfixes." ], [ - "72d4d93", + "72d4d93e", "1582650205", "fix", "menu", "Bugfix." ], [ - "be967ed", + "be967ed0", "1582561863", "fix", "menu", "Delint." ], [ - "2276d18", + "2276d18e", "1582552419", "feat", "update-notifier", "Make the update notifier link to changelog for changes" ], [ - "e34f447", + "e34f4479", "1582549440", "fix", "ci", "Bump the revision number for commit log tests" ], [ - "ebfa269", + "ebfa2694", "1582547823", "fix", "mailviewer", "Make it possible to reply to emails with no \"To\" header" ], [ - "409450c", + "409450c7", "1582324828", "feat", "payment", "Add subscribe link in header for trial accounts." ], [ - "3961593", + "3961593e", "1582222948", "fix", "payment", "Various bug fixes." ], [ - "ab369d6", + "ab369d61", "1582109637", "fix", "identity", "fix lint errors" ], [ - "68d23e4", + "68d23e4a", "1582108624", "fix", "identity", "show \"email\" field grayed in aliases because its readonly" ], [ - "e374eea", + "e374eea6", "1581701171", "fix", "identity", "set the email field as readonly if its an aliases. aliases email cannot be edited." ], [ - "47b7af6", + "47b7af60", "1581698494", "fix", "identity", "allow the user to unset the reply_to address in identitys" ], [ - "309add4", + "309add4d", "1581684809", "feat", "account", "Distinguish Apple Pay from regular payment methods" ], [ - "d9e4e34", + "d9e4e343", "1581684809", "feat", "account", "Add a way to list and edit available payment methods" ], [ - "95e518d", + "95e518df", "1581621544", "build", "deps", "Update angular-calendar" ], [ - "f1c142d", + "f1c142df", "1581620926", "fix", "calendar", "Add a missing license header to calendar-event-card" ], [ - "0da999a", + "0da999a4", "1581620926", "feat", "calendar", "show event previews in the import dialog" ], [ - "2484c98", + "2484c98c", "1581620926", "refactor", "calendar", "refactor event preview card into its own component" ], [ - "078561c", + "078561ce", "1581620926", "fix", "calendar", "make sure recurring events are displayed when starting in overview mode" ], [ - "9df7df3", + "9df7df39", "1581620926", "fix", "calednar/contacts", "fix import dialog popping up repeatedly" ], [ - "f00e7d1", + "f00e7d19", "1581620926", "fix", "contacts", "fix a contact import regression" ], [ - "4214381", + "4214381f", "1581620926", "fix", "calendar", "Fix importing events with a METHOD property" ], [ - "be9f683", + "be9f683b", "1581599903", "feat", "contacts", "Add a way to export contacts" ], [ - "8c4be7e", + "8c4be7e1", "1581539397", "ci", "cypress", "Increase the default command timeout" ], [ - "97b6f79", + "97b6f79f", "1581532103", "fix", "calendar", "Make the datetime picker show up again" ], [ - "7b77a6e", + "7b77a6e2", "1581527146", "fix", "contacts", "Don't include contacts' nicknames when composing emails to them" ], [ - "a7721d5", + "a7721d53", "1581521104", "feat", "contacts", "Update contacts incrementally instead of fetching the entire list every time" ], [ - "020d28a", + "020d28ab", "1581509027", "test", "lint", "Remove obsolete linter rules" ], [ - "5ccbc93", + "5ccbc933", "1581507826", "build", "e2e", "Remove old e2e angular build setup" ], [ - "4105be6", + "4105be62", "1581507187", "test", "cypress", "tidy up e2e test locations and setup" ], [ - "94fcb83", + "94fcb83b", "1581504532", "test", "login", "Convert old e2e login tests to cypress" ], [ - "2b46946", + "2b46946e", "1581501645", "test", "search", "Convert old e2e search tests to cypress" ], [ - "ffbdb4b", + "ffbdb4bc", "1581449700", "test", "folders", "Port the folder tests to cypress, fixing them in the process" ], [ - "7a8c97b", + "7a8c97ba", "1581444632", "fix", "folder", "Prevent duplicate requests during folder operations" ], [ - "9816843", + "98168432", "1581438201", "test", "compose", "Convert old e2e compose tests to cypress" ], [ - "34bffa4", + "34bffa42", "1581434409", "test", "canvastable", "Convert old e2e canvastable tests to cypress" ], [ - "c035890", + "c035890f", "1581428261", "test", "domreg", "Convert old e2e domreg tests to cypress" ], [ - "3a7016b", + "3a7016b7", "1581427525", "test", "payments", "Convert old e2e payment tests to cypress" ], [ - "0af793b", + "0af793b2", "1581426260", "test", "calendar", "Convert old e2e calendar tests to cypress" ], [ - "a182d74", + "a182d746", "1581424329", "ci", "travis", "Update .travis.yml for new e2e setup" ], [ - "bba8bbc", + "bba8bbca", "1581424217", "build", "deps", "Update cypress to 4.0.1" ], [ - "c2be0c2", + "c2be0c23", "1581423480", "test", "e2e", "Run cypress tests by default instead of existing ones" ], [ - "8523e4a", + "8523e4ab", "1581422587", "build", "deps", "Update e2e dependencies" ], [ - "45bfe56", + "45bfe56b", "1581420519", "test", "cypress-e2e", "Add cypress e2e setup with some basic tests for contacts" ], [ - "8974853", + "89748530", "1581365272", "build", "app", "Changes required for Angular 9" ], [ - "818f664", + "818f6641", "1581365272", "build", "deps", "Update to Angular 9" ], [ - "4817569", + "4817569e", "1581365272", "build", "deps", "Update dependencies" ], [ - "4d7f368", + "4d7f3688", "1581353908", "style", "menu", "Add tooltip to menu items that link to Runbox 6. (#400)" ], [ - "401a461", + "401a4619", "1580804530", "fix", "timer", "Use moment.js instead of proprietary code." ], [ - "8877f18", + "8877f189", "1580419398", "build", "deps", "bump tinymce from 5.0.16 to 5.1.4" ], [ - "4324b84", + "4324b84d", "1580397445", "docs", "readme", "Update npm commands with npx. (#444)" ], [ - "78169c1", + "78169c15", "1579706065", "feat", "account", "Ensure business rules are met before purchase is allowed" ], [ - "a978215", + "a9782154", "1579636372", "fix", "tinymce", "replace old compose tinymce with TinyMCEPlugin" ], [ - "74cda19", + "74cda195", "1579620894", "feat", "account", "Offer a purchase of Email Hosting when necessary" ], [ - "7cf2937", + "7cf29375", "1579587261", "fix", "timer", "Delint." ], [ - "7f42df6", + "7f42df68", "1579584393", "feat", "payment", "Add timer to display remaining time of offers." ], [ - "71a9d4b", + "71a9d4be", "1579583824", "style", "payment", "Reorganize page and improve responsiveness. Add limited time offer." ], [ - "af61636", + "af616365", "1579576050", "feat", "email", "delete multiple messages" ], [ - "1b7dfe0", + "1b7dfe0f", "1579545306", "style", "account", "Rename Account Upgrades to Main Account Subscriptions" ], [ - "34cdcb9", + "34cdcb9c", "1579539932", "feat", "changelog", "Add a built-in changelog" ], [ - "0d571bc", + "0d571bc2", "1579364032", "fix", "identities", "added matdialog module dependency to fix identities modal." ], [ - "444da1f", + "444da1f5", "1579000304", "style", "payment", "Make layout more responsive, add button color, and credit card logos." ], [ - "24a0c8a", + "24a0c8aa", "1578935410", "fix", "tinymce", "remove RMM from tinymce.plugin.ts" ], [ - "f0624dd", + "f0624dd2", "1578935336", "fix", "rmm.ts", "move TinyMCEPlugin out of rmm.ts" ], [ - "631cf19", + "631cf19f", "1578768357", "fix", "auth", "dont use type=\"number\" otherwise the form wont get the values" ], [ - "4f250b7", + "4f250b72", "1578578284", "fix", "account", "Improve error handling in bitpay payment dialog" ], [ - "8c3a898", + "8c3a8983", "1578576969", "fix", "account", "disallow renewal of trial" ], [ - "3a4f561", + "3a4f5615", "1577795829", "perf", "app", "lazy-load non-core modules to improve load times" ], [ - "1877990", + "1877990f", "1577576739", "fix", "lint", "fixed line too long lint error" ], [ - "d61af89", + "d61af894", "1577575998", "fix", "identities", "move resend_validate_email from profile lister to profile editor" ], [ - "2484bfa", + "2484bfa5", "1577574303", "feat", "identities", "update test to use FromAddress from rmmap/from_address" ], [ - "15559a3", + "15559a34", "1577573098", "feat", "identities", "prepare the from_addresses to be used by the compose \"From\" field" ], [ - "7f8e471", + "7f8e471a", "1577572952", "feat", "identities", "created new tinymce class that can be instanciated via RMM class" ], [ - "a9d0594", + "a9d0594c", "1577572817", "feat", "identities", "switched fields positions, added signature support for tinymce" ], [ - "9a9fda4", + "9a9fda4a", "1577572664", "feat", "identities", "update draftdesk to use RMM class in refreshForms method" ], [ - "1d09c70", + "1d09c70f", "1577570978", "feat", "identities", "update compose to enable html if signature is html" ], [ - "3c7a98d", + "3c7a98d0", "1577570477", "feat", "identities", "move FromAddress class into its own file" ], [ - "204b227", + "204b2274", "1577569898", "fix", "identities", "Remove Description from identities overview" ], [ - "3517b91", + "3517b910", "1577569882", "fix", "identitie", "Under \"Other identities\" next to \"Add email\" btn, change to \"n identities created\"" ], [ - "d931aeb", + "d931aeb9", "1577463614", "feat", "contacts", "Button to select all visible contacts" ], [ - "59ff56f", + "59ff56fe", "1576863015", "fix", "identitie", "Under \"Other identities\", rename button \"Add email\" to \"Add identity\"" ], [ - "d198c5d", + "d198c5d5", "1576772266", "build", "deps", "Update webdriver-manager for new chromium versions" ], [ - "c269a66", + "c269a662", "1576771367", "fix", "contacts", "Make the address form display correctly" ], [ - "efe52cc", + "efe52ccd", "1576683344", "style", "print", "Add tooltip about printing vs the environment." ], [ - "d2fb368", + "d2fb3683", "1576290169", "fix", "identities", "fix lint problems" ], [ - "a31c6da", + "a31c6da3", "1576285439", "feat", "identities", "added more endpoints related to compose in mockserver" ], [ - "80308c8", + "80308c8a", "1576280267", "fix", "identities", "import Observable from rxjs instead of rxjs/Rx" ], [ - "73f305e", + "73f305e1", "1576274850", "fix", "idenitites", "update app.module and profile.module so e2e tests pass" ], [ - "add4d81", + "add4d81d", "1576264677", "fix", "identities", "move dependencies from app.module into profiles.module" ], [ - "a9a34cc", + "a9a34ccd", "1576176865", "fix", "identities", "add missing DevModule to app.module.ts" ], [ - "717bcc5", + "717bcc5f", "1576175997", "fix", "identities", "import profiles module in app module" ], [ - "1b378ea", + "1b378ea4", "1576174438", "fix", "identities", "add a type to data attribute" ], [ - "9161818", + "91618184", "1576172204", "fix", "identities", "return of to always return an observable via this method" ], [ - "0ac0868", + "0ac08686", "1576155138", "fix", "identities", "add Observable to load" ], [ - "9ae2eb1", + "9ae2eb13", "1576153002", "fix", "identities", "re-add required components to NgModule" ], [ - "37f9795", + "37f97950", "1576089025", "fix", "idenitties", "fixed more review points and lint problems" ], [ - "bbfa515", + "bbfa5150", "1576088885", "fix", "identities", "re-added these lines because it does not work without them here it seems" ], [ - "dee23f6", + "dee23f66", "1575961718", "fix", "travis", "fix travis errors" ], [ - "2377251", + "23772510", "1575957116", "fix", "profiles", "fix lint and merge review suggestions" ], [ - "d14169f", + "d14169f6", "1575955120", "fix", "appmodule", "removed unecessary imports from app.module.ts" ], [ - "334bc24", + "334bc24f", "1575953558", "fix", "lint", "identities lint problems fix" ], [ - "4fa195c", + "4fa195c2", "1575750346", "fix", "compose", "use profiles signature" ], [ - "c7b377f", + "c7b377f7", "1575747126", "fix", "profiles", "fixing profiles to compile" ], [ - "265e0d1", + "265e0d14", "1575742830", "feat", "identities", "identities" ], [ - "0ff194d", + "0ff194db", "1575633683", "build", "deps", "Update some dependencies" ], [ - "a5db0d3", + "a5db0d31", "1575633175", "feat", "account", "Add a page for Paypal billing agreements" ], [ - "66c34c8", + "66c34c8b", "1575632503", "feat", "account", "Allow enabling/disabling product autorenewal" ], [ - "e2db28d", + "e2db28d5", "1575564524", "style", "intro", "Update introductory welcome text." ], [ - "7e07ab8", + "7e07ab8c", "1575383249", "style", "payments", "Keep the ZIP field underline from overflowing the form" ], [ - "f6c1bcb", + "f6c1bcb5", "1575381183", "fix", "payments", "Improve error reporting for Stripe in some edge cases" ], [ - "1ac674f", + "1ac674fb", "1575311869", "fix", "canvastable", "Keep columns from overlapping in the wrapped view" ], [ - "7f69e8e", + "7f69e8ec", "1575307453", "refactor", "rmmapi", "Switch to the new, faster REST endpoint for moving messages" ], [ - "80760d6", + "80760d61", "1575283863", "style", "account", "Fix wording on the subscription page" ], [ - "7329b9a", + "7329b9a7", "1575283389", "fix", "dkim", "add missing dot in dkim page > dns zone file" ], [ - "8fc3b16", + "8fc3b167", "1575116953", "fix", "folders", "Make folders show up on old versions of Edge" ], [ - "5abe9e9", + "5abe9e9c", "1575045336", "style", "canvastable", "Fix overflow and adjustment of column headers" ], [ - "f32c1da", + "f32c1dad", "1575041177", "feat", "account-products", "Only allow one subscription order at a time" ], [ - "9896cdc", + "9896cdcd", "1575036646", "fix", "calendar", "Don't try to import events when user cancelled it" ], [ - "54bb5ee", + "54bb5ee9", "1575034297", "feat", "account-app", "Offer a redirect back to rmm6 if stripe payment form fails to load" ], [ - "abb66e0", + "abb66e02", "1575031474", "style", "update-notifier", "Make the version wording a bit more understandable" ], [ - "b3cc2ba", + "b3cc2baa", "1575026468", "style", "login", "Improve layout of login area. Move progress bar to footer." ], [ - "2f21669", + "2f21669c", "1574958021", "fix", "canvastable", "Make sure column widths are remembered and respected" ], [ - "fe0693f", + "fe0693fe", "1574956692", "refactor", "webmail", "remove unused column widths code from AppComponent" ], [ - "7a6e340", + "7a6e3400", "1574950551", "fix", "canvastable", "Fix scrolling with keyboard bindings" ], [ - "abefe2c", + "abefe2c9", "1574783071", "feat", "update-notifier", "Include build time in appData" ], [ - "e58d15b", + "e58d15ba", "1574780568", "feat", "update-notifier", "Include commit hash in app update data" ], [ - "28606b5", + "28606b58", "1574774285", "feat", "calendar", "add overview -- a new calendar view mode" ], [ - "c534aac", + "c534aac2", "1574683303", "fix", "contacts", "make sure no details are leftover when switching between contacts" ], [ - "4bf0200", + "4bf0200c", "1574681860", "feat", "contacts", "Add a way to select (and delete) multiple contacts" ], [ - "0eed26c", + "0eed26c2", "1574676741", "style", "contacts", "make each row in contacts editor a flexbox" ], [ - "edf51a4", + "edf51a41", "1574673994", "fix", "folderlist", "handle draft subfolders" ], [ - "76ba8c3", + "76ba8c34", "1574673570", "docs", "contributing", "Update TOC" ], [ - "2b7a221", + "2b7a2219", "1574487772", "style", "login", "Improve style of error message and make layout more consistent." ], [ - "cef7878", + "cef78788", "1574430474", "fix", "login", "Re-add the specialized expired account error" ], [ - "1296ecb", + "1296ecb5", "1574370845", "fix", "authguard", "don't push users out to login if they're merely expired" ], [ - "163d88f", + "163d88f9", "1574346008", "style", "webmail", "Hide nav submenu in mobile view" ], [ - "0b65c8f", + "0b65c8f6", "1574345196", "feat", "contacts", "Implement a basic mobile view for Contacts" ], [ - "84b6c47", + "84b6c47c", "1574232330", "feat", "login", "include checkbox option to use legacy and stay logged in" ], [ - "0d6b92c", + "0d6b92cf", "1574198440", "fix", "login", "fix lint" ], [ - "a746eb5", + "a746eb57", "1574177409", "fix", "dkim", "Change \"CNAME in DNS\"\", \"Active\" selector2 active date." ], [ - "32525ff", + "32525ff3", "1574092979", "fix", "login", "use this.handleLoginError in handleLoginResponse" ], [ - "89be95e", + "89be95e3", "1573947642", "fix", "login", "simplify login error messages" ], [ - "be9520b", + "be9520ba", "1573928680", "build", "dependencies", "update package-lock.json" ], [ - "a37257e", + "a37257e4", "1573928646", "fix", "Unit", "folderlist.component.spec.ts mock for hotkeys added" ], [ - "64ec6f4", + "64ec6f4d", "1573924677", "feat", "HomePage", "Add keyboard shortcuts" ], [ - "8026cf1", + "8026cf1e", "1573924645", "feat", "HomePage", "Add keyboard shortcuts" ], [ - "f69af60", + "f69af60a", "1573848492", "style", "webmail", "Omit \"Deleting...\" feedback message when deleting messages." ], [ - "f58b874", + "f58b8748", "1573825263", "refactor", "authguard", "Add a fast pass for previously logged in users" ], [ - "c1fe23e", + "c1fe23e5", "1573800457", "feat", "loginerrors", "render more login errors" ], [ - "77aee54", + "77aee543", "1573745308", "feat", "tests", "Add a ci-runner script and use it in Travis settings" ], [ - "e3506cb", + "e3506cb2", "1573744450", "style", "menu", "Move the logout button to the \"global\" sidenav-menu" ], [ - "3171fb8", + "3171fb81", "1573669888", "style", "calendar", "make sure datepicker is usable in mobile view" ], [ - "0a861d0", + "0a861d0f", "1573669866", "fix", "calendar", "make sure first-day-of-week settings are respected in event editor" ], [ - "40df944", + "40df9447", "1573668248", "style", "calendar", "Make the calendar header more concise in mobile view" ], [ - "2d882ed", + "2d882ed2", "1573665884", "style", "calendar", "Disable custom day template in mobile view" ], [ - "4abe522", + "4abe5227", "1573575906", "fix", "account-app", "Use ngOnInit consistently when loading async resources" ], [ - "c14aac7", + "c14aac7d", "1573568860", "fix", "cart", "Don't perform async queries before the component is fully initialized" ], [ - "dfe49e1", + "dfe49e10", "1573487344", "docs", "introductions", "Add links to more details about the project. (#348)" ], [ - "2e3be51", + "2e3be517", "1573486468", "feat", "menu", "Add buttons to navigate between calendar and webmail" ], [ - "85ea2fa", + "85ea2fa6", "1573478651", "fix", "tests", "Ensure that xapian is loaded in each testcase" ], [ - "d0ffd9d", + "d0ffd9d2", "1573476255", "fix", "unit-tests", "Add MobileQueryService to test providers" ], [ - "3a53c4d", + "3a53c4d7", "1573475267", "fix", "contacts", "Cap the contacts buffer at 1 to prevent excessive memory usage" ], [ - "e0144a9", + "e0144a9f", "1573475267", "fix", "contacts", "Prevent contact-details from reloading every time contact list reloads" ], [ - "d4c2d76", + "d4c2d760", "1573471024", "fix", "contacts", "Abandon custom routereusestrategy" ], [ - "f220966", + "f220966c", "1573466759", "fix", "calendar", "Fix linter errors" ], [ - "ab161a2", + "ab161a27", "1573466443", "fix", "account-app", "const-ize a variable to make linter happy" ], [ - "f9d6f52", + "f9d6f528", "1573465246", "fix", "payments", "Make the shopping cart more resilient" ], [ - "7e3251e", + "7e3251e8", "1573230634", "fix", "account-app", "Fix total price in shopping cart" ], [ - "be5c796", + "be5c7967", "1573229944", "feat", "account-app", "Add loading indicators to async elements of account-app" ], [ - "93bb314", + "93bb3143", "1573211390", "test", "rmmapi", "remove obsolete test assumptions" ], [ - "18dc94d", + "18dc94d9", "1573023063", "fix", "dkim", "the button \"check cname\" now should display for all users" ], [ - "3ea1b7b", + "3ea1b7bb", "1573012695", "feat", "README.md", "link the Github documentation to tips & tricks page" ], [ - "4ddb4cd", + "4ddb4cd9", "1572627548", "feat", "calendar", "Make calendar (more) usable on mobile" ], [ - "e8c518a", + "e8c518a6", "1572627548", "feat", "calendar", "allow expanding days from non-current month" ], [ - "c02d5cd", + "c02d5cdf", "1572627548", "refactor", "sidemenu", "refactor the sidemenu into its own component" ], [ - "f2ce8f4", + "f2ce8f44", "1572627011", "refactor", "app", "extract the mobileQuery into a service" ], [ - "eb33e58", + "eb33e589", "1572539144", "fix", "draftdesk", "Make sure draft previews don't overflow regardless of when they're created" ], [ - "04beb7e", + "04beb7e3", "1572526335", "fix", "folderlist", "Make sure folders appear in the right order" ], [ - "81c901b", + "81c901ba", "1572454751", "fix", "dkim", "remove reconfigure option" ], [ - "f6a56f0", + "f6a56f04", "1572451388", "fix", "httpinterceptor", "undo changes in httpinterceptor" ], [ - "94af34a", + "94af34ad", "1572442577", "fix", "calendar", "make sure toggling calendar visibility refreshes the event list" ], [ - "68a6efe", + "68a6efe2", "1572426602", "feat", "calendar", "Make \"Synchronize calendars\" button more aggresive" ], [ - "c60113e", + "c60113e8", "1572368558", "feat", "calendar", "Add an activity tracker to monitor calendar queries" ], [ - "7987e25", + "7987e255", "1572362180", "fix", "calendar", "refactor/fix the way event times are handled" ], [ - "164b9c2", + "164b9c21", "1572357709", "fix", "calendar", "don't double-encode calendar cache" ], [ - "a2242bf", + "a2242bfe", "1572356855", "fix", "calendar", "fix changing event from one recurring freq to another" ], [ - "21e3cb7", + "21e3cb78", "1572355885", "fix", "calendar", "fix recurring event settings" ], [ - "1a21270", + "1a212700", "1572351944", "fix", "webmail", "don't keep the message pane open when in mobile view" ], [ - "d2969db", + "d2969db7", "1572351280", "style", "webmail", "adjust preview pane wording" ], [ - "9c4e9b6", + "9c4e9b63", "1572280164", "fix", "webmail", "Display viewMode menu even when no local index exists" ], [ - "5a30155", + "5a30155b", "1572279479", "feat", "webmail", "add an option to toggle the message pane openness" ], [ - "09a3fa1", + "09a3fa13", "1572277440", "feat", "webmail", "Add an option to hide the message pane" ], [ - "0a51680", + "0a516807", "1572269645", "fix", "login", "Make sure we're displaying all login errors" ], [ - "cbb5822", + "cbb58227", "1572268445", "test", "all", "make sure *all* tests are running again" ], [ - "716277f", + "716277f4", "1572260992", "feat", "calendar", "add a settings option to clear the local cache" ], [ - "90cf8d5", + "90cf8d5d", "1571851107", "fix", "canvastable", "scroll lagging caused by focus" ], [ - "62ba9ba", + "62ba9bab", "1571542164", "fix", "canvastable", "scroll speed, column resize" ], [ - "dbed608", + "dbed6084", "1571328720", "fix", "dkim", "fix lint errors" ], [ - "567a6b9", + "567a6b96", "1571325831", "style", "draftdesk", "Add a message to an empty draftdesk" ], [ - "405a10d", + "405a10d5", "1571325031", "fix", "calendar", "make sure allDay settings are respected" ], [ - "47bbc7d", + "47bbc7de", "1571323867", "fix", "calendar", "prevent calendar visibility from being reset each update" ], [ - "906d570", + "906d5706", "1571320989", "feat", "calendar", "Set a default calendar when creating new events" ], [ - "11b0c34", + "11b0c34e", "1571320812", "style", "calendar", "order available calendars alphabetically" ], [ - "7ff74b7", + "7ff74b78", "1571315487", "test", "all", "make sure all tests are running again" ], [ - "0bb45a3", + "0bb45a31", "1571313210", "test", "e2e", "Add basic tests for calendar event creation" ], [ - "f9b179e", + "f9b179ec", "1571290280", "fix", "dkim", "remove cname reconfigure functionality" ], [ - "4c62549", + "4c62549f", "1571290230", "feat", "dkim", "button to re-check cname in dkim page" ], [ - "51fb2f9", + "51fb2f91", "1571244983", "fix", "account-app", "Implement the missing bits of PaymentRequest handling" ], [ - "16bd505", + "16bd505d", "1571244548", "fix", "email-app", "Hide the message panel when in compose/drafts" ], [ - "033f1f7", + "033f1f78", "1571243828", "fix", "calendar", "update event editor for the new API" ], [ - "54ea906", + "54ea906d", "1571149663", "feat", "calendar", "Implement client-side ical parsing" ], [ - "e124032", + "e1240327", "1571063181", "test", "e2e", "extra compose test and root folder creation" ], [ - "64ace43", + "64ace433", "1570810524", "feat", "compose", "Attachment upload progress bar indicate filename and filesize." ], [ - "765c319", + "765c3196", "1570378912", "fix", "app", "re-add deprecated mobileQuery APIs" ], [ - "c42c1d6", + "c42c1d63", "1570127201", "feat", "dkim", "create button to re-check dkim cnames" ], [ - "40130d2", + "40130d29", "1570014789", "feat", "contacts-app", "Keep an offline cache of contacts" ], [ - "7c77048", + "7c770482", "1569525279", "fix", "calendar", "Allow to move events between calendars by modifying them" ], [ - "b9fbfcb", + "b9fbfcbb", "1569504496", "build", "material", "Update to angular/material 8" ], [ - "7dbfce2", + "7dbfce2d", "1569497862", "build", "calendar", "Update angular-calendar" ], [ - "87f5655", + "87f5655a", "1569497228", "refactor", "compose", "Use the native serviceworker bypass feature" ], [ - "8e4faa7", + "8e4faa7f", "1569425926", "build", "app", "Update to Angular 8, including required API changes" ], [ - "2c9d869", + "2c9d8695", "1569425925", "refactor", "progress-service", "remove deprecated usage of BrowserXHR" ], [ - "a8233a3", + "a8233a3f", "1569425925", "refactor", "mailviewer", "remove unnecessary use of HttpModule" ], [ - "15f93cb", + "15f93cb8", "1569425925", "refactor", "searchservice", "remove a deprecated use of Injector.get" ], [ - "4670c0a", + "4670c0ad", "1569425925", "refactor", "webmail", "remove deprecated MediaQueryList APIs" ], [ - "5e3cf47", + "5e3cf47d", "1569425925", "refactor", "canvastable", "migrate to non-deprecated APIs" ], [ - "8145736", + "81457367", "1569425925", "refactor", "all", "Remove most deprecated usage of '@angular/http'" ], [ - "fc4205c", + "fc4205c5", "1569336615", "fix", "tests", "Update the commit log test, hopefully for the last time" ], [ - "330b926", + "330b926d", "1567685774", "fix", "folder", diff --git a/src/app/common/messagedisplay.ts b/src/app/common/messagedisplay.ts index 2dd6227f1..2821fad8d 100644 --- a/src/app/common/messagedisplay.ts +++ b/src/app/common/messagedisplay.ts @@ -203,4 +203,5 @@ export abstract class MessageDisplay { // columns abstract getCanvasTableColumns(app: any): CanvasTableColumn[]; + abstract getRowData(index: number, app: any): any; } diff --git a/src/app/common/messagelist.ts b/src/app/common/messagelist.ts index a5c8a960c..8df40ab97 100644 --- a/src/app/common/messagelist.ts +++ b/src/app/common/messagelist.ts @@ -157,4 +157,23 @@ export class MessageList extends MessageDisplay { return columns; } + + getRowData(rowIndex, app) { + const row = this.rows[rowIndex] + + return { + id: row.id, + seen: row.seenFlag, + messageDate: MessageTableRowTool.formatTimestamp(row.messageDate.toJSON()), + from: app.selectedFolder === 'Sent' + ? this.getToColumnValueForRow(rowIndex) + : this.getFromColumnValueForRow(rowIndex), + subject: row.subject, + size: row.size, + attachment: row.attachment , + answered: row.answeredFlag , + flagged: row.flaggedFlag , + plaintext: row.plaintext?.trim(), + }; + } } diff --git a/src/app/follows-mouse/follows-mouse.component.html b/src/app/follows-mouse/follows-mouse.component.html new file mode 100644 index 000000000..e85571510 --- /dev/null +++ b/src/app/follows-mouse/follows-mouse.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/follows-mouse/follows-mouse.component.scss b/src/app/follows-mouse/follows-mouse.component.scss new file mode 100644 index 000000000..77645beaf --- /dev/null +++ b/src/app/follows-mouse/follows-mouse.component.scss @@ -0,0 +1,8 @@ +.follows-mouse { + /* Ensure the mouse can interact with underlying elements */ + pointer-events: none; + transition: transform 0.1s ease; + z-index: 10000; + display: inline-block; + white-space: nowrap; +} diff --git a/src/app/follows-mouse/follows-mouse.component.ts b/src/app/follows-mouse/follows-mouse.component.ts new file mode 100644 index 000000000..1a83b4507 --- /dev/null +++ b/src/app/follows-mouse/follows-mouse.component.ts @@ -0,0 +1,42 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2025 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 { Component, ElementRef, HostListener } from '@angular/core'; + +@Component({ + selector: 'app-follows-mouse', + standalone: true, + templateUrl: './follows-mouse.component.html', + styleUrls: ['./follows-mouse.component.scss'], +}) +export class FollowsMouseComponent { + + constructor(private el: ElementRef) { + this.el.nativeElement.style.display = 'inline-block'; + this.el.nativeElement.style.position = 'fixed'; + this.el.nativeElement.style['z-index'] = '1000'; + } + + @HostListener('document:mousemove', ['$event']) + @HostListener('document:drag', ['$event']) + onMouseMove(event: MouseEvent) { + this.el.nativeElement.style.left = `${event.clientX + 4}px`; + this.el.nativeElement.style.top = `${event.clientY + 4}px`; + } +} diff --git a/src/app/human-bytes.pipe.ts b/src/app/human-bytes.pipe.ts new file mode 100644 index 000000000..35f1db424 --- /dev/null +++ b/src/app/human-bytes.pipe.ts @@ -0,0 +1,39 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2025 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 { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'humanBytes', + standalone: true +}) +export class HumanBytesPipe implements PipeTransform { + public transform(value: number, decimalPlaces: number = 2): string { + if (value === 0) { + return '0 B'; + } + + const base = 1000; + const suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + const exponent = Math.floor(Math.log(value) / Math.log(base)); + + const result = (value / Math.pow(base, exponent)).toFixed(decimalPlaces); + return `${parseFloat(result)} ${suffixes[exponent]}`; + } +} diff --git a/src/app/mailviewer/singlemailviewer.component.ts b/src/app/mailviewer/singlemailviewer.component.ts index a1b8e0859..e38582f55 100644 --- a/src/app/mailviewer/singlemailviewer.component.ts +++ b/src/app/mailviewer/singlemailviewer.component.ts @@ -71,7 +71,7 @@ type Mail = any; templateUrl: 'singlemailviewer.component.html', styleUrls: ['singlemailviewer.component.scss'] }) -export class SingleMailViewerComponent implements OnInit, DoCheck, AfterViewInit { +export class SingleMailViewerComponent implements OnInit, AfterViewInit, DoCheck { _messageId = null; // Message id or filename diff --git a/src/app/models/bindable-selection-model.ts b/src/app/models/bindable-selection-model.ts new file mode 100644 index 000000000..c7fe3a0ea --- /dev/null +++ b/src/app/models/bindable-selection-model.ts @@ -0,0 +1,44 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2025 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 { SelectionModel } from '@angular/cdk/collections'; + +export class BindableSelectionModel { + selectionModel: SelectionModel; + + constructor( + multiple: boolean, + initialValues: T[] = [], + emitChanges: boolean = true, + compareWith: (a: T, b: T) => boolean = (a, b) => a === b, + ) { + this.selectionModel = new SelectionModel(multiple, initialValues, emitChanges, compareWith); + } + + // Getter for `selected` + get selected(): T | T[] { + return this.selectionModel.isMultipleSelection() ? this.selectionModel.selected : this.selectionModel.selected[0]; + } + + // Setter for `selected` + set selected(items: T | T[]) { + const selection = (this.selectionModel.isMultipleSelection() ? items : [items]) as T[]; + this.selectionModel.setSelection(...selection) + } +} diff --git a/src/app/models/filter-selection-model.ts b/src/app/models/filter-selection-model.ts new file mode 100644 index 000000000..f771112b6 --- /dev/null +++ b/src/app/models/filter-selection-model.ts @@ -0,0 +1,38 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2025 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 { SelectionModel } from '@angular/cdk/collections'; + +export class FilterSelectionModel extends SelectionModel { + constructor(multiple: boolean, initialValues: T[], emitChanges: boolean, compareWith: (a: T, b: T) => boolean, predicate: (a) => boolean) { + super(multiple, initialValues, emitChanges, compareWith); + + return new Proxy(this, { + get(target, prop) { + if (prop === 'select') { + return (...items: T[]) => { + return target.select(...items.filter(predicate)); + }; + } + + return target[prop]; + } + }); + } +} diff --git a/src/app/resizable-button/resizable-button.component.html b/src/app/resizable-button/resizable-button.component.html new file mode 100644 index 000000000..74a099e92 --- /dev/null +++ b/src/app/resizable-button/resizable-button.component.html @@ -0,0 +1,11 @@ + diff --git a/src/app/resizable-button/resizable-button.component.scss b/src/app/resizable-button/resizable-button.component.scss new file mode 100644 index 000000000..c88828c67 --- /dev/null +++ b/src/app/resizable-button/resizable-button.component.scss @@ -0,0 +1,25 @@ +button { + --fg: #333; + border-left: 1px solid var(--fg); + border-right: 1px solid var(--fg); + border-top: none; + border-bottom: none; + position: absolute; + top: 0; + bottom: 0; + cursor: col-resize; + right: 0; + width: 0.5rem; + border-radius: unset; + padding: 0; + margin: 0; + display: block; + transition: opacity 0.1s ease; + opacity: 0; + background: none; +} + +button:hover, button:focus, button.resizing { + opacity: 1; +} + diff --git a/src/app/resizable-button/resizable-button.component.ts b/src/app/resizable-button/resizable-button.component.ts new file mode 100644 index 000000000..bd978debf --- /dev/null +++ b/src/app/resizable-button/resizable-button.component.ts @@ -0,0 +1,154 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2025 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 { Component, ElementRef, EventEmitter, Output, AfterViewInit, Input, HostListener } from '@angular/core'; +import { Subject } from 'rxjs'; +import { take } from 'rxjs/operators'; + +const userResize = new Subject() + +@Component({ + selector: 'app-resizable-button', + templateUrl: './resizable-button.component.html', + styleUrls: ['./resizable-button.component.scss'], + standalone: true, +}) +export class ResizableButtonComponent implements AfterViewInit { + + @Input() width: number; + @Output() widthChange = new EventEmitter(); + + isResizing = false; + private startX: number = 0; + private startWidth: number = 0; + + // Hold the reference to the event listeners + private onMouseMoveListener: (event: MouseEvent) => void; + private onMouseUpListener: () => void; + private initialWidth: string | null = null; + + constructor(private elementRef: ElementRef) { + // Only set absolute value when the user does a resize. + userResize.pipe(take(1)).subscribe(() => { + this.setAbsoluteWidth(); + }); + } + + ngAfterViewInit() { + this.initialWidth = this.parentElement.style.width + } + + get parentElement() { + return this.elementRef.nativeElement.parentElement; + } + + setAbsoluteWidth() { + setTimeout(() => { + if (!this.parentElement) return + + this.changeWidth(this.parentElement.offsetWidth); + }, 0) + } + + resetWidth() { + const parentElement = this.elementRef.nativeElement.parentElement; + + parentElement.style.width = this.initialWidth; + this.setAbsoluteWidth() + } + + @HostListener('window:resize') + onWindowResize() { + this.resetWidth(); + } + + onMouseDown(event: MouseEvent): void { + this.isResizing = true; + this.startX = event.clientX; + const parentElement = this.elementRef.nativeElement.parentElement; + if (parentElement) { + this.startWidth = parentElement.offsetWidth; + } + + // Define the mouse move and up handlers + this.onMouseMoveListener = this.onMouseMove.bind(this); + this.onMouseUpListener = this.onMouseUp.bind(this); + + // Add the mousemove and mouseup event listeners to the document + document.addEventListener('mousemove', this.onMouseMoveListener); + document.addEventListener('mouseup', this.onMouseUpListener); + + // Prevent text selection during resizing + event.preventDefault(); + } + + private onMouseMove(event: MouseEvent): void { + if (!this.isResizing) return; + + const parentElement = this.elementRef.nativeElement.parentElement; + if (parentElement) { + const diff = event.clientX - this.startX; + const newWidth = this.startWidth + diff; + this.changeWidth(newWidth); + } + } + + private onMouseUp(): void { + this.isResizing = false; + this.removeMouseListeners(); + } + + @HostListener('document:keydown', ['$event']) + onKeyDown(event: KeyboardEvent): void { + if (!this.elementRef.nativeElement.contains(document.activeElement)) { + return; + } + + const parentElement = this.elementRef.nativeElement.parentElement; + if (!parentElement) return; + + const step = 10; // Resize step for each key press + const currentWidth = parentElement.offsetWidth; + + if (event.key === 'ArrowRight') { + this.changeWidth(currentWidth + step); + } else if (event.key === 'ArrowLeft') { + this.changeWidth(currentWidth - step); + } + } + + changeWidth(pixels: number) { + this.widthChange.emit(pixels) + userResize.next(pixels) + } + + @HostListener('window:blur') + @HostListener('window:focus') + onWindowFocus(): void { + if (this.isResizing) { + this.isResizing = false; // Stop resizing immediately + this.removeMouseListeners(); // Remove listeners if resizing was interrupted + } + } + + private removeMouseListeners(): void { + document.removeEventListener('mousemove', this.onMouseMoveListener); + document.removeEventListener('mouseup', this.onMouseUpListener); + } +} diff --git a/src/app/rmmapi/messagelist.service.ts b/src/app/rmmapi/messagelist.service.ts index 964df9792..3db8ec2c4 100644 --- a/src/app/rmmapi/messagelist.service.ts +++ b/src/app/rmmapi/messagelist.service.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 ---------- @@ -61,6 +61,7 @@ export class MessageListService { staleFolders: { [name: string]: boolean } = {}; trashFolderName = 'Trash'; + sentFolderName = 'Sent'; spamFolderName = 'Spam'; unindexedFolders = ['Trash', 'Spam', 'Templates']; templateFolderName = 'Templates'; diff --git a/src/app/sort-button/sort-button.component.ts b/src/app/sort-button/sort-button.component.ts new file mode 100644 index 000000000..15d086add --- /dev/null +++ b/src/app/sort-button/sort-button.component.ts @@ -0,0 +1,121 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2025 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 { Component, Input, Output, EventEmitter } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatIconModule } from '@angular/material/icon'; + +export enum Direction { + Ascending = 'ASC', + Descending = 'DESC', + None = 'NONE' +} + +export interface OrderEvent { + data: any; + direction: Direction; +} + +@Component({ + standalone: true, + imports: [CommonModule, MatIconModule], // Use MatIconModule instead of MatIcon + selector: 'app-sort-button', + template: ` + + `, + styles: [ + ` + .sort-button { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + background: none; + border: none; + font-size: inherit; + font-weight: inherit; + padding-left: 0; + } + + .sort-button:hover { + text-decoration: underline; + } + `, + ], +}) +export class SortButtonComponent { + @Input() order: OrderEvent = { data: Symbol('init'), direction: Direction.None }; + @Input() data: any; + @Output() orderChange = new EventEmitter(); + + readonly Direction = Direction; // Make enum accessible in template + + // Map defining the state transitions + private readonly directionCycle = new Map([ + [Direction.Ascending, Direction.Descending], + [Direction.Descending, Direction.None], + [Direction.None, Direction.Ascending], + ]); + + private readonly hrDirectionTr = new Map([ + [Direction.Ascending, 'ascending'], + [Direction.Descending, 'descending'], + [Direction.None, 'no particular'], + ]) + + private readonly directionIconMap = new Map([ + [Direction.Ascending, 'arrow_upward'], + [Direction.Descending, 'arrow_downward'], + [Direction.None, 'empty'], + ]); + + get directionIcon() { + return (this.data === this.order?.data) + ? this.directionIconMap.get(this.order?.direction) + : this.directionIconMap.get(Direction.None); + } + + get hrDirection() { + return this.hrDirectionTr.get(this.order?.direction) + } + + toggleSort(): void { + // Set direction to Ascending when switching columns. + const direction = (this.order?.data !== this.data) + ? Direction.Ascending + : this.directionCycle.get(this.order?.direction) ?? Direction.Ascending + + this.orderChange.emit({ + data: this.data, + direction, + }); + } +} diff --git a/src/app/websocketsearch/websocketsearchmaillist.ts b/src/app/websocketsearch/websocketsearchmaillist.ts index e70c19e8f..9158afad2 100644 --- a/src/app/websocketsearch/websocketsearchmaillist.ts +++ b/src/app/websocketsearch/websocketsearchmaillist.ts @@ -98,4 +98,15 @@ export class WebSocketSearchMailList extends MessageDisplay { return columns; } + getRowData(rowIndex, app) { + return { + id: this.getRowMessageId(rowIndex), + selectbox: this.isSelectedRow(rowIndex), + messageDate: this.getRow(rowIndex).dateTime, + from: this.getRow(rowIndex).fromName, + subject: this.getRow(rowIndex).subject, + size: this.getRow(rowIndex).size, + }; + } + } diff --git a/src/app/xapian/searchmessagedisplay.ts b/src/app/xapian/searchmessagedisplay.ts index 6c9a1271a..0639fc70a 100644 --- a/src/app/xapian/searchmessagedisplay.ts +++ b/src/app/xapian/searchmessagedisplay.ts @@ -32,7 +32,7 @@ export class SearchMessageDisplay extends MessageDisplay { } getRowSeen(index: number): boolean { - return this.searchService.getDocData(this.rows[index][0]).seen ? false : true; + return this.searchService.getDocData(this.getRowId(index)).seen; } getRowId(index: number): number { @@ -42,11 +42,11 @@ export class SearchMessageDisplay extends MessageDisplay { getRowMessageId(index: number): number { let msgId = 0; try { - msgId = this.searchService.getMessageIdFromDocId(this.rows[index][0]); + msgId = this.searchService.getMessageIdFromDocId(this.getRowId(index)); } catch (e) { // This shouldnt happen, it means something changed the stored // data without updating the messagedisplay rows. - console.log('Tried to lookup ' + index + ' in searchIndex, isnt there! ' + e); + console.error('Tried to lookup ' + index + ' in searchIndex, isnt there! ' + e); } return msgId; } @@ -215,4 +215,37 @@ export class SearchMessageDisplay extends MessageDisplay { } return columns; } + + public getRowData(index: number, app: any) { + const rowData: any = { + id: this.getRowMessageId(index), + messageDate: MessageTableRowTool.formatTimestampFromStringWithoutSeparators(this.searchService.api.getStringValue(this.getRowId(index), 2)), + from: app.selectedFolder.indexOf('Sent') === 0 && !app.displayFolderColumn + ? this.searchService.getDocData(this.getRowId(index)).recipients.join(', ') + : this.searchService.getDocData(this.getRowId(index)).from, + subject: this.searchService.getDocData(this.getRowId(index)).subject, + plaintext: this.searchService.getDocData(this.getRowId(index)).textcontent?.trim(), + size: this.searchService.api.getNumericValue(this.getRowId(index), 3), + attachment: this.searchService.getDocData(this.getRowId(index)).attachment ? true : false, + answered: this.searchService.getDocData(this.getRowId(index)).answered ? true : false, + flagged: this.searchService.getDocData(this.getRowId(index)).flagged ? true : false, + folder: this.searchService.getDocData(this.getRowId(index)).folder, + seen: this.searchService.getDocData(this.getRowId(index)).seen, + }; + + if (app.viewmode === 'conversations') { + const row = this.getRow(index); + if (!row[2]) { + const conversationId = this.searchService.api.getStringValue(row[0], 1); + const results = this.searchService.api.sortedXapianQuery( + `conversation:${conversationId}..${conversationId}`, + 1, 0, 0, 1000, 1 + ); + row[2] = `${results[0][1] + 1}`; + } + rowData.count = row[2]; + } + + return rowData; + } } diff --git a/src/app/xapian/searchservice.ts b/src/app/xapian/searchservice.ts index cfc971c5a..9450997db 100644 --- a/src/app/xapian/searchservice.ts +++ b/src/app/xapian/searchservice.ts @@ -258,10 +258,10 @@ export class SearchService { FS.syncfs(true, () => { // console.log('Main: Syncd files:'); // console.log(FS.stat(XAPIAN_GLASS_WR)); - FS.readdir(this.partitionsdir).forEach((f) => { + // FS.readdir(this.partitionsdir).forEach((f) => { // console.log(`${f}`); // console.log(FS.stat(`${this.partitionsdir}/${f}`)); - }); + // }); this.api.reloadXapianDatabase(); this.indexReloadedSubject.next(undefined); }); diff --git a/src/styles.scss b/src/styles.scss index 62e3ea48a..efa86c7d5 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; @@ -159,7 +159,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 +313,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; } @@ -337,6 +337,10 @@ mat-grid-tile.tableTitle { font-size: 12px; } +.text-primary { + color: mat.get-color-from-palette($rmm-default-primary); +} + .warning { color: mat.get-color-from-palette($rmm-default-warn); font-weight: bold; @@ -394,12 +398,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; @@ -429,7 +433,7 @@ mat-grid-tile.tableTitle { display: none; margin: 0; } - + #logo { margin: 0; width: 300px; @@ -508,7 +512,7 @@ div.loginScreen { mat-form-field { width: 200px; } - } + } #loginOptions { display: flex; margin: 0.5em; @@ -714,8 +718,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; @@ -772,7 +776,7 @@ mat-sidenav-container { .mat-mini-fab .mat-button-wrapper { line-height: 18px; } - + a { width: 30%; } @@ -926,7 +930,7 @@ rmm-folderlist { } .foldersidebarcount { - font-size: 10px; + font-size: 10px; } .draftsFolder { @@ -972,7 +976,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 } @@ -998,7 +1002,7 @@ app-saved-searches .mat-list-base[dense] .mat-list-item { button { margin-right: 10px; - + @media(max-width: 540px) { margin-right: 2px; height: 30px; @@ -1012,7 +1016,7 @@ app-saved-searches .mat-list-base[dense] .mat-list-item { #offerLocalIndex .mat-list-item-content { padding: 0 5px; -} +} #searchField { flex-grow: 10; @@ -1087,7 +1091,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; @@ -1162,7 +1166,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; } @@ -1185,7 +1189,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; @@ -1297,7 +1301,7 @@ compose #fieldFrom .mat-form-field-wrapper, compose .fieldRecipient .mat-form-fi .recipientSuggestionContainer { max-height: 90px; overflow: auto; - + span { font-size: 12.5px; } @@ -1337,7 +1341,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; @@ -1360,7 +1364,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; } @@ -1531,7 +1535,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; */ @@ -1581,7 +1585,7 @@ app-calendar-event-editor-dialog p { color: #0F0; } -.dev.runbox-components .nice_green_timer .timeunit { +.dev.runbox-components .nice_green_timer .timeunit { border: 1px solid #0F0; width: 40px; height: 40px; @@ -1596,22 +1600,22 @@ app-calendar-event-editor-dialog p { .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"; } @@ -1627,22 +1631,22 @@ app-calendar-event-editor-dialog p { 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"; } @@ -1730,7 +1734,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%; @@ -1740,3 +1744,28 @@ table.detailsTable tr td:nth-of-type(2) { display: flex; justify-content: flex-end; } + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + border: 0; + clip: rect(0, 0, 0, 0); + overflow: hidden; +} + +.bold { + font-weight: bold; +} + +.text-center { + text-align: center; +} + +/* Transition causes column width calculations to glitch. */ +.mat-drawer-transition .mat-drawer-content { + transition: none !important; +} +