mirror of
https://github.com/tutao/tutanota.git
synced 2025-10-19 16:03:43 +00:00
Move indexer and mail search to mail-app
This commit is contained in:
parent
f04bf16bd3
commit
f99bd69ff0
89 changed files with 1464 additions and 704 deletions
|
@ -98,7 +98,7 @@ async function buildWebPart({ stage, host, version, domainConfigs, app }) {
|
|||
const tsConfig = isCalendarBuild ? "tsconfig-calendar-app.json" : "tsconfig.json"
|
||||
const buildDir = isCalendarBuild ? "build-calendar-app" : "build"
|
||||
const entryFile = isCalendarBuild ? "src/calendar-app/calendar-app.ts" : "src/mail-app/app.ts"
|
||||
const workerFile = isCalendarBuild ? "src/calendar-app/calendar-worker.ts" : "src/mail-app/mail-worker.ts"
|
||||
const workerFile = isCalendarBuild ? "src/calendar-app/workerUtils/worker/calendar-worker.ts" : "src/mail-app/workerUtils/worker/mail-worker.ts"
|
||||
|
||||
await runStep("Web: Assets", async () => {
|
||||
await prepareAssets(stage, host, version, domainConfigs, buildDir)
|
||||
|
|
|
@ -229,7 +229,12 @@ export function getChunkName(moduleId, { getModuleInfo }) {
|
|||
return "wasm"
|
||||
} else if (moduleId.includes("wasm-fallback")) {
|
||||
return "wasm-fallback"
|
||||
} else if (isIn("src/common/native/worker")) {
|
||||
} else if (
|
||||
isIn("src/common/native/worker") ||
|
||||
isIn("src/mail-app/workerUtils/worker") ||
|
||||
isIn("src/calendar-app/worker") ||
|
||||
isIn("src/mail-app/workerUtils/offline")
|
||||
) {
|
||||
return "worker"
|
||||
} else if (isIn("src/common/native/common")) {
|
||||
return "native-common"
|
||||
|
@ -280,7 +285,7 @@ export function getChunkName(moduleId, { getModuleInfo }) {
|
|||
} else if (isIn("src/common/api/worker/facades/lazy")) {
|
||||
// things that are not used for login and are generally accessed occasionally
|
||||
return "worker-lazy"
|
||||
} else if (isIn("src/common/api/worker/search")) {
|
||||
} else if (isIn("src/common/api/worker/search") || isIn("src/mail-app/workerUtils/index")) {
|
||||
// things related to indexer or search
|
||||
return "worker-search"
|
||||
} else if (isIn("src/common/api/worker/Urlifier") || isIn("libs/linkify") || isIn("libs/linkify-html")) {
|
||||
|
|
|
@ -33,7 +33,7 @@ export async function buildWebapp({ version, stage, host, measure, minify, proje
|
|||
const tsConfig = isCalendarApp ? "tsconfig-calendar-app.json" : "tsconfig.json"
|
||||
const buildDir = isCalendarApp ? "build-calendar-app" : "build"
|
||||
const entryFile = isCalendarApp ? "src/calendar-app/calendar-app.ts" : "src/mail-app/app.ts"
|
||||
const workerFile = isCalendarApp ? "src/calendar-app/calendar-worker.ts" : "src/mail-app/mail-worker.ts"
|
||||
const workerFile = isCalendarApp ? "src/calendar-app/workerUtils/worker/calendar-worker.ts" : "src/mail-app/workerUtils/worker/mail-worker.ts"
|
||||
|
||||
console.log("Building app", app)
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ import("../mail-app/translations/en.js")
|
|||
setupNavShortcuts()
|
||||
|
||||
// this needs to stay after client.init
|
||||
windowFacade.init(calendarLocator.logins, calendarLocator.indexerFacade, calendarLocator.connectivityModel)
|
||||
windowFacade.init(calendarLocator.logins, calendarLocator.connectivityModel, null)
|
||||
if (isDesktop()) {
|
||||
import("../common/native/main/UpdatePrompt.js").then(({ registerForUpdates }) => registerForUpdates(calendarLocator.desktopSettingsFacade))
|
||||
}
|
||||
|
|
|
@ -396,17 +396,8 @@ export class CalendarSearchBar implements Component<CalendarSearchBarAttrs> {
|
|||
}
|
||||
|
||||
if (this.isQuickSearch()) {
|
||||
if (safeLimit && hasMoreResults(safeResult) && safeResult.results.length < safeLimit) {
|
||||
calendarLocator.searchFacade.getMoreSearchResults(safeResult, safeLimit - safeResult.results.length).then((moreResults) => {
|
||||
if (calendarLocator.search.isNewSearch(query, moreResults.restriction)) {
|
||||
return
|
||||
} else {
|
||||
this.loadAndDisplayResult(query, moreResults, limit)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.showResultsInOverlay(safeResult)
|
||||
}
|
||||
// Calendar does not have a quick search bar, so this has been taken out
|
||||
// but this is left in case this changes in the future
|
||||
} else {
|
||||
// instances will be displayed as part of the list of the search view, when the search view is displayed
|
||||
searchRouter.routeTo(query, safeResult.restriction)
|
||||
|
|
|
@ -2,7 +2,6 @@ import stream from "mithril/stream"
|
|||
import Stream from "mithril/stream"
|
||||
import type { SearchRestriction, SearchResult } from "../../../../common/api/worker/search/SearchTypes"
|
||||
import { arrayEquals, assertNonNull, assertNotNull, incrementMonth, isSameTypeRef, lazyAsync, tokenize } from "@tutao/tutanota-utils"
|
||||
import type { SearchFacade } from "../../../../common/api/worker/search/SearchFacade"
|
||||
import { assertMainOrNode } from "../../../../common/api/common/Env"
|
||||
import { listIdPart } from "../../../../common/api/common/utils/EntityUtils.js"
|
||||
import { IProgressMonitor } from "../../../../common/api/common/utils/ProgressMonitor.js"
|
||||
|
@ -22,24 +21,24 @@ export class CalendarSearchModel {
|
|||
// we store this as a reference to the currently running search. if we don't, we only have the last result's query info
|
||||
// to compare against incoming new queries
|
||||
lastQueryString: Stream<string | null>
|
||||
private _lastQuery: SearchQuery | null
|
||||
_lastSearchPromise: Promise<SearchResult | void>
|
||||
private lastQuery: SearchQuery | null
|
||||
private lastSearchPromise: Promise<SearchResult | void>
|
||||
cancelSignal: Stream<boolean>
|
||||
|
||||
constructor(private readonly calendarModel: lazyAsync<CalendarEventsRepository>) {
|
||||
this.result = stream()
|
||||
this.lastQueryString = stream<string | null>("")
|
||||
this._lastQuery = null
|
||||
this._lastSearchPromise = Promise.resolve()
|
||||
this.lastQuery = null
|
||||
this.lastSearchPromise = Promise.resolve()
|
||||
this.cancelSignal = stream(false)
|
||||
}
|
||||
|
||||
async search(searchQuery: SearchQuery, progressTracker: ProgressTracker): Promise<SearchResult | void> {
|
||||
if (this._lastQuery && searchQueryEquals(searchQuery, this._lastQuery)) {
|
||||
return this._lastSearchPromise
|
||||
if (this.lastQuery && searchQueryEquals(searchQuery, this.lastQuery)) {
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
|
||||
this._lastQuery = searchQuery
|
||||
this.lastQuery = searchQuery
|
||||
const { query, restriction, minSuggestionCount, maxResults } = searchQuery
|
||||
this.lastQueryString(query)
|
||||
let result = this.result()
|
||||
|
@ -63,7 +62,7 @@ export class CalendarSearchModel {
|
|||
moreResultsEntries: [],
|
||||
}
|
||||
this.result(result)
|
||||
this._lastSearchPromise = Promise.resolve(result)
|
||||
this.lastSearchPromise = Promise.resolve(result)
|
||||
} else {
|
||||
// we interpret restriction.start as the start of the first day of the first month we want to search
|
||||
// restriction.end is the end of the last day of the last month we want to search
|
||||
|
@ -94,8 +93,8 @@ export class CalendarSearchModel {
|
|||
|
||||
if (this.cancelSignal()) {
|
||||
this.result(calendarResult)
|
||||
this._lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this._lastSearchPromise
|
||||
this.lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
|
||||
await calendarModel.loadMonthsIfNeeded(daysInMonths, monitor, this.cancelSignal)
|
||||
|
@ -113,8 +112,8 @@ export class CalendarSearchModel {
|
|||
|
||||
if (this.cancelSignal()) {
|
||||
this.result(calendarResult)
|
||||
this._lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this._lastSearchPromise
|
||||
this.lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
|
||||
if (tokens.length > 0) {
|
||||
|
@ -163,23 +162,23 @@ export class CalendarSearchModel {
|
|||
|
||||
if (this.cancelSignal()) {
|
||||
this.result(calendarResult)
|
||||
this._lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this._lastSearchPromise
|
||||
this.lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.result(calendarResult)
|
||||
this._lastSearchPromise = Promise.resolve(calendarResult)
|
||||
this.lastSearchPromise = Promise.resolve(calendarResult)
|
||||
}
|
||||
|
||||
return this._lastSearchPromise
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
|
||||
isNewSearch(query: string, restriction: SearchRestriction): boolean {
|
||||
let isNew = false
|
||||
let lastQuery = this._lastQuery
|
||||
let lastQuery = this.lastQuery
|
||||
if (lastQuery == null) {
|
||||
isNew = true
|
||||
} else if (lastQuery.query !== query) {
|
||||
|
|
|
@ -15,7 +15,7 @@ import { KindaCalendarRow } from "../../gui/CalendarRow.js"
|
|||
|
||||
assertMainOrNode()
|
||||
|
||||
export class SearchResultListEntry {
|
||||
export class CalendarSearchResultListEntry {
|
||||
constructor(readonly entry: CalendarEvent) {}
|
||||
|
||||
get _id(): IdTuple {
|
||||
|
@ -24,14 +24,14 @@ export class SearchResultListEntry {
|
|||
}
|
||||
|
||||
export interface CalendarSearchListViewAttrs {
|
||||
listModel: ListModel<SearchResultListEntry>
|
||||
onSingleSelection: (item: SearchResultListEntry) => unknown
|
||||
listModel: ListModel<CalendarSearchResultListEntry>
|
||||
onSingleSelection: (item: CalendarSearchResultListEntry) => unknown
|
||||
isFreeAccount: boolean
|
||||
cancelCallback: () => unknown | null
|
||||
}
|
||||
|
||||
export class CalendarSearchListView implements Component<CalendarSearchListViewAttrs> {
|
||||
private listModel: ListModel<SearchResultListEntry>
|
||||
private listModel: ListModel<CalendarSearchResultListEntry>
|
||||
|
||||
constructor({ attrs }: Vnode<CalendarSearchListViewAttrs>) {
|
||||
this.listModel = attrs.listModel
|
||||
|
@ -57,14 +57,14 @@ export class CalendarSearchListView implements Component<CalendarSearchListViewA
|
|||
onRetryLoading: () => {
|
||||
attrs.listModel?.retryLoading()
|
||||
},
|
||||
onSingleSelection: (item: SearchResultListEntry) => {
|
||||
onSingleSelection: (item: CalendarSearchResultListEntry) => {
|
||||
attrs.listModel?.onSingleSelection(item)
|
||||
attrs.onSingleSelection(item)
|
||||
},
|
||||
onSingleTogglingMultiselection: (item: SearchResultListEntry) => {
|
||||
onSingleTogglingMultiselection: (item: CalendarSearchResultListEntry) => {
|
||||
attrs.listModel.onSingleInclusiveSelection(item, styles.isSingleColumnLayout())
|
||||
},
|
||||
onRangeSelectionTowards: (item: SearchResultListEntry) => {
|
||||
onRangeSelectionTowards: (item: CalendarSearchResultListEntry) => {
|
||||
attrs.listModel.selectRangeTowards(item)
|
||||
},
|
||||
onStopLoading() {
|
||||
|
@ -74,10 +74,10 @@ export class CalendarSearchListView implements Component<CalendarSearchListViewA
|
|||
|
||||
attrs.listModel.stopLoading()
|
||||
},
|
||||
} satisfies ListAttrs<SearchResultListEntry, SearchResultListRow>)
|
||||
} satisfies ListAttrs<CalendarSearchResultListEntry, SearchResultListRow>)
|
||||
}
|
||||
|
||||
private readonly calendarRenderConfig: RenderConfig<SearchResultListEntry, SearchResultListRow> = {
|
||||
private readonly calendarRenderConfig: RenderConfig<CalendarSearchResultListEntry, SearchResultListRow> = {
|
||||
itemHeight: size.list_row_height,
|
||||
multiselectionAllowed: MultiselectMode.Disabled,
|
||||
swipe: null,
|
||||
|
@ -89,14 +89,14 @@ export class CalendarSearchListView implements Component<CalendarSearchListViewA
|
|||
}
|
||||
}
|
||||
|
||||
export class SearchResultListRow implements VirtualRow<SearchResultListEntry> {
|
||||
export class SearchResultListRow implements VirtualRow<CalendarSearchResultListEntry> {
|
||||
top: number
|
||||
// set from List
|
||||
domElement: HTMLElement | null = null
|
||||
|
||||
// this is our own entry which we need for some reason (probably easier to deal with than a lot of sum type entries)
|
||||
private _entity: SearchResultListEntry | null = null
|
||||
get entity(): SearchResultListEntry | null {
|
||||
private _entity: CalendarSearchResultListEntry | null = null
|
||||
get entity(): CalendarSearchResultListEntry | null {
|
||||
return this._entity
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ export class SearchResultListRow implements VirtualRow<SearchResultListEntry> {
|
|||
this.top = 0
|
||||
}
|
||||
|
||||
update(entry: SearchResultListEntry, selected: boolean, isInMultiSelect: boolean): void {
|
||||
update(entry: CalendarSearchResultListEntry, selected: boolean, isInMultiSelect: boolean): void {
|
||||
this._delegate.domElement = this.domElement!
|
||||
this._entity = entry
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ListModel } from "../../../../common/misc/ListModel.js"
|
||||
import { SearchResultListEntry } from "./CalendarSearchListView.js"
|
||||
import { CalendarSearchResultListEntry } from "./CalendarSearchListView.js"
|
||||
import { SearchRestriction, SearchResult } from "../../../../common/api/worker/search/SearchTypes.js"
|
||||
import { EntityEventsListener, EventController } from "../../../../common/api/main/EventController.js"
|
||||
import { CalendarEvent, CalendarEventTypeRef, Contact, Mail, MailTypeRef } from "../../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
|
@ -27,7 +27,6 @@ import { createRestriction, decodeCalendarSearchKey, encodeCalendarSearchKey, ge
|
|||
import Stream from "mithril/stream"
|
||||
import stream from "mithril/stream"
|
||||
import { getStartOfTheWeekOffsetForUser } from "../../../../common/calendar/date/CalendarUtils.js"
|
||||
import { SearchFacade } from "../../../../common/api/worker/search/SearchFacade.js"
|
||||
import { LoginController } from "../../../../common/api/main/LoginController.js"
|
||||
import { EntityClient } from "../../../../common/api/common/EntityClient.js"
|
||||
import { containsEventOfType, EntityUpdateData, getEventOfType, isUpdateForTypeRef } from "../../../../common/api/common/utils/EntityUpdateUtils.js"
|
||||
|
@ -39,11 +38,10 @@ import { ListAutoSelectBehavior } from "../../../../common/misc/DeviceConfig.js"
|
|||
import { ProgrammingError } from "../../../../common/api/common/error/ProgrammingError.js"
|
||||
import { SearchRouter } from "../../../../common/search/view/SearchRouter.js"
|
||||
import { locator } from "../../../../common/api/main/CommonLocator.js"
|
||||
import { SearchResultListEntry } from "../../../../mail-app/search/view/SearchListView.js"
|
||||
|
||||
const SEARCH_PAGE_SIZE = 100
|
||||
|
||||
export type SearchableTypes = Mail | Contact | CalendarEvent
|
||||
|
||||
export enum PaidFunctionResult {
|
||||
Success,
|
||||
PaidSubscriptionNeeded,
|
||||
|
@ -52,7 +50,7 @@ export enum PaidFunctionResult {
|
|||
export type ConfirmCallback = () => Promise<boolean>
|
||||
|
||||
export class CalendarSearchViewModel {
|
||||
listModel: ListModel<SearchResultListEntry>
|
||||
listModel: ListModel<CalendarSearchResultListEntry>
|
||||
|
||||
// Contains load more results even when searchModel doesn't.
|
||||
// Load more should probably be moved to the model to update it's result stream.
|
||||
|
@ -78,7 +76,6 @@ export class CalendarSearchViewModel {
|
|||
constructor(
|
||||
readonly router: SearchRouter,
|
||||
private readonly search: CalendarSearchModel,
|
||||
private readonly searchFacade: SearchFacade,
|
||||
private readonly logins: LoginController,
|
||||
private readonly entityClient: EntityClient,
|
||||
private readonly eventController: EventController,
|
||||
|
@ -224,7 +221,7 @@ export class CalendarSearchViewModel {
|
|||
|
||||
if (args.id != null) {
|
||||
const { start, id } = decodeCalendarSearchKey(args.id)
|
||||
this.loadAndSelectIfNeeded(id, ({ entry }: SearchResultListEntry) => {
|
||||
this.loadAndSelectIfNeeded(id, ({ entry }: CalendarSearchResultListEntry) => {
|
||||
entry = entry as CalendarEvent
|
||||
return id === getElementId(entry) && start === entry.startTime.getTime()
|
||||
})
|
||||
|
@ -384,21 +381,20 @@ export class CalendarSearchViewModel {
|
|||
.filter(assertIsEntity2(CalendarEventTypeRef))
|
||||
}
|
||||
|
||||
private onListStateChange(newState: ListState<SearchResultListEntry>) {
|
||||
private onListStateChange(newState: ListState<CalendarSearchResultListEntry>) {
|
||||
this.updateSearchUrl()
|
||||
this.updateUi()
|
||||
}
|
||||
|
||||
private createList(): ListModel<SearchResultListEntry> {
|
||||
private createList(): ListModel<CalendarSearchResultListEntry> {
|
||||
// since we recreate the list every time we set a new result object,
|
||||
// we bind the value of result for the lifetime of this list model
|
||||
// at this point
|
||||
// note in case of refactor: the fact that the list updates the URL every time it changes
|
||||
// its state is a major source of complexity and makes everything very order-dependent
|
||||
return new ListModel<SearchResultListEntry>({
|
||||
fetch: async (lastFetchedEntity: SearchResultListEntry, count: number) => {
|
||||
return new ListModel<CalendarSearchResultListEntry>({
|
||||
fetch: async (lastFetchedEntity: CalendarSearchResultListEntry, count: number) => {
|
||||
const startId = lastFetchedEntity == null ? GENERATED_MAX_ID : getElementId(lastFetchedEntity)
|
||||
|
||||
const lastResult = this._searchResult
|
||||
if (lastResult !== this._searchResult) {
|
||||
console.warn("got a fetch request for outdated results object, ignoring")
|
||||
|
@ -411,7 +407,7 @@ export class CalendarSearchViewModel {
|
|||
}
|
||||
|
||||
const { items, newSearchResult } = await this.loadSearchResults(lastResult, startId, count)
|
||||
const entries = items.map((instance) => new SearchResultListEntry(instance))
|
||||
const entries = items.map((instance) => new CalendarSearchResultListEntry(instance))
|
||||
const complete = !hasMoreResults(newSearchResult)
|
||||
|
||||
return { items: entries, complete }
|
||||
|
@ -425,7 +421,7 @@ export class CalendarSearchViewModel {
|
|||
if (id) {
|
||||
return this.entityClient
|
||||
.load(lastResult.restriction.type, id)
|
||||
.then((entity) => new SearchResultListEntry(entity))
|
||||
.then((entity) => new CalendarSearchResultListEntry(entity))
|
||||
.catch(
|
||||
ofClass(NotFoundError, (_) => {
|
||||
return null
|
||||
|
@ -435,7 +431,7 @@ export class CalendarSearchViewModel {
|
|||
return null
|
||||
}
|
||||
},
|
||||
sortCompare: (o1: SearchResultListEntry, o2: SearchResultListEntry) =>
|
||||
sortCompare: (o1: CalendarSearchResultListEntry, o2: CalendarSearchResultListEntry) =>
|
||||
downcast(o1.entry).startTime.getTime() - downcast(o2.entry).startTime.getTime(),
|
||||
autoSelectBehavior: () => ListAutoSelectBehavior.OLDER,
|
||||
})
|
||||
|
@ -464,7 +460,7 @@ export class CalendarSearchViewModel {
|
|||
startId: Id,
|
||||
count: number,
|
||||
): Promise<{ items: CalendarEvent[]; newSearchResult: SearchResult }> {
|
||||
const updatedResult = hasMoreResults(currentResult) ? await this.searchFacade.getMoreSearchResults(currentResult, count) : currentResult
|
||||
const updatedResult = currentResult
|
||||
|
||||
// we need to override global reference for other functions
|
||||
this._searchResult = updatedResult
|
||||
|
|
|
@ -32,9 +32,6 @@ import { WhitelabelSettingsViewer } from "../../../common/settings/whitelabel/Wh
|
|||
import { SubscriptionViewer } from "../../../common/subscription/SubscriptionViewer.js"
|
||||
import { PaymentViewer } from "../../../common/subscription/PaymentViewer.js"
|
||||
import { ReferralSettingsViewer } from "../../../common/settings/ReferralSettingsViewer.js"
|
||||
import { GroupDetailsView } from "../../../common/settings/groups/GroupDetailsView.js"
|
||||
import { TemplateDetailsViewer } from "../../../mail-app/settings/TemplateDetailsViewer.js"
|
||||
import { KnowledgeBaseSettingsDetailsViewer } from "../../../mail-app/settings/KnowledgeBaseListView.js"
|
||||
import { NavButton, NavButtonAttrs, NavButtonColor } from "../../../common/gui/base/NavButton.js"
|
||||
import { CustomerInfoTypeRef, CustomerTypeRef, User } from "../../../common/api/entities/sys/TypeRefs.js"
|
||||
import { Dialog } from "../../../common/gui/base/Dialog.js"
|
||||
|
|
|
@ -20,8 +20,6 @@ import { CalendarFacade } from "../common/api/worker/facades/lazy/CalendarFacade
|
|||
import { MailFacade } from "../common/api/worker/facades/lazy/MailFacade.js"
|
||||
import { ShareFacade } from "../common/api/worker/facades/lazy/ShareFacade.js"
|
||||
import { CounterFacade } from "../common/api/worker/facades/lazy/CounterFacade.js"
|
||||
import { Indexer } from "../common/api/worker/search/Indexer.js"
|
||||
import { SearchFacade } from "../common/api/worker/search/SearchFacade.js"
|
||||
import { BookingFacade } from "../common/api/worker/facades/lazy/BookingFacade.js"
|
||||
import { MailAddressFacade } from "../common/api/worker/facades/lazy/MailAddressFacade.js"
|
||||
import { BlobFacade } from "../common/api/worker/facades/lazy/BlobFacade.js"
|
||||
|
@ -42,7 +40,6 @@ import { InterWindowEventFacadeSendDispatcher } from "../common/native/common/ge
|
|||
import { ExposedCacheStorage } from "../common/api/worker/rest/DefaultEntityRestCache.js"
|
||||
import { WorkerFacade } from "../common/api/worker/facades/WorkerFacade.js"
|
||||
import { PageContextLoginListener } from "../common/api/main/PageContextLoginListener.js"
|
||||
import { WorkerRandomizer } from "../common/api/worker/WorkerImpl.js"
|
||||
import { WebsocketConnectivityModel } from "../common/misc/WebsocketConnectivityModel.js"
|
||||
import { OperationProgressTracker } from "../common/api/main/OperationProgressTracker.js"
|
||||
import { InfoMessageHandler } from "../common/gui/InfoMessageHandler.js"
|
||||
|
@ -111,6 +108,8 @@ import type { ParsedEvent } from "../common/calendar/import/CalendarImporter.js"
|
|||
import { ExternalCalendarFacade } from "../common/native/common/generatedipc/ExternalCalendarFacade.js"
|
||||
import { locator } from "../common/api/main/CommonLocator.js"
|
||||
import m from "mithril"
|
||||
import { DbError } from "../common/api/common/error/DbError.js"
|
||||
import { WorkerRandomizer } from "../common/api/worker/workerInterfaces.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
@ -137,8 +136,6 @@ class CalendarLocator {
|
|||
mailFacade!: MailFacade
|
||||
shareFacade!: ShareFacade
|
||||
counterFacade!: CounterFacade
|
||||
indexerFacade!: Indexer
|
||||
searchFacade!: SearchFacade
|
||||
bookingFacade!: BookingFacade
|
||||
mailAddressFacade!: MailAddressFacade
|
||||
blobFacade!: BlobFacade
|
||||
|
@ -216,7 +213,6 @@ class CalendarLocator {
|
|||
return new CalendarSearchViewModel(
|
||||
searchRouter,
|
||||
this.search,
|
||||
this.searchFacade,
|
||||
this.logins,
|
||||
this.entityClient,
|
||||
this.eventController,
|
||||
|
@ -235,7 +231,6 @@ class CalendarLocator {
|
|||
return new CalendarSearchViewModel(
|
||||
searchRouter,
|
||||
this.search,
|
||||
this.searchFacade,
|
||||
this.logins,
|
||||
this.entityClient,
|
||||
this.eventController,
|
||||
|
@ -428,12 +423,12 @@ class CalendarLocator {
|
|||
}
|
||||
|
||||
async ownMailAddressNameChanger(): Promise<MailAddressNameChanger> {
|
||||
const { OwnMailAddressNameChanger } = await import("../mail-app/settings/mailaddress/OwnMailAddressNameChanger.js")
|
||||
const { OwnMailAddressNameChanger } = await import("../common/settings/mailaddress/OwnMailAddressNameChanger.js")
|
||||
return new OwnMailAddressNameChanger(this.mailboxModel, this.entityClient)
|
||||
}
|
||||
|
||||
async adminNameChanger(mailGroupId: Id, userId: Id): Promise<MailAddressNameChanger> {
|
||||
const { AnotherUserMailAddressNameChanger } = await import("../mail-app/settings/mailaddress/AnotherUserMailAddressNameChanger.js")
|
||||
const { AnotherUserMailAddressNameChanger } = await import("../common/settings/mailaddress/AnotherUserMailAddressNameChanger.js")
|
||||
return new AnotherUserMailAddressNameChanger(this.mailAddressFacade, mailGroupId, userId)
|
||||
}
|
||||
|
||||
|
@ -453,7 +448,10 @@ class CalendarLocator {
|
|||
const { NoopCredentialRemovalHandler, AppsCredentialRemovalHandler } = await import("../common/login/CredentialRemovalHandler.js")
|
||||
return isBrowser()
|
||||
? new NoopCredentialRemovalHandler()
|
||||
: new AppsCredentialRemovalHandler(this.indexerFacade, this.pushService, this.configFacade, null)
|
||||
: new AppsCredentialRemovalHandler(this.pushService, this.configFacade, async () => {
|
||||
// nothing needs to be specifically done for the calendar app right now.
|
||||
noOp()
|
||||
})
|
||||
}
|
||||
|
||||
async loginViewModelFactory(): Promise<lazy<LoginViewModel>> {
|
||||
|
@ -526,8 +524,6 @@ class CalendarLocator {
|
|||
mailFacade,
|
||||
shareFacade,
|
||||
counterFacade,
|
||||
indexerFacade,
|
||||
searchFacade,
|
||||
bookingFacade,
|
||||
mailAddressFacade,
|
||||
blobFacade,
|
||||
|
@ -553,8 +549,6 @@ class CalendarLocator {
|
|||
this.mailFacade = mailFacade
|
||||
this.shareFacade = shareFacade
|
||||
this.counterFacade = counterFacade
|
||||
this.indexerFacade = indexerFacade
|
||||
this.searchFacade = searchFacade
|
||||
this.bookingFacade = bookingFacade
|
||||
this.mailAddressFacade = mailAddressFacade
|
||||
this.blobFacade = blobFacade
|
||||
|
@ -707,7 +701,9 @@ class CalendarLocator {
|
|||
: new FileControllerNative(blobFacade, guiDownload, this.nativeInterfaces.fileApp)
|
||||
|
||||
const { ContactModel } = await import("../common/contactsFunctionality/ContactModel.js")
|
||||
this.contactModel = new ContactModel(this.searchFacade, this.entityClient, this.logins, this.eventController)
|
||||
this.contactModel = new ContactModel(this.entityClient, this.logins, this.eventController, () => {
|
||||
throw new DbError("Calendar cannot search for contacts through db")
|
||||
})
|
||||
this.appStorePaymentPicker = new AppStorePaymentPicker()
|
||||
|
||||
// THEME
|
||||
|
|
216
src/calendar-app/workerUtils/worker/CalendarWorkerImpl.ts
Normal file
216
src/calendar-app/workerUtils/worker/CalendarWorkerImpl.ts
Normal file
|
@ -0,0 +1,216 @@
|
|||
import type { Commands } from "../../../common/api/common/threading/MessageDispatcher.js"
|
||||
import { errorToObj, MessageDispatcher, Request } from "../../../common/api/common/threading/MessageDispatcher.js"
|
||||
import { NotAuthenticatedError } from "../../../common/api/common/error/RestError.js"
|
||||
import { ProgrammingError } from "../../../common/api/common/error/ProgrammingError.js"
|
||||
import { initLocator, locator, resetLocator } from "./CalendarWorkerLocator.js"
|
||||
import { assertWorkerOrNode, isMainOrNode } from "../../../common/api/common/Env.js"
|
||||
import type { BrowserData } from "../../../common/misc/ClientConstants.js"
|
||||
import { DelayedImpls, exposeLocalDelayed, exposeRemote } from "../../../common/api/common/WorkerProxy.js"
|
||||
import { random } from "@tutao/tutanota-crypto"
|
||||
import type { NativeInterface } from "../../../common/native/common/NativeInterface.js"
|
||||
import { WebWorkerTransport } from "../../../common/api/common/threading/Transport.js"
|
||||
import { CommonWorkerInterface, MainInterface } from "../../../common/api/worker/workerInterfaces.js"
|
||||
import { CryptoError } from "@tutao/tutanota-crypto/error.js"
|
||||
|
||||
assertWorkerOrNode()
|
||||
|
||||
type WorkerRequest = Request<WorkerRequestType>
|
||||
|
||||
export class CalendarWorkerImpl implements NativeInterface {
|
||||
private readonly _scope: DedicatedWorkerGlobalScope
|
||||
private readonly _dispatcher: MessageDispatcher<MainRequestType, WorkerRequestType>
|
||||
|
||||
constructor(self: DedicatedWorkerGlobalScope) {
|
||||
this._scope = self
|
||||
this._dispatcher = new MessageDispatcher(new WebWorkerTransport(this._scope), this.queueCommands(this.exposedInterface), "worker-main")
|
||||
}
|
||||
|
||||
async init(browserData: BrowserData): Promise<void> {
|
||||
await initLocator(this, browserData)
|
||||
const workerScope = this._scope
|
||||
|
||||
// only register oncaught error handler if we are in the *real* worker scope
|
||||
// Otherwise uncaught error handler might end up in an infinite loop for test cases.
|
||||
if (workerScope && !isMainOrNode()) {
|
||||
workerScope.addEventListener("unhandledrejection", (event: PromiseRejectionEvent) => {
|
||||
this.sendError(event.reason)
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
workerScope.onerror = (e: string | Event, source, lineno, colno, error) => {
|
||||
console.error("workerImpl.onerror", e, source, lineno, colno, error)
|
||||
|
||||
if (error instanceof Error) {
|
||||
this.sendError(error)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const err = new Error(e)
|
||||
// @ts-ignore
|
||||
err.lineNumber = lineno
|
||||
// @ts-ignore
|
||||
err.columnNumber = colno
|
||||
// @ts-ignore
|
||||
err.fileName = source
|
||||
this.sendError(err)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get exposedInterface(): DelayedImpls<CommonWorkerInterface> {
|
||||
return {
|
||||
async loginFacade() {
|
||||
return locator.login
|
||||
},
|
||||
|
||||
async customerFacade() {
|
||||
return locator.customer()
|
||||
},
|
||||
|
||||
async giftCardFacade() {
|
||||
return locator.giftCards()
|
||||
},
|
||||
|
||||
async groupManagementFacade() {
|
||||
return locator.groupManagement()
|
||||
},
|
||||
|
||||
async configFacade() {
|
||||
return locator.configFacade()
|
||||
},
|
||||
|
||||
async calendarFacade() {
|
||||
return locator.calendar()
|
||||
},
|
||||
|
||||
async mailFacade() {
|
||||
return locator.mail()
|
||||
},
|
||||
|
||||
async shareFacade() {
|
||||
return locator.share()
|
||||
},
|
||||
|
||||
async cacheManagementFacade() {
|
||||
return locator.cacheManagement()
|
||||
},
|
||||
|
||||
async counterFacade() {
|
||||
return locator.counters()
|
||||
},
|
||||
|
||||
async bookingFacade() {
|
||||
return locator.booking()
|
||||
},
|
||||
|
||||
async mailAddressFacade() {
|
||||
return locator.mailAddress()
|
||||
},
|
||||
|
||||
async blobAccessTokenFacade() {
|
||||
return locator.blobAccessToken
|
||||
},
|
||||
|
||||
async blobFacade() {
|
||||
return locator.blob()
|
||||
},
|
||||
|
||||
async userManagementFacade() {
|
||||
return locator.userManagement()
|
||||
},
|
||||
|
||||
async recoverCodeFacade() {
|
||||
return locator.recoverCode()
|
||||
},
|
||||
|
||||
async restInterface() {
|
||||
return locator.cache
|
||||
},
|
||||
|
||||
async serviceExecutor() {
|
||||
return locator.serviceExecutor
|
||||
},
|
||||
|
||||
async cryptoFacade() {
|
||||
return locator.crypto
|
||||
},
|
||||
|
||||
async cacheStorage() {
|
||||
return locator.cacheStorage
|
||||
},
|
||||
|
||||
async sqlCipherFacade() {
|
||||
return locator.sqlCipherFacade
|
||||
},
|
||||
|
||||
async random() {
|
||||
return {
|
||||
async generateRandomNumber(nbrOfBytes: number) {
|
||||
return random.generateRandomNumber(nbrOfBytes)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
async eventBus() {
|
||||
return locator.eventBusClient
|
||||
},
|
||||
|
||||
async entropyFacade() {
|
||||
return locator.entropyFacade
|
||||
},
|
||||
|
||||
async workerFacade() {
|
||||
return locator.workerFacade
|
||||
},
|
||||
|
||||
async contactFacade() {
|
||||
return locator.contactFacade()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
queueCommands(exposedWorker: DelayedImpls<CommonWorkerInterface>): Commands<WorkerRequestType> {
|
||||
return {
|
||||
setup: async (message) => {
|
||||
console.error("WorkerImpl: setup was called after bootstrap! message: ", message)
|
||||
},
|
||||
testEcho: (message) =>
|
||||
Promise.resolve({
|
||||
msg: ">>> " + message.args[0].msg,
|
||||
}),
|
||||
testError: (message) => {
|
||||
const errorTypes = {
|
||||
ProgrammingError,
|
||||
CryptoError,
|
||||
NotAuthenticatedError,
|
||||
}
|
||||
// @ts-ignore
|
||||
let ErrorType = errorTypes[message.args[0].errorType]
|
||||
return Promise.reject(new ErrorType(`wtf: ${message.args[0].errorType}`))
|
||||
},
|
||||
reset: (message: WorkerRequest) => {
|
||||
return resetLocator()
|
||||
},
|
||||
restRequest: (message: WorkerRequest) => {
|
||||
// Rest Requests only come from the admin client, not needed here
|
||||
return Promise.reject(new Error(`restRequest is not implemented for Calendar Worker`))
|
||||
},
|
||||
|
||||
facade: exposeLocalDelayed<DelayedImpls<CommonWorkerInterface>, WorkerRequestType>(exposedWorker),
|
||||
}
|
||||
}
|
||||
|
||||
invokeNative(requestType: string, args: ReadonlyArray<unknown>): Promise<any> {
|
||||
return this._dispatcher.postRequest(new Request("execNative", [requestType, args]))
|
||||
}
|
||||
|
||||
getMainInterface(): MainInterface {
|
||||
return exposeRemote<MainInterface>((request) => this._dispatcher.postRequest(request))
|
||||
}
|
||||
|
||||
sendError(e: Error): Promise<void> {
|
||||
return this._dispatcher.postRequest(new Request("error", [errorToObj(e)]))
|
||||
}
|
||||
}
|
456
src/calendar-app/workerUtils/worker/CalendarWorkerLocator.ts
Normal file
456
src/calendar-app/workerUtils/worker/CalendarWorkerLocator.ts
Normal file
|
@ -0,0 +1,456 @@
|
|||
import { CacheInfo, LoginFacade, LoginListener } from "../../../common/api/worker/facades/LoginFacade.js"
|
||||
import type { EntityRestInterface } from "../../../common/api/worker/rest/EntityRestClient.js"
|
||||
import { EntityRestClient } from "../../../common/api/worker/rest/EntityRestClient.js"
|
||||
import type { UserManagementFacade } from "../../../common/api/worker/facades/lazy/UserManagementFacade.js"
|
||||
import { CacheStorage, DefaultEntityRestCache, EntityRestCache } from "../../../common/api/worker/rest/DefaultEntityRestCache.js"
|
||||
import type { GroupManagementFacade } from "../../../common/api/worker/facades/lazy/GroupManagementFacade.js"
|
||||
import type { MailFacade } from "../../../common/api/worker/facades/lazy/MailFacade.js"
|
||||
import type { MailAddressFacade } from "../../../common/api/worker/facades/lazy/MailAddressFacade.js"
|
||||
import type { CustomerFacade } from "../../../common/api/worker/facades/lazy/CustomerFacade.js"
|
||||
import type { CounterFacade } from "../../../common/api/worker/facades/lazy/CounterFacade.js"
|
||||
import { EventBusClient } from "../../../common/api/worker/EventBusClient.js"
|
||||
import { assertWorkerOrNode, getWebsocketBaseUrl, isAndroidApp, isBrowser, isIOSApp, isOfflineStorageAvailable } from "../../../common/api/common/Env.js"
|
||||
import { Const } from "../../../common/api/common/TutanotaConstants.js"
|
||||
import type { BrowserData } from "../../../common/misc/ClientConstants.js"
|
||||
import type { CalendarFacade } from "../../../common/api/worker/facades/lazy/CalendarFacade.js"
|
||||
import type { ShareFacade } from "../../../common/api/worker/facades/lazy/ShareFacade.js"
|
||||
import { RestClient } from "../../../common/api/worker/rest/RestClient.js"
|
||||
import { SuspensionHandler } from "../../../common/api/worker/SuspensionHandler.js"
|
||||
import { EntityClient } from "../../../common/api/common/EntityClient.js"
|
||||
import type { GiftCardFacade } from "../../../common/api/worker/facades/lazy/GiftCardFacade.js"
|
||||
import type { ConfigurationDatabase } from "../../../common/api/worker/facades/lazy/ConfigurationDatabase.js"
|
||||
import { DeviceEncryptionFacade } from "../../../common/api/worker/facades/DeviceEncryptionFacade.js"
|
||||
import type { NativeInterface } from "../../../common/native/common/NativeInterface.js"
|
||||
import { NativeFileApp } from "../../../common/native/common/FileApp.js"
|
||||
import { AesApp } from "../../../common/native/worker/AesApp.js"
|
||||
import type { RsaImplementation } from "../../../common/api/worker/crypto/RsaImplementation.js"
|
||||
import { createRsaImplementation } from "../../../common/api/worker/crypto/RsaImplementation.js"
|
||||
import { CryptoFacade } from "../../../common/api/worker/crypto/CryptoFacade.js"
|
||||
import { InstanceMapper } from "../../../common/api/worker/crypto/InstanceMapper.js"
|
||||
import { SleepDetector } from "../../../common/api/worker/utils/SleepDetector.js"
|
||||
import { SchedulerImpl } from "../../../common/api/common/utils/Scheduler.js"
|
||||
import { NoZoneDateProvider } from "../../../common/api/common/utils/NoZoneDateProvider.js"
|
||||
import { LateInitializedCacheStorageImpl } from "../../../common/api/worker/rest/CacheStorageProxy.js"
|
||||
import { IServiceExecutor } from "../../../common/api/common/ServiceRequest.js"
|
||||
import { ServiceExecutor } from "../../../common/api/worker/rest/ServiceExecutor.js"
|
||||
import type { BookingFacade } from "../../../common/api/worker/facades/lazy/BookingFacade.js"
|
||||
import type { BlobFacade } from "../../../common/api/worker/facades/lazy/BlobFacade.js"
|
||||
import { UserFacade } from "../../../common/api/worker/facades/UserFacade.js"
|
||||
import { OfflineStorage } from "../../../common/api/worker/offline/OfflineStorage.js"
|
||||
import { OFFLINE_STORAGE_MIGRATIONS, OfflineStorageMigrator } from "../../../common/api/worker/offline/OfflineStorageMigrator.js"
|
||||
import { modelInfos } from "../../../common/api/common/EntityFunctions.js"
|
||||
import { FileFacadeSendDispatcher } from "../../../common/native/common/generatedipc/FileFacadeSendDispatcher.js"
|
||||
import { NativePushFacadeSendDispatcher } from "../../../common/native/common/generatedipc/NativePushFacadeSendDispatcher.js"
|
||||
import { NativeCryptoFacadeSendDispatcher } from "../../../common/native/common/generatedipc/NativeCryptoFacadeSendDispatcher.js"
|
||||
import { random } from "@tutao/tutanota-crypto"
|
||||
import { ExportFacadeSendDispatcher } from "../../../common/native/common/generatedipc/ExportFacadeSendDispatcher.js"
|
||||
import { lazyAsync, lazyMemoized, noOp } from "@tutao/tutanota-utils"
|
||||
import { InterWindowEventFacadeSendDispatcher } from "../../../common/native/common/generatedipc/InterWindowEventFacadeSendDispatcher.js"
|
||||
import { SqlCipherFacadeSendDispatcher } from "../../../common/native/common/generatedipc/SqlCipherFacadeSendDispatcher.js"
|
||||
import { EntropyFacade } from "../../../common/api/worker/facades/EntropyFacade.js"
|
||||
import { BlobAccessTokenFacade } from "../../../common/api/worker/facades/BlobAccessTokenFacade.js"
|
||||
import { OwnerEncSessionKeysUpdateQueue } from "../../../common/api/worker/crypto/OwnerEncSessionKeysUpdateQueue.js"
|
||||
import { EventBusEventCoordinator } from "../../../common/api/worker/EventBusEventCoordinator.js"
|
||||
import { WorkerFacade } from "../../../common/api/worker/facades/WorkerFacade.js"
|
||||
import { SqlCipherFacade } from "../../../common/native/common/generatedipc/SqlCipherFacade.js"
|
||||
import { Challenge } from "../../../common/api/entities/sys/TypeRefs.js"
|
||||
import { LoginFailReason } from "../../../common/api/main/PageContextLoginListener.js"
|
||||
import { SessionType } from "../../../common/api/common/SessionType.js"
|
||||
import { Argon2idFacade, NativeArgon2idFacade, WASMArgon2idFacade } from "../../../common/api/worker/facades/Argon2idFacade.js"
|
||||
import { DomainConfigProvider } from "../../../common/api/common/DomainConfigProvider.js"
|
||||
import { KyberFacade, NativeKyberFacade, WASMKyberFacade } from "../../../common/api/worker/facades/KyberFacade.js"
|
||||
import { PQFacade } from "../../../common/api/worker/facades/PQFacade.js"
|
||||
import { PdfWriter } from "../../../common/api/worker/pdf/PdfWriter.js"
|
||||
import { ContactFacade } from "../../../common/api/worker/facades/lazy/ContactFacade.js"
|
||||
import { KeyLoaderFacade } from "../../../common/api/worker/facades/KeyLoaderFacade.js"
|
||||
import { KeyRotationFacade } from "../../../common/api/worker/facades/KeyRotationFacade.js"
|
||||
import { KeyCache } from "../../../common/api/worker/facades/KeyCache.js"
|
||||
import { cryptoWrapper } from "../../../common/api/worker/crypto/CryptoWrapper.js"
|
||||
import { RecoverCodeFacade } from "../../../common/api/worker/facades/lazy/RecoverCodeFacade.js"
|
||||
import { CacheManagementFacade } from "../../../common/api/worker/facades/lazy/CacheManagementFacade.js"
|
||||
import { CalendarWorkerImpl } from "./CalendarWorkerImpl.js"
|
||||
import { CalendarOfflineCleaner } from "../../offline/CalendarOfflineCleaner.js"
|
||||
import type { QueuedBatch } from "../../../common/api/worker/EventQueue.js"
|
||||
import { Credentials } from "../../../common/misc/credentials/Credentials.js"
|
||||
|
||||
assertWorkerOrNode()
|
||||
|
||||
export type CalendarWorkerLocatorType = {
|
||||
// network & encryption
|
||||
restClient: RestClient
|
||||
serviceExecutor: IServiceExecutor
|
||||
crypto: CryptoFacade
|
||||
instanceMapper: InstanceMapper
|
||||
cacheStorage: CacheStorage
|
||||
cache: EntityRestInterface
|
||||
cachingEntityClient: EntityClient
|
||||
eventBusClient: EventBusClient
|
||||
rsa: RsaImplementation
|
||||
kyberFacade: KyberFacade
|
||||
pqFacade: PQFacade
|
||||
entropyFacade: EntropyFacade
|
||||
blobAccessToken: BlobAccessTokenFacade
|
||||
keyCache: KeyCache
|
||||
keyLoader: KeyLoaderFacade
|
||||
keyRotation: KeyRotationFacade
|
||||
|
||||
// login
|
||||
user: UserFacade
|
||||
login: LoginFacade
|
||||
|
||||
// domains
|
||||
blob: lazyAsync<BlobFacade>
|
||||
mail: lazyAsync<MailFacade>
|
||||
calendar: lazyAsync<CalendarFacade>
|
||||
counters: lazyAsync<CounterFacade>
|
||||
Const: Record<string, any>
|
||||
|
||||
// management facades
|
||||
groupManagement: lazyAsync<GroupManagementFacade>
|
||||
userManagement: lazyAsync<UserManagementFacade>
|
||||
recoverCode: lazyAsync<RecoverCodeFacade>
|
||||
customer: lazyAsync<CustomerFacade>
|
||||
giftCards: lazyAsync<GiftCardFacade>
|
||||
mailAddress: lazyAsync<MailAddressFacade>
|
||||
booking: lazyAsync<BookingFacade>
|
||||
share: lazyAsync<ShareFacade>
|
||||
cacheManagement: lazyAsync<CacheManagementFacade>
|
||||
|
||||
// misc & native
|
||||
configFacade: lazyAsync<ConfigurationDatabase>
|
||||
deviceEncryptionFacade: DeviceEncryptionFacade
|
||||
native: NativeInterface
|
||||
workerFacade: WorkerFacade
|
||||
sqlCipherFacade: SqlCipherFacade
|
||||
pdfWriter: lazyAsync<PdfWriter>
|
||||
|
||||
// used to cache between resets
|
||||
_worker: CalendarWorkerImpl
|
||||
_browserData: BrowserData
|
||||
|
||||
//contact
|
||||
contactFacade: lazyAsync<ContactFacade>
|
||||
}
|
||||
export const locator: CalendarWorkerLocatorType = {} as any
|
||||
|
||||
export async function initLocator(worker: CalendarWorkerImpl, browserData: BrowserData) {
|
||||
locator._worker = worker
|
||||
locator._browserData = browserData
|
||||
locator.keyCache = new KeyCache()
|
||||
locator.user = new UserFacade(locator.keyCache)
|
||||
locator.workerFacade = new WorkerFacade()
|
||||
const dateProvider = new NoZoneDateProvider()
|
||||
|
||||
const mainInterface = worker.getMainInterface()
|
||||
|
||||
const suspensionHandler = new SuspensionHandler(mainInterface.infoMessageHandler, self)
|
||||
locator.instanceMapper = new InstanceMapper()
|
||||
locator.rsa = await createRsaImplementation(worker)
|
||||
|
||||
const domainConfig = new DomainConfigProvider().getCurrentDomainConfig()
|
||||
|
||||
locator.restClient = new RestClient(suspensionHandler, domainConfig)
|
||||
locator.serviceExecutor = new ServiceExecutor(locator.restClient, locator.user, locator.instanceMapper, () => locator.crypto)
|
||||
locator.entropyFacade = new EntropyFacade(locator.user, locator.serviceExecutor, random, () => locator.keyLoader)
|
||||
locator.blobAccessToken = new BlobAccessTokenFacade(locator.serviceExecutor, dateProvider, locator.user)
|
||||
const entityRestClient = new EntityRestClient(locator.user, locator.restClient, () => locator.crypto, locator.instanceMapper, locator.blobAccessToken)
|
||||
|
||||
locator.native = worker
|
||||
locator.booking = lazyMemoized(async () => {
|
||||
const { BookingFacade } = await import("../../../common/api/worker/facades/lazy/BookingFacade.js")
|
||||
return new BookingFacade(locator.serviceExecutor)
|
||||
})
|
||||
|
||||
let offlineStorageProvider
|
||||
if (isOfflineStorageAvailable()) {
|
||||
locator.sqlCipherFacade = new SqlCipherFacadeSendDispatcher(locator.native)
|
||||
offlineStorageProvider = async () => {
|
||||
return new OfflineStorage(
|
||||
locator.sqlCipherFacade,
|
||||
new InterWindowEventFacadeSendDispatcher(worker),
|
||||
dateProvider,
|
||||
new OfflineStorageMigrator(OFFLINE_STORAGE_MIGRATIONS, modelInfos),
|
||||
new CalendarOfflineCleaner(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
offlineStorageProvider = async () => null
|
||||
}
|
||||
locator.pdfWriter = async () => {
|
||||
const { PdfWriter } = await import("../../../common/api/worker/pdf/PdfWriter.js")
|
||||
return new PdfWriter(new TextEncoder(), undefined)
|
||||
}
|
||||
|
||||
const maybeUninitializedStorage = new LateInitializedCacheStorageImpl(async (error: Error) => {
|
||||
await worker.sendError(error)
|
||||
}, offlineStorageProvider)
|
||||
|
||||
locator.cacheStorage = maybeUninitializedStorage
|
||||
|
||||
const fileApp = new NativeFileApp(new FileFacadeSendDispatcher(worker), new ExportFacadeSendDispatcher(worker))
|
||||
|
||||
locator.cache = new DefaultEntityRestCache(entityRestClient, maybeUninitializedStorage)
|
||||
|
||||
locator.cachingEntityClient = new EntityClient(locator.cache)
|
||||
const nonCachingEntityClient = new EntityClient(entityRestClient)
|
||||
|
||||
locator.cacheManagement = lazyMemoized(async () => {
|
||||
const { CacheManagementFacade } = await import("../../../common/api/worker/facades/lazy/CacheManagementFacade.js")
|
||||
return new CacheManagementFacade(locator.user, locator.cachingEntityClient, locator.cache as DefaultEntityRestCache)
|
||||
})
|
||||
|
||||
if (isIOSApp() || isAndroidApp()) {
|
||||
locator.kyberFacade = new NativeKyberFacade(new NativeCryptoFacadeSendDispatcher(worker))
|
||||
} else {
|
||||
locator.kyberFacade = new WASMKyberFacade()
|
||||
}
|
||||
|
||||
locator.pqFacade = new PQFacade(locator.kyberFacade)
|
||||
|
||||
locator.keyLoader = new KeyLoaderFacade(locator.keyCache, locator.user, locator.cachingEntityClient, locator.cacheManagement)
|
||||
|
||||
locator.crypto = new CryptoFacade(
|
||||
locator.user,
|
||||
locator.cachingEntityClient,
|
||||
locator.restClient,
|
||||
locator.rsa,
|
||||
locator.serviceExecutor,
|
||||
locator.instanceMapper,
|
||||
new OwnerEncSessionKeysUpdateQueue(locator.user, locator.serviceExecutor),
|
||||
locator.pqFacade,
|
||||
locator.cache as DefaultEntityRestCache,
|
||||
locator.keyLoader,
|
||||
)
|
||||
|
||||
locator.recoverCode = lazyMemoized(async () => {
|
||||
const { RecoverCodeFacade } = await import("../../../common/api/worker/facades/lazy/RecoverCodeFacade.js")
|
||||
return new RecoverCodeFacade(locator.user, locator.cachingEntityClient, locator.login, locator.keyLoader)
|
||||
})
|
||||
locator.share = lazyMemoized(async () => {
|
||||
const { ShareFacade } = await import("../../../common/api/worker/facades/lazy/ShareFacade.js")
|
||||
return new ShareFacade(locator.user, locator.crypto, locator.serviceExecutor, locator.cachingEntityClient, locator.keyLoader)
|
||||
})
|
||||
locator.counters = lazyMemoized(async () => {
|
||||
const { CounterFacade } = await import("../../../common/api/worker/facades/lazy/CounterFacade.js")
|
||||
return new CounterFacade(locator.serviceExecutor)
|
||||
})
|
||||
locator.groupManagement = lazyMemoized(async () => {
|
||||
const { GroupManagementFacade } = await import("../../../common/api/worker/facades/lazy/GroupManagementFacade.js")
|
||||
return new GroupManagementFacade(
|
||||
locator.user,
|
||||
await locator.counters(),
|
||||
locator.cachingEntityClient,
|
||||
locator.serviceExecutor,
|
||||
locator.pqFacade,
|
||||
locator.keyLoader,
|
||||
await locator.cacheManagement(),
|
||||
)
|
||||
})
|
||||
locator.keyRotation = new KeyRotationFacade(
|
||||
locator.cachingEntityClient,
|
||||
locator.keyLoader,
|
||||
locator.pqFacade,
|
||||
locator.serviceExecutor,
|
||||
cryptoWrapper,
|
||||
locator.recoverCode,
|
||||
locator.user,
|
||||
locator.crypto,
|
||||
locator.share,
|
||||
locator.groupManagement,
|
||||
)
|
||||
|
||||
const loginListener: LoginListener = {
|
||||
onFullLoginSuccess(sessionType: SessionType, cacheInfo: CacheInfo, credentials: Credentials): Promise<void> {
|
||||
return mainInterface.loginListener.onFullLoginSuccess(sessionType, cacheInfo, credentials)
|
||||
},
|
||||
|
||||
onLoginFailure(reason: LoginFailReason): Promise<void> {
|
||||
return mainInterface.loginListener.onLoginFailure(reason)
|
||||
},
|
||||
|
||||
onSecondFactorChallenge(sessionId: IdTuple, challenges: ReadonlyArray<Challenge>, mailAddress: string | null): Promise<void> {
|
||||
return mainInterface.loginListener.onSecondFactorChallenge(sessionId, challenges, mailAddress)
|
||||
},
|
||||
}
|
||||
|
||||
let argon2idFacade: Argon2idFacade
|
||||
if (!isBrowser()) {
|
||||
argon2idFacade = new NativeArgon2idFacade(new NativeCryptoFacadeSendDispatcher(worker))
|
||||
} else {
|
||||
argon2idFacade = new WASMArgon2idFacade()
|
||||
}
|
||||
|
||||
locator.deviceEncryptionFacade = new DeviceEncryptionFacade()
|
||||
const { DatabaseKeyFactory } = await import("../../../common/misc/credentials/DatabaseKeyFactory.js")
|
||||
|
||||
locator.login = new LoginFacade(
|
||||
locator.restClient,
|
||||
/**
|
||||
* we don't want to try to use the cache in the login facade, because it may not be available (when no user is logged in)
|
||||
*/
|
||||
new EntityClient(locator.cache),
|
||||
loginListener,
|
||||
locator.instanceMapper,
|
||||
locator.crypto,
|
||||
locator.keyRotation,
|
||||
maybeUninitializedStorage,
|
||||
locator.serviceExecutor,
|
||||
locator.user,
|
||||
locator.blobAccessToken,
|
||||
locator.entropyFacade,
|
||||
new DatabaseKeyFactory(locator.deviceEncryptionFacade),
|
||||
argon2idFacade,
|
||||
nonCachingEntityClient,
|
||||
async (error: Error) => {
|
||||
await worker.sendError(error)
|
||||
},
|
||||
)
|
||||
|
||||
locator.userManagement = lazyMemoized(async () => {
|
||||
const { UserManagementFacade } = await import("../../../common/api/worker/facades/lazy/UserManagementFacade.js")
|
||||
return new UserManagementFacade(
|
||||
locator.user,
|
||||
await locator.groupManagement(),
|
||||
await locator.counters(),
|
||||
locator.cachingEntityClient,
|
||||
locator.serviceExecutor,
|
||||
mainInterface.operationProgressTracker,
|
||||
locator.login,
|
||||
locator.pqFacade,
|
||||
locator.keyLoader,
|
||||
await locator.recoverCode(),
|
||||
)
|
||||
})
|
||||
locator.customer = lazyMemoized(async () => {
|
||||
const { CustomerFacade } = await import("../../../common/api/worker/facades/lazy/CustomerFacade.js")
|
||||
return new CustomerFacade(
|
||||
locator.user,
|
||||
await locator.groupManagement(),
|
||||
await locator.userManagement(),
|
||||
await locator.counters(),
|
||||
locator.rsa,
|
||||
locator.cachingEntityClient,
|
||||
locator.serviceExecutor,
|
||||
await locator.booking(),
|
||||
locator.crypto,
|
||||
mainInterface.operationProgressTracker,
|
||||
locator.pdfWriter,
|
||||
locator.pqFacade,
|
||||
locator.keyLoader,
|
||||
await locator.recoverCode(),
|
||||
)
|
||||
})
|
||||
const aesApp = new AesApp(new NativeCryptoFacadeSendDispatcher(worker), random)
|
||||
locator.blob = lazyMemoized(async () => {
|
||||
const { BlobFacade } = await import("../../../common/api/worker/facades/lazy/BlobFacade.js")
|
||||
return new BlobFacade(
|
||||
locator.user,
|
||||
locator.serviceExecutor,
|
||||
locator.restClient,
|
||||
suspensionHandler,
|
||||
fileApp,
|
||||
aesApp,
|
||||
locator.instanceMapper,
|
||||
locator.crypto,
|
||||
locator.blobAccessToken,
|
||||
locator.cache as DefaultEntityRestCache,
|
||||
)
|
||||
})
|
||||
locator.mail = lazyMemoized(async () => {
|
||||
const { MailFacade } = await import("../../../common/api/worker/facades/lazy/MailFacade.js")
|
||||
return new MailFacade(
|
||||
locator.user,
|
||||
locator.cachingEntityClient,
|
||||
locator.crypto,
|
||||
locator.serviceExecutor,
|
||||
await locator.blob(),
|
||||
fileApp,
|
||||
locator.login,
|
||||
locator.keyLoader,
|
||||
)
|
||||
})
|
||||
const nativePushFacade = new NativePushFacadeSendDispatcher(worker)
|
||||
locator.calendar = lazyMemoized(async () => {
|
||||
const { CalendarFacade } = await import("../../../common/api/worker/facades/lazy/CalendarFacade.js")
|
||||
return new CalendarFacade(
|
||||
locator.user,
|
||||
await locator.groupManagement(),
|
||||
locator.cache as DefaultEntityRestCache,
|
||||
nonCachingEntityClient, // without cache
|
||||
nativePushFacade,
|
||||
mainInterface.operationProgressTracker,
|
||||
locator.instanceMapper,
|
||||
locator.serviceExecutor,
|
||||
locator.crypto,
|
||||
mainInterface.infoMessageHandler,
|
||||
)
|
||||
})
|
||||
|
||||
locator.mailAddress = lazyMemoized(async () => {
|
||||
const { MailAddressFacade } = await import("../../../common/api/worker/facades/lazy/MailAddressFacade.js")
|
||||
return new MailAddressFacade(
|
||||
locator.user,
|
||||
await locator.groupManagement(),
|
||||
locator.serviceExecutor,
|
||||
nonCachingEntityClient, // without cache
|
||||
)
|
||||
})
|
||||
const scheduler = new SchedulerImpl(dateProvider, self, self)
|
||||
|
||||
locator.configFacade = lazyMemoized(async () => {
|
||||
const { ConfigurationDatabase } = await import("../../../common/api/worker/facades/lazy/ConfigurationDatabase.js")
|
||||
return new ConfigurationDatabase(locator.keyLoader, locator.user)
|
||||
})
|
||||
|
||||
const eventBusCoordinator = new EventBusEventCoordinator(
|
||||
mainInterface.wsConnectivityListener,
|
||||
locator.mail,
|
||||
locator.user,
|
||||
locator.cachingEntityClient,
|
||||
mainInterface.eventController,
|
||||
locator.configFacade,
|
||||
locator.keyRotation,
|
||||
locator.cacheManagement,
|
||||
async (error: Error) => {
|
||||
await worker.sendError(error)
|
||||
},
|
||||
(queuedBatch: QueuedBatch[]) => noOp,
|
||||
)
|
||||
|
||||
locator.eventBusClient = new EventBusClient(
|
||||
eventBusCoordinator,
|
||||
locator.cache as EntityRestCache,
|
||||
locator.user,
|
||||
locator.cachingEntityClient,
|
||||
locator.instanceMapper,
|
||||
(path) => new WebSocket(getWebsocketBaseUrl(domainConfig) + path),
|
||||
new SleepDetector(scheduler, dateProvider),
|
||||
mainInterface.progressTracker,
|
||||
)
|
||||
locator.login.init(locator.eventBusClient)
|
||||
locator.Const = Const
|
||||
locator.giftCards = lazyMemoized(async () => {
|
||||
const { GiftCardFacade } = await import("../../../common/api/worker/facades/lazy/GiftCardFacade.js")
|
||||
return new GiftCardFacade(locator.user, await locator.customer(), locator.serviceExecutor, locator.crypto, locator.keyLoader)
|
||||
})
|
||||
locator.contactFacade = lazyMemoized(async () => {
|
||||
const { ContactFacade } = await import("../../../common/api/worker/facades/lazy/ContactFacade.js")
|
||||
return new ContactFacade(new EntityClient(locator.cache))
|
||||
})
|
||||
}
|
||||
|
||||
export async function resetLocator(): Promise<void> {
|
||||
await locator.login.resetSession()
|
||||
await initLocator(locator._worker, locator._browserData)
|
||||
}
|
||||
|
||||
if (typeof self !== "undefined") {
|
||||
;(self as unknown as WorkerGlobalScope).locator = locator // export in worker scope
|
||||
}
|
||||
|
||||
/*
|
||||
* @returns true if webassembly is supported
|
||||
*/
|
||||
export function isWebAssemblySupported() {
|
||||
return typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function"
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import { WorkerImpl } from "../common/api/worker/WorkerImpl"
|
||||
import { Logger, replaceNativeLogger } from "../common/api/common/Logger.js"
|
||||
import { CalendarOfflineCleaner } from "./offline/CalendarOfflineCleaner.js"
|
||||
import { Logger, replaceNativeLogger } from "../../../common/api/common/Logger.js"
|
||||
import { CalendarWorkerImpl } from "./CalendarWorkerImpl.js"
|
||||
|
||||
/**
|
||||
* Receives the first message from the client and initializes the WorkerImpl to receive all future messages. Sends a response to the client on this first message.
|
||||
|
@ -21,8 +20,8 @@ self.onmessage = function (msg) {
|
|||
}
|
||||
|
||||
// @ts-ignore
|
||||
const workerImpl = new WorkerImpl(typeof self !== "undefined" ? self : null)
|
||||
await workerImpl.init(browserData, new CalendarOfflineCleaner())
|
||||
const workerImpl = new CalendarWorkerImpl(typeof self !== "undefined" ? self : null)
|
||||
await workerImpl.init(browserData)
|
||||
workerImpl.exposedInterface.entropyFacade().then((entropyFacade) => entropyFacade.addEntropy(initialRandomizerEntropy))
|
||||
self.postMessage({
|
||||
id: data.id,
|
|
@ -8,7 +8,6 @@ import { SystemPermissionHandler } from "../../native/main/SystemPermissionHandl
|
|||
import { SecondFactorHandler } from "../../misc/2fa/SecondFactorHandler.js"
|
||||
import { PageContextLoginListener } from "./PageContextLoginListener.js"
|
||||
import { NewsModel } from "../../misc/news/NewsModel.js"
|
||||
import { SearchModel } from "../../../mail-app/search/model/SearchModel.js"
|
||||
import { InfoMessageHandler } from "../../gui/InfoMessageHandler.js"
|
||||
import { SettingsFacade } from "../../native/common/generatedipc/SettingsFacade.js"
|
||||
import { DesktopSystemFacade } from "../../native/common/generatedipc/DesktopSystemFacade.js"
|
||||
|
@ -22,8 +21,6 @@ import type { CalendarFacade } from "../worker/facades/lazy/CalendarFacade.js"
|
|||
import type { MailFacade } from "../worker/facades/lazy/MailFacade.js"
|
||||
import type { ShareFacade } from "../worker/facades/lazy/ShareFacade.js"
|
||||
import type { CounterFacade } from "../worker/facades/lazy/CounterFacade.js"
|
||||
import type { Indexer } from "../worker/search/Indexer.js"
|
||||
import type { SearchFacade } from "../worker/search/SearchFacade.js"
|
||||
import type { BookingFacade } from "../worker/facades/lazy/BookingFacade.js"
|
||||
import type { MailAddressFacade } from "../worker/facades/lazy/MailAddressFacade.js"
|
||||
import type { BlobFacade } from "../worker/facades/lazy/BlobFacade.js"
|
||||
|
@ -34,7 +31,6 @@ import { IServiceExecutor } from "../common/ServiceRequest.js"
|
|||
import { CryptoFacade } from "../worker/crypto/CryptoFacade.js"
|
||||
import { ExposedCacheStorage } from "../worker/rest/DefaultEntityRestCache.js"
|
||||
import { WorkerFacade } from "../worker/facades/WorkerFacade.js"
|
||||
import { WorkerRandomizer } from "../worker/WorkerImpl.js"
|
||||
import { WebsocketConnectivityModel } from "../../misc/WebsocketConnectivityModel.js"
|
||||
import type { MailboxDetail, MailboxModel } from "../../mailFunctionality/MailboxModel.js"
|
||||
import { EventController } from "./EventController.js"
|
||||
|
@ -51,8 +47,6 @@ import { MailAddressTableModel } from "../../settings/mailaddress/MailAddressTab
|
|||
import { GroupInfo } from "../entities/sys/TypeRefs.js"
|
||||
import { lazy } from "@tutao/tutanota-utils"
|
||||
import { Router } from "../../gui/ScopedRouter.js"
|
||||
import type { IMailLocator } from "../../../mail-app/mailLocator.js"
|
||||
import type { ICalendarLocator } from "../../../calendar-app/calendarLocator.js"
|
||||
import { NativeInterfaceMain } from "../../native/main/NativeInterfaceMain.js"
|
||||
import { CommonSystemFacade } from "../../native/common/generatedipc/CommonSystemFacade.js"
|
||||
import { ThemeFacade } from "../../native/common/generatedipc/ThemeFacade.js"
|
||||
|
@ -69,9 +63,10 @@ import { CalendarEventModel, CalendarOperation } from "../../../calendar-app/cal
|
|||
import { CalendarEventPreviewViewModel } from "../../../calendar-app/calendar/gui/eventpopup/CalendarEventPreviewViewModel.js"
|
||||
import { RecipientsModel } from "./RecipientsModel.js"
|
||||
import { ThemeController } from "../../gui/ThemeController.js"
|
||||
import { CalendarSearchModel } from "../../../calendar-app/calendar/search/model/CalendarSearchModel.js"
|
||||
import { MobilePaymentsFacade } from "../../native/common/generatedipc/MobilePaymentsFacade.js"
|
||||
import { AppStorePaymentPicker } from "../../misc/AppStorePaymentPicker.js"
|
||||
import { WorkerRandomizer } from "../worker/workerInterfaces.js"
|
||||
import { CommonSearchModel } from "../../search/CommonSearchModel.js"
|
||||
|
||||
export interface CommonLocator {
|
||||
worker: WorkerClient
|
||||
|
@ -84,7 +79,7 @@ export interface CommonLocator {
|
|||
secondFactorHandler: SecondFactorHandler
|
||||
loginListener: PageContextLoginListener
|
||||
newsModel: NewsModel
|
||||
search: SearchModel | CalendarSearchModel
|
||||
search: CommonSearchModel
|
||||
infoMessageHandler: InfoMessageHandler
|
||||
desktopSettingsFacade: SettingsFacade
|
||||
desktopSystemFacade: DesktopSystemFacade
|
||||
|
@ -100,8 +95,6 @@ export interface CommonLocator {
|
|||
mailFacade: MailFacade
|
||||
shareFacade: ShareFacade
|
||||
counterFacade: CounterFacade
|
||||
indexerFacade: Indexer
|
||||
searchFacade: SearchFacade
|
||||
bookingFacade: BookingFacade
|
||||
mailAddressFacade: MailAddressFacade
|
||||
blobFacade: BlobFacade
|
||||
|
@ -177,6 +170,6 @@ export let locator: CommonLocator = new Proxy<CommonLocator>({} as unknown as Co
|
|||
},
|
||||
})
|
||||
|
||||
export function initCommonLocator(loc: IMailLocator | ICalendarLocator) {
|
||||
export function initCommonLocator(loc: CommonLocator) {
|
||||
locator = loc
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import { PartialRecipient, Recipient, RecipientType } from "../common/recipients
|
|||
import { BoundedExecutor, LazyLoaded } from "@tutao/tutanota-utils"
|
||||
import { Contact, ContactTypeRef } from "../entities/tutanota/TypeRefs"
|
||||
import { cleanMailAddress } from "../common/utils/CommonCalendarUtils.js"
|
||||
import { createNewContact, isTutanotaMailAddress } from "../../mailFunctionality/SharedMailUtils.js"
|
||||
import { createNewContact, isTutaMailAddress } from "../../mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
/**
|
||||
* A recipient that can be resolved to obtain contact and recipient type
|
||||
|
@ -102,7 +102,7 @@ class ResolvableRecipientImpl implements ResolvableRecipient {
|
|||
private readonly entityClient: EntityClient,
|
||||
resolveMode: ResolveMode,
|
||||
) {
|
||||
if (isTutanotaMailAddress(arg.address) || arg.type === RecipientType.INTERNAL) {
|
||||
if (isTutaMailAddress(arg.address) || arg.type === RecipientType.INTERNAL) {
|
||||
this.initialType = RecipientType.INTERNAL
|
||||
this._address = cleanMailAddress(arg.address)
|
||||
} else if (arg.type) {
|
||||
|
|
|
@ -6,12 +6,12 @@ import { client } from "../../misc/ClientDetector"
|
|||
import type { DeferredObject } from "@tutao/tutanota-utils"
|
||||
import { defer, downcast } from "@tutao/tutanota-utils"
|
||||
import { handleUncaughtError } from "../../misc/ErrorHandler"
|
||||
import type { MainInterface, WorkerInterface } from "../worker/WorkerImpl"
|
||||
import { DelayedImpls, exposeLocalDelayed, exposeRemote } from "../common/WorkerProxy"
|
||||
import type { RestClient } from "../worker/rest/RestClient"
|
||||
import { EntropyDataChunk } from "../worker/facades/EntropyFacade.js"
|
||||
import { objToError } from "../common/utils/ErrorUtils.js"
|
||||
import { CommonLocator } from "./CommonLocator.js"
|
||||
import { CommonWorkerInterface, MainInterface } from "../worker/workerInterfaces.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
@ -109,8 +109,8 @@ export class WorkerClient {
|
|||
}
|
||||
}
|
||||
|
||||
getWorkerInterface(): WorkerInterface {
|
||||
return exposeRemote<WorkerInterface>(async (request) => this._postRequest(request))
|
||||
getWorkerInterface(): CommonWorkerInterface {
|
||||
return exposeRemote<CommonWorkerInterface>(async (request) => this._postRequest(request))
|
||||
}
|
||||
|
||||
restRequest(...args: Parameters<RestClient["request"]>): Promise<any | null> {
|
||||
|
|
|
@ -10,10 +10,8 @@ import {
|
|||
} from "../entities/sys/TypeRefs.js"
|
||||
import { ReportedMailFieldMarker } from "../entities/tutanota/TypeRefs.js"
|
||||
import { WebsocketConnectivityListener } from "../../misc/WebsocketConnectivityModel.js"
|
||||
import { WorkerImpl } from "./WorkerImpl.js"
|
||||
import { isAdminClient, isTest } from "../common/Env.js"
|
||||
import { MailFacade } from "./facades/lazy/MailFacade.js"
|
||||
import type { Indexer } from "./search/Indexer.js"
|
||||
import { UserFacade } from "./facades/UserFacade.js"
|
||||
import { EntityClient } from "../common/EntityClient.js"
|
||||
import { AccountType, OperationType } from "../common/TutanotaConstants.js"
|
||||
|
@ -23,20 +21,21 @@ import { ExposedEventController } from "../main/EventController.js"
|
|||
import { ConfigurationDatabase } from "./facades/lazy/ConfigurationDatabase.js"
|
||||
import { KeyRotationFacade } from "./facades/KeyRotationFacade.js"
|
||||
import { CacheManagementFacade } from "./facades/lazy/CacheManagementFacade.js"
|
||||
import type { QueuedBatch } from "./EventQueue.js"
|
||||
|
||||
/** A bit of glue to distribute event bus events across the app. */
|
||||
export class EventBusEventCoordinator implements EventBusListener {
|
||||
constructor(
|
||||
private readonly worker: WorkerImpl,
|
||||
private readonly connectivityListener: WebsocketConnectivityListener,
|
||||
private readonly mailFacade: lazyAsync<MailFacade>,
|
||||
private readonly indexer: lazyAsync<Indexer>,
|
||||
private readonly userFacade: UserFacade,
|
||||
private readonly entityClient: EntityClient,
|
||||
private readonly eventController: ExposedEventController,
|
||||
private readonly configurationDatabase: lazyAsync<ConfigurationDatabase>,
|
||||
private readonly keyRotationFacade: KeyRotationFacade,
|
||||
private readonly cacheManagementFacade: lazyAsync<CacheManagementFacade>,
|
||||
private readonly sendError: (error: Error) => Promise<void>,
|
||||
private readonly appSpecificBatchHandling: (queuedBatch: QueuedBatch[]) => void,
|
||||
) {}
|
||||
|
||||
onWebsocketStateChanged(state: WsConnectionState) {
|
||||
|
@ -53,9 +52,7 @@ export class EventBusEventCoordinator implements EventBusListener {
|
|||
const queuedBatch = { groupId, batchId, events }
|
||||
const configurationDatabase = await this.configurationDatabase()
|
||||
await configurationDatabase.onEntityEventsReceived(queuedBatch)
|
||||
const indexer = await this.indexer()
|
||||
indexer.addBatchesToQueue([queuedBatch])
|
||||
indexer.startProcessing()
|
||||
this.appSpecificBatchHandling([queuedBatch])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +64,7 @@ export class EventBusEventCoordinator implements EventBusListener {
|
|||
}
|
||||
|
||||
onError(tutanotaError: Error) {
|
||||
this.worker.sendError(tutanotaError)
|
||||
this.sendError(tutanotaError)
|
||||
}
|
||||
|
||||
onLeaderStatusChanged(leaderStatus: WebsocketLeaderStatus) {
|
||||
|
|
|
@ -54,7 +54,6 @@ import { assertWorkerOrNode, isAdminClient } from "../../common/Env"
|
|||
import { ConnectMode, EventBusClient } from "../EventBusClient"
|
||||
import { EntityRestClient, typeRefToPath } from "../rest/EntityRestClient"
|
||||
import { AccessExpiredError, ConnectionError, LockedError, NotAuthenticatedError, NotFoundError, SessionExpiredError } from "../../common/error/RestError"
|
||||
import type { WorkerImpl } from "../WorkerImpl"
|
||||
import { CancelledError } from "../../common/error/CancelledError"
|
||||
import { RestClient } from "../rest/RestClient"
|
||||
import { EntityClient } from "../../common/EntityClient"
|
||||
|
@ -179,7 +178,6 @@ export class LoginFacade {
|
|||
asyncLoginState: AsyncLoginState = { state: "idle" }
|
||||
|
||||
constructor(
|
||||
readonly worker: WorkerImpl,
|
||||
private readonly restClient: RestClient,
|
||||
private readonly entityClient: EntityClient,
|
||||
private readonly loginListener: LoginListener,
|
||||
|
@ -199,6 +197,7 @@ export class LoginFacade {
|
|||
private readonly databaseKeyFactory: DatabaseKeyFactory,
|
||||
private readonly argon2idFacade: Argon2idFacade,
|
||||
private readonly noncachingEntityClient: EntityClient,
|
||||
private readonly sendError: (error: Error) => Promise<void>,
|
||||
) {}
|
||||
|
||||
init(eventBusClient: EventBusClient) {
|
||||
|
@ -417,7 +416,6 @@ export class LoginFacade {
|
|||
throw new Error("user already logged in")
|
||||
}
|
||||
|
||||
console.log("login external worker")
|
||||
const userPassphraseKey = await this.deriveUserPassphraseKey({ kdfType, passphrase, salt })
|
||||
// the verifier is always sent as url parameter, so it must be url encoded
|
||||
const authVerifier = createAuthVerifierAsBase64Url(userPassphraseKey)
|
||||
|
@ -629,7 +627,7 @@ export class LoginFacade {
|
|||
await this.loginListener.onLoginFailure(LoginFailReason.SessionExpired)
|
||||
} else {
|
||||
this.asyncLoginState = { state: "failed", credentials, cacheInfo }
|
||||
if (!(e instanceof ConnectionError)) await this.worker.sendError(e)
|
||||
if (!(e instanceof ConnectionError)) await this.sendError(e)
|
||||
await this.loginListener.onLoginFailure(LoginFailReason.Error)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import { ElementEntity, ListElementEntity, SomeEntity, TypeModel } from "../../common/EntityTypes.js"
|
||||
import {
|
||||
constructMailSetEntryId,
|
||||
CUSTOM_MIN_ID,
|
||||
DEFAULT_MAILSET_ENTRY_CUSTOM_CUTOFF_TIMESTAMP,
|
||||
elementIdPart,
|
||||
firstBiggerThanSecond,
|
||||
GENERATED_MIN_ID,
|
||||
getElementId,
|
||||
listIdPart,
|
||||
timestampToGeneratedId,
|
||||
} from "../../common/utils/EntityUtils.js"
|
||||
import { CacheStorage, expandId, ExposedCacheStorage, LastUpdateTime } from "../rest/DefaultEntityRestCache.js"
|
||||
import * as cborg from "cborg"
|
||||
|
@ -20,9 +15,7 @@ import {
|
|||
base64ToBase64Ext,
|
||||
base64ToBase64Url,
|
||||
base64UrlToBase64,
|
||||
DAY_IN_MILLIS,
|
||||
getTypeId,
|
||||
groupByAndMap,
|
||||
groupByAndMapUniquely,
|
||||
mapNullable,
|
||||
splitInChunks,
|
||||
|
@ -30,20 +23,11 @@ import {
|
|||
} from "@tutao/tutanota-utils"
|
||||
import { isDesktop, isOfflineStorageAvailable, isTest } from "../../common/Env.js"
|
||||
import { modelInfos, resolveTypeReference } from "../../common/EntityFunctions.js"
|
||||
import { AccountType, OFFLINE_STORAGE_DEFAULT_TIME_RANGE_DAYS } from "../../common/TutanotaConstants.js"
|
||||
import { DateProvider } from "../../common/DateProvider.js"
|
||||
import { TokenOrNestedTokens } from "cborg/interface"
|
||||
import {
|
||||
CalendarEventTypeRef,
|
||||
FileTypeRef,
|
||||
MailBoxTypeRef,
|
||||
MailDetailsBlobTypeRef,
|
||||
MailDetailsDraftTypeRef,
|
||||
MailFolderTypeRef,
|
||||
MailSetEntryTypeRef,
|
||||
MailTypeRef,
|
||||
} from "../../entities/tutanota/TypeRefs.js"
|
||||
import { UserTypeRef } from "../../entities/sys/TypeRefs.js"
|
||||
import { OfflineStorageMigrator } from "./OfflineStorageMigrator.js"
|
||||
import { CustomCacheHandlerMap, CustomCalendarEventCacheHandler } from "../rest/CustomCacheHandler.js"
|
||||
import { EntityRestClient } from "../rest/EntityRestClient.js"
|
||||
|
@ -53,8 +37,6 @@ import { FormattedQuery, SqlValue, TaggedSqlValue, untagSqlObject } from "./SqlV
|
|||
import { AssociationType, Cardinality, Type as TypeId, ValueType } from "../../common/EntityConstants.js"
|
||||
import { OutOfSyncError } from "../../common/error/OutOfSyncError.js"
|
||||
import { sql, SqlFragment } from "./Sql.js"
|
||||
import { isDraft, isSpamOrTrashFolder } from "../../../../mail-app/mail/model/MailUtils.js"
|
||||
import { FolderSystem } from "../../common/mail/FolderSystem.js"
|
||||
|
||||
/**
|
||||
* this is the value of SQLITE_MAX_VARIABLE_NUMBER in sqlite3.c
|
||||
|
|
|
@ -3,7 +3,6 @@ import { ProgrammingError } from "../../common/error/ProgrammingError"
|
|||
import { ListElementEntity, SomeEntity } from "../../common/EntityTypes"
|
||||
import { TypeRef } from "@tutao/tutanota-utils"
|
||||
import { OfflineStorage, OfflineStorageInitArgs } from "../offline/OfflineStorage.js"
|
||||
import { WorkerImpl } from "../WorkerImpl"
|
||||
import { EphemeralCacheStorage, EphemeralStorageInitArgs } from "./EphemeralCacheStorage"
|
||||
import { EntityRestClient } from "./EntityRestClient.js"
|
||||
import { CustomCacheHandlerMap } from "./CustomCacheHandler.js"
|
||||
|
@ -46,7 +45,7 @@ type SomeStorage = OfflineStorage | EphemeralCacheStorage
|
|||
export class LateInitializedCacheStorageImpl implements CacheStorageLateInitializer, CacheStorage {
|
||||
private _inner: SomeStorage | null = null
|
||||
|
||||
constructor(private readonly worker: WorkerImpl, private readonly offlineStorageProvider: () => Promise<null | OfflineStorage>) {}
|
||||
constructor(private readonly sendError: (error: Error) => Promise<void>, private readonly offlineStorageProvider: () => Promise<null | OfflineStorage>) {}
|
||||
|
||||
private get inner(): CacheStorage {
|
||||
if (this._inner == null) {
|
||||
|
@ -88,7 +87,7 @@ export class LateInitializedCacheStorageImpl implements CacheStorageLateInitiali
|
|||
} catch (e) {
|
||||
// Precaution in case something bad happens to offline database. We want users to still be able to log in.
|
||||
console.error("Error while initializing offline cache storage", e)
|
||||
this.worker.sendError(e)
|
||||
this.sendError(e)
|
||||
}
|
||||
}
|
||||
// both "else" case and fallback for unavailable storage and error cases
|
||||
|
|
|
@ -14,7 +14,7 @@ import { typeModels as tutanotaTypeModels } from "../../entities/tutanota/TypeMo
|
|||
import type { GroupMembership, User } from "../../entities/sys/TypeRefs.js"
|
||||
import type { TypeModel } from "../../common/EntityTypes"
|
||||
import { isTest } from "../../common/Env"
|
||||
import { aes256EncryptSearchIndexEntry, Aes256Key, aesDecrypt, unauthenticatedAesDecrypt } from "@tutao/tutanota-crypto"
|
||||
import { aes256EncryptSearchIndexEntry, Aes256Key, unauthenticatedAesDecrypt } from "@tutao/tutanota-crypto"
|
||||
|
||||
export function encryptIndexKeyBase64(key: Aes256Key, indexKey: string, dbIv: Uint8Array): Base64 {
|
||||
return uint8ArrayToBase64(encryptIndexKeyUint8Array(key, indexKey, dbIv))
|
||||
|
|
79
src/common/api/worker/workerInterfaces.ts
Normal file
79
src/common/api/worker/workerInterfaces.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { EventBusClient } from "./EventBusClient.js"
|
||||
import { LoginFacade, LoginListener } from "./facades/LoginFacade.js"
|
||||
import { WebsocketConnectivityListener } from "../../misc/WebsocketConnectivityModel.js"
|
||||
import { ExposedProgressTracker } from "../main/ProgressTracker.js"
|
||||
import { ExposedEventController } from "../main/EventController.js"
|
||||
import { ExposedOperationProgressTracker } from "../main/OperationProgressTracker.js"
|
||||
import { InfoMessageHandler } from "../../gui/InfoMessageHandler.js"
|
||||
import { CustomerFacade } from "./facades/lazy/CustomerFacade.js"
|
||||
import { GiftCardFacade } from "./facades/lazy/GiftCardFacade.js"
|
||||
import { GroupManagementFacade } from "./facades/lazy/GroupManagementFacade.js"
|
||||
import { ConfigurationDatabase } from "./facades/lazy/ConfigurationDatabase.js"
|
||||
import { CalendarFacade } from "./facades/lazy/CalendarFacade.js"
|
||||
import { MailFacade } from "./facades/lazy/MailFacade.js"
|
||||
import { ShareFacade } from "./facades/lazy/ShareFacade.js"
|
||||
import { CacheManagementFacade } from "./facades/lazy/CacheManagementFacade.js"
|
||||
import { CounterFacade } from "./facades/lazy/CounterFacade.js"
|
||||
import { BookingFacade } from "./facades/lazy/BookingFacade.js"
|
||||
import { MailAddressFacade } from "./facades/lazy/MailAddressFacade.js"
|
||||
import { BlobAccessTokenFacade } from "./facades/BlobAccessTokenFacade.js"
|
||||
import { BlobFacade } from "./facades/lazy/BlobFacade.js"
|
||||
import { UserManagementFacade } from "./facades/lazy/UserManagementFacade.js"
|
||||
import { RecoverCodeFacade } from "./facades/lazy/RecoverCodeFacade.js"
|
||||
import { EntityRestInterface } from "./rest/EntityRestClient.js"
|
||||
import { IServiceExecutor } from "../common/ServiceRequest.js"
|
||||
import { CryptoFacade } from "./crypto/CryptoFacade.js"
|
||||
import { ExposedCacheStorage } from "./rest/DefaultEntityRestCache.js"
|
||||
import { SqlCipherFacade } from "../../native/common/generatedipc/SqlCipherFacade.js"
|
||||
import { EntropyFacade } from "./facades/EntropyFacade.js"
|
||||
import { WorkerFacade } from "./facades/WorkerFacade.js"
|
||||
import { ContactFacade } from "./facades/lazy/ContactFacade.js"
|
||||
|
||||
export interface WorkerRandomizer {
|
||||
generateRandomNumber(numBytes: number): Promise<number>
|
||||
}
|
||||
|
||||
export interface ExposedEventBus {
|
||||
tryReconnect: EventBusClient["tryReconnect"]
|
||||
close: EventBusClient["close"]
|
||||
}
|
||||
|
||||
/** Interface for the "main"/webpage context of the app, interface for the worker client. */
|
||||
export interface MainInterface {
|
||||
readonly loginListener: LoginListener
|
||||
readonly wsConnectivityListener: WebsocketConnectivityListener
|
||||
readonly progressTracker: ExposedProgressTracker
|
||||
readonly eventController: ExposedEventController
|
||||
readonly operationProgressTracker: ExposedOperationProgressTracker
|
||||
readonly infoMessageHandler: InfoMessageHandler
|
||||
}
|
||||
|
||||
/** Interface of the facades exposed by the worker, basically interface for the worker itself */
|
||||
export interface CommonWorkerInterface {
|
||||
readonly loginFacade: LoginFacade
|
||||
readonly customerFacade: CustomerFacade
|
||||
readonly giftCardFacade: GiftCardFacade
|
||||
readonly groupManagementFacade: GroupManagementFacade
|
||||
readonly configFacade: ConfigurationDatabase
|
||||
readonly calendarFacade: CalendarFacade
|
||||
readonly mailFacade: MailFacade
|
||||
readonly shareFacade: ShareFacade
|
||||
readonly cacheManagementFacade: CacheManagementFacade
|
||||
readonly counterFacade: CounterFacade
|
||||
readonly bookingFacade: BookingFacade
|
||||
readonly mailAddressFacade: MailAddressFacade
|
||||
readonly blobAccessTokenFacade: BlobAccessTokenFacade
|
||||
readonly blobFacade: BlobFacade
|
||||
readonly userManagementFacade: UserManagementFacade
|
||||
readonly recoverCodeFacade: RecoverCodeFacade
|
||||
readonly restInterface: EntityRestInterface
|
||||
readonly serviceExecutor: IServiceExecutor
|
||||
readonly cryptoFacade: CryptoFacade
|
||||
readonly cacheStorage: ExposedCacheStorage
|
||||
readonly sqlCipherFacade: SqlCipherFacade
|
||||
readonly random: WorkerRandomizer
|
||||
readonly eventBus: ExposedEventBus
|
||||
readonly entropyFacade: EntropyFacade
|
||||
readonly workerFacade: WorkerFacade
|
||||
readonly contactFacade: ContactFacade
|
||||
}
|
|
@ -11,18 +11,17 @@ import {
|
|||
import { getFirstOrThrow, isNotNull, LazyLoaded, ofClass, promiseMap } from "@tutao/tutanota-utils"
|
||||
import Stream from "mithril/stream"
|
||||
import stream from "mithril/stream"
|
||||
import { SearchFacade } from "../api/worker/search/SearchFacade.js"
|
||||
import { EntityClient, loadMultipleFromLists } from "../api/common/EntityClient.js"
|
||||
import { LoginController } from "../api/main/LoginController.js"
|
||||
import { EntityEventsListener, EventController } from "../api/main/EventController.js"
|
||||
import { LoginIncompleteError } from "../api/common/error/LoginIncompleteError.js"
|
||||
import { cleanMailAddress } from "../api/common/utils/CommonCalendarUtils.js"
|
||||
import { createRestriction, SearchCategoryTypes } from "../../mail-app/search/model/SearchUtils.js"
|
||||
import { DbError } from "../api/common/error/DbError.js"
|
||||
import { compareOldestFirst, getEtId } from "../api/common/utils/EntityUtils.js"
|
||||
import { NotAuthorizedError, NotFoundError } from "../api/common/error/RestError.js"
|
||||
import { ShareCapability } from "../api/common/TutanotaConstants.js"
|
||||
import { EntityUpdateData } from "../api/common/utils/EntityUpdateUtils.js"
|
||||
import type { SearchResult } from "../api/worker/search/SearchTypes.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
@ -40,10 +39,10 @@ export class ContactModel {
|
|||
private contactListInfo: Stream<ReadonlyArray<ContactListInfo>> = stream()
|
||||
|
||||
constructor(
|
||||
private readonly searchFacade: SearchFacade,
|
||||
private readonly entityClient: EntityClient,
|
||||
private readonly loginController: LoginController,
|
||||
private readonly eventController: EventController,
|
||||
private readonly contactSearch: (query: string, field: string, minSuggestionCount: number, maxResults?: number) => Promise<SearchResult>,
|
||||
) {
|
||||
this.contactListId = lazyContactListId(loginController, this.entityClient)
|
||||
this.eventController.addEntityListener(this.entityEventsReceived)
|
||||
|
@ -84,11 +83,7 @@ export class ContactModel {
|
|||
const cleanedMailAddress = cleanMailAddress(mailAddress)
|
||||
let result
|
||||
try {
|
||||
result = await this.searchFacade.search(
|
||||
'"' + cleanedMailAddress + '"',
|
||||
createRestriction(SearchCategoryTypes.contact, null, null, "mailAddress", [], null),
|
||||
0,
|
||||
)
|
||||
result = await this.contactSearch('"' + cleanedMailAddress + '"', "mailAddress", 0)
|
||||
} catch (e) {
|
||||
// If IndexedDB is not supported or isn't working for some reason we load contacts from the server and
|
||||
// search manually.
|
||||
|
@ -131,7 +126,7 @@ export class ContactModel {
|
|||
if (!this.loginController.isFullyLoggedIn()) {
|
||||
throw new LoginIncompleteError("cannot search for contacts as online login is not completed")
|
||||
}
|
||||
const result = await this.searchFacade.search(query, createRestriction(SearchCategoryTypes.contact, null, null, field, [], null), minSuggestionCount)
|
||||
const result = await this.contactSearch(query, field, minSuggestionCount)
|
||||
return await loadMultipleFromLists(ContactTypeRef, this.entityClient, result.results)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { ExportFacade } from "../native/common/generatedipc/ExportFacade.js"
|
||||
import { MailBundle } from "../../mail-app/mail/export/Bundler.js"
|
||||
import { createDataFile, DataFile } from "../api/common/DataFile.js"
|
||||
import { fileExists } from "./PathUtils.js"
|
||||
import path from "node:path"
|
||||
import { MailExportMode } from "../../mail-app/mail/export/Exporter.js"
|
||||
import { DesktopConfigKey } from "./config/ConfigKeys.js"
|
||||
import { DesktopConfig } from "./config/DesktopConfig.js"
|
||||
import { NativeImage } from "electron"
|
||||
|
@ -12,6 +10,7 @@ import { Attachment, Email, MessageEditorFormat } from "@tutao/oxmsg"
|
|||
import { sanitizeFilename } from "../api/common/utils/FileUtils.js"
|
||||
import { promises as fs } from "node:fs"
|
||||
import { TempFs } from "./files/TempFs.js"
|
||||
import { MailBundle, MailExportMode } from "../mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
const EXPORT_DIR = "export"
|
||||
|
||||
|
|
|
@ -2,9 +2,7 @@ import m from "mithril"
|
|||
import { show as showNotificationOverlay } from "./base/NotificationOverlay"
|
||||
import { lang, TranslationKey } from "../misc/LanguageViewModel"
|
||||
import { assertMainOrNode } from "../api/common/Env"
|
||||
import { SearchModel } from "../../mail-app/search/model/SearchModel.js"
|
||||
import { SearchIndexStateInfo } from "../api/worker/search/SearchTypes.js"
|
||||
import { CalendarSearchModel } from "../../calendar-app/calendar/search/model/CalendarSearchModel.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { Indexer } from "../api/worker/search/Indexer.js"
|
||||
import { NativePushServiceApp } from "../native/main/NativePushServiceApp.js"
|
||||
import { ConfigurationDatabase } from "../api/worker/facades/lazy/ConfigurationDatabase.js"
|
||||
import { NativeContactsSyncManager } from "../../mail-app/contacts/model/NativeContactsSyncManager.js"
|
||||
import { CredentialsInfo } from "../native/common/generatedipc/CredentialsInfo.js"
|
||||
|
||||
export interface CredentialRemovalHandler {
|
||||
|
@ -14,18 +12,16 @@ export class NoopCredentialRemovalHandler implements CredentialRemovalHandler {
|
|||
|
||||
export class AppsCredentialRemovalHandler implements CredentialRemovalHandler {
|
||||
constructor(
|
||||
private readonly indexer: Indexer,
|
||||
private readonly pushApp: NativePushServiceApp,
|
||||
private readonly configFacade: ConfigurationDatabase,
|
||||
private readonly mobileContactsManager: NativeContactsSyncManager | null,
|
||||
private readonly appSpecificCredentialRemovalActions: (login: string, userId: string) => Promise<void>,
|
||||
) {}
|
||||
|
||||
async onCredentialsRemoved({ login, userId }: CredentialsInfo) {
|
||||
await this.indexer.deleteIndex(userId)
|
||||
await this.pushApp.invalidateAlarmsForUser(userId)
|
||||
await this.pushApp.removeUserFromNotifications(userId)
|
||||
await this.configFacade.delete(userId)
|
||||
|
||||
await this.mobileContactsManager?.disableSync(userId, login)
|
||||
await this.appSpecificCredentialRemovalActions(login, userId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ export class MailboxModel {
|
|||
if (isUpdateForTypeRef(GroupInfoTypeRef, update)) {
|
||||
if (update.operation === OperationType.UPDATE) {
|
||||
await this._init()
|
||||
m.redraw
|
||||
m.redraw()
|
||||
}
|
||||
} else if (this.logins.getUserController().isUpdateForLoggedInUserInstance(update, eventOwnerGroupId)) {
|
||||
let newMemberships = this.logins.getUserController().getMailGroupMemberships()
|
||||
|
|
|
@ -2,14 +2,16 @@ import { assertMainOrNode } from "../api/common/Env.js"
|
|||
import { CustomerPropertiesTypeRef, GroupInfo, User } from "../api/entities/sys/TypeRefs.js"
|
||||
import { Contact, createContact, createContactMailAddress, Mail } from "../api/entities/tutanota/TypeRefs.js"
|
||||
import { fullNameToFirstAndLastName, mailAddressToFirstAndLastName } from "../misc/parsing/MailAddressParser.js"
|
||||
import { assertNotNull, contains, neverNull } from "@tutao/tutanota-utils"
|
||||
import { assertNotNull, contains, neverNull, uint8ArrayToBase64 } from "@tutao/tutanota-utils"
|
||||
import {
|
||||
ALLOWED_IMAGE_FORMATS,
|
||||
ContactAddressType,
|
||||
ConversationType, EncryptionAuthStatus,
|
||||
getMailFolderType,
|
||||
GroupType,
|
||||
MailState,
|
||||
MAX_ATTACHMENT_SIZE, SYSTEM_GROUP_MAIL_ADDRESS, TUTA_MAIL_ADDRESS_DOMAINS,
|
||||
SYSTEM_GROUP_MAIL_ADDRESS,
|
||||
MAX_BASE64_IMAGE_SIZE,
|
||||
MAX_ATTACHMENT_SIZE, TUTA_MAIL_ADDRESS_DOMAINS,
|
||||
} from "../api/common/TutanotaConstants.js"
|
||||
import { UserController } from "../api/main/UserController.js"
|
||||
import { getEnabledMailAddressesForGroupInfo, getGroupInfoDisplayName } from "../api/common/utils/GroupUtils.js"
|
||||
|
@ -18,7 +20,9 @@ import { MailboxDetail } from "./MailboxModel.js"
|
|||
import { LoginController } from "../api/main/LoginController.js"
|
||||
import { EntityClient } from "../api/common/EntityClient.js"
|
||||
import { Attachment } from "./SendMailModel.js"
|
||||
import { TUTANOTA_MAIL_ADDRESS_DOMAINS } from "../../../.rollup.cache/home/and/dev/repositories/tutanota-3/build/src/common/api/common/TutanotaConstants.js"
|
||||
import { showFileChooser } from "../file/FileController.js"
|
||||
import { DataFile } from "../api/common/DataFile.js"
|
||||
import { Dialog } from "../gui/base/Dialog.js"
|
||||
|
||||
assertMainOrNode()
|
||||
export const LINE_BREAK = "<br>"
|
||||
|
@ -209,8 +213,8 @@ export enum RecipientField {
|
|||
BCC = "bcc",
|
||||
}
|
||||
|
||||
export function isTutanotaMailAddress(mailAddress: string): boolean {
|
||||
return TUTANOTA_MAIL_ADDRESS_DOMAINS.some((tutaDomain) => mailAddress.endsWith("@" + tutaDomain))
|
||||
export function isTutaMailAddress(mailAddress: string): boolean {
|
||||
return TUTA_MAIL_ADDRESS_DOMAINS.some((tutaDomain) => mailAddress.endsWith("@" + tutaDomain))
|
||||
}
|
||||
|
||||
export function hasValidEncryptionAuthForTeamOrSystemMail({ encryptionAuthStatus }: Mail): boolean {
|
||||
|
@ -249,3 +253,59 @@ export function isSystemNotification(mail: Mail): boolean {
|
|||
export function isNoReplyTeamAddress(address: string): boolean {
|
||||
return address === "no-reply@tutao.de" || address === "no-reply@tutanota.de"
|
||||
}
|
||||
|
||||
export function insertInlineImageB64ClickHandler(ev: Event, handler: ImageHandler) {
|
||||
showFileChooser(true, ALLOWED_IMAGE_FORMATS).then((files) => {
|
||||
const tooBig: DataFile[] = []
|
||||
|
||||
for (let file of files) {
|
||||
if (file.size > MAX_BASE64_IMAGE_SIZE) {
|
||||
tooBig.push(file)
|
||||
} else {
|
||||
const b64 = uint8ArrayToBase64(file.data)
|
||||
const dataUrlString = `data:${file.mimeType};base64,${b64}`
|
||||
handler.insertImage(dataUrlString, {
|
||||
style: "max-width: 100%",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (tooBig.length > 0) {
|
||||
Dialog.message(() =>
|
||||
lang.get("tooBigInlineImages_msg", {
|
||||
"{size}": MAX_BASE64_IMAGE_SIZE / 1024,
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// .msg export is handled in DesktopFileExport because it uses APIs that can't be loaded web side
|
||||
export type MailExportMode = "msg" | "eml"
|
||||
/**
|
||||
* Used to pass all downloaded mail stuff to the desktop side to be exported as a file
|
||||
* Ideally this would just be {Mail, Headers, Body, FileReference[]}
|
||||
* but we can't send Dates over to the native side, so we may as well just extract everything here
|
||||
*/
|
||||
export type MailBundleRecipient = {
|
||||
address: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export type MailBundle = {
|
||||
mailId: IdTuple
|
||||
subject: string
|
||||
body: string
|
||||
sender: MailBundleRecipient
|
||||
to: MailBundleRecipient[]
|
||||
cc: MailBundleRecipient[]
|
||||
bcc: MailBundleRecipient[]
|
||||
replyTo: MailBundleRecipient[]
|
||||
isDraft: boolean
|
||||
isRead: boolean
|
||||
sentOn: number
|
||||
// UNIX timestamp
|
||||
receivedOn: number // UNIX timestamp,
|
||||
headers: string | null
|
||||
attachments: DataFile[]
|
||||
}
|
||||
|
|
|
@ -26,14 +26,12 @@ import { showSnackBar } from "../gui/base/SnackBar"
|
|||
import { Credentials, credentialsToUnencrypted } from "./credentials/Credentials"
|
||||
import { showErrorDialogNotLoggedIn, showErrorNotification } from "./ErrorReporter"
|
||||
import { CancelledError } from "../api/common/error/CancelledError"
|
||||
import { getLoginErrorMessage } from "./LoginUtils"
|
||||
|
||||
import { SessionType } from "../api/common/SessionType.js"
|
||||
import { OfflineDbClosedError } from "../api/common/error/OfflineDbClosedError.js"
|
||||
import { UserTypeRef } from "../api/entities/sys/TypeRefs.js"
|
||||
import { isOfflineError } from "../api/common/utils/ErrorUtils.js"
|
||||
import { showRequestPasswordDialog } from "./passwords/PasswordRequestDialog.js"
|
||||
import { SearchModel } from "../../mail-app/search/model/SearchModel.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
@ -124,7 +122,8 @@ export async function handleUncaughtErrorImpl(e: Error) {
|
|||
}
|
||||
} else if (e instanceof IndexingNotSupportedError) {
|
||||
console.log("Indexing not supported", e)
|
||||
if (search instanceof SearchModel) {
|
||||
if ("indexingSupported" in search) {
|
||||
// search can be in two flavours: "SearchModel" and "CalendarSearchModel. Only "SearchModel" has indexing
|
||||
search.indexingSupported = false
|
||||
}
|
||||
} else if (e instanceof QuotaExceededError) {
|
||||
|
|
|
@ -2,8 +2,8 @@ import { WsConnectionState } from "../api/main/WorkerClient.js"
|
|||
import stream from "mithril/stream"
|
||||
import { identity } from "@tutao/tutanota-utils"
|
||||
import { CloseEventBusOption } from "../api/common/TutanotaConstants.js"
|
||||
import { ExposedEventBus } from "../api/worker/WorkerImpl.js"
|
||||
import { WebsocketLeaderStatus } from "../api/entities/sys/TypeRefs.js"
|
||||
import { ExposedEventBus } from "../api/worker/workerInterfaces.js"
|
||||
|
||||
export interface WebsocketConnectivityListener {
|
||||
updateWebSocketState(wsConnectionState: WsConnectionState): Promise<void>
|
||||
|
|
|
@ -2,8 +2,8 @@ import m, { Params } from "mithril"
|
|||
import { assertMainOrNodeBoot, isApp, isElectronClient, isIOSApp, Mode } from "../api/common/Env"
|
||||
import { lang } from "./LanguageViewModel"
|
||||
import { client } from "./ClientDetector"
|
||||
import type { Indexer } from "../api/worker/search/Indexer"
|
||||
import { remove } from "@tutao/tutanota-utils"
|
||||
import type { Indexer } from "../../mail-app/workerUtils/index/Indexer.js"
|
||||
import { noOp, remove } from "@tutao/tutanota-utils"
|
||||
import { WebsocketConnectivityModel } from "./WebsocketConnectivityModel.js"
|
||||
import { LoginController } from "../api/main/LoginController.js"
|
||||
|
||||
|
@ -24,6 +24,7 @@ export class WindowFacade {
|
|||
private _ignoreNextPopstate: boolean = false
|
||||
private connectivityModel!: WebsocketConnectivityModel
|
||||
private logins: LoginController | null = null
|
||||
private appBasedVisibilityChage: (visible: boolean) => void = noOp
|
||||
|
||||
constructor() {
|
||||
this._windowSizeListeners = []
|
||||
|
@ -99,7 +100,7 @@ export class WindowFacade {
|
|||
}
|
||||
}
|
||||
|
||||
init(logins: LoginController, indexerFacade: Indexer, connectivityModel: WebsocketConnectivityModel) {
|
||||
init(logins: LoginController, connectivityModel: WebsocketConnectivityModel, appBasedVisibilityChage: ((visible: boolean) => void) | null) {
|
||||
this.logins = logins
|
||||
|
||||
if (window.addEventListener && !isApp()) {
|
||||
|
@ -108,7 +109,7 @@ export class WindowFacade {
|
|||
window.addEventListener("unload", (e) => this._onUnload())
|
||||
}
|
||||
|
||||
this.indexerFacade = indexerFacade
|
||||
this.appBasedVisibilityChage = appBasedVisibilityChage ?? noOp
|
||||
this.connectivityModel = connectivityModel
|
||||
|
||||
if (env.mode === Mode.App || env.mode === Mode.Desktop || env.mode === Mode.Admin) {
|
||||
|
@ -257,7 +258,7 @@ export class WindowFacade {
|
|||
document.addEventListener("visibilitychange", () => {
|
||||
console.log("Visibility change, hidden: ", document.hidden)
|
||||
|
||||
this.indexerFacade?.onVisibilityChanged(!document.hidden)
|
||||
this.appBasedVisibilityChage(!document.hidden)
|
||||
|
||||
if (!document.hidden) {
|
||||
// On iOS devices the WebSocket close event fires when the app comes back to foreground
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { assertMainOrNode } from "../../api/common/Env"
|
||||
import { assert } from "@tutao/tutanota-utils"
|
||||
import { WorkerRandomizer } from "../../api/worker/WorkerImpl"
|
||||
import { WorkerRandomizer } from "../../api/worker/workerInterfaces.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { promiseMap } from "@tutao/tutanota-utils"
|
||||
import type { MailBundle } from "../../../mail-app/mail/export/Bundler"
|
||||
import { FileReference } from "../../api/common/utils/FileUtils"
|
||||
import { DataFile } from "../../api/common/DataFile"
|
||||
import { HttpMethod } from "../../api/common/EntityFunctions"
|
||||
|
@ -7,6 +6,7 @@ import { FileFacade } from "./generatedipc/FileFacade.js"
|
|||
import { ExportFacade } from "./generatedipc/ExportFacade.js"
|
||||
import { DownloadTaskResponse } from "./generatedipc/DownloadTaskResponse"
|
||||
import { UploadTaskResponse } from "./generatedipc/UploadTaskResponse"
|
||||
import { MailBundle } from "../../mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
export type FileUri = string
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
/* generated file, don't edit. */
|
||||
|
||||
export { MailBundle } from "../../../../mail-app/mail/export/Bundler.js"
|
||||
export { MailBundle } from "../../../mailFunctionality/SharedMailUtils.js"
|
||||
|
|
16
src/common/search/CommonSearchModel.ts
Normal file
16
src/common/search/CommonSearchModel.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import Stream from "mithril/stream"
|
||||
import type { SearchRestriction, SearchResult } from "../api/worker/search/SearchTypes.js"
|
||||
import { SearchQuery } from "../../calendar-app/calendar/search/model/CalendarSearchModel.js"
|
||||
import { ProgressTracker } from "../api/main/ProgressTracker.js"
|
||||
|
||||
export interface CommonSearchModel {
|
||||
result: Stream<SearchResult | null>
|
||||
lastQueryString: Stream<string | null>
|
||||
cancelSignal: Stream<boolean>
|
||||
|
||||
search(searchQuery: SearchQuery, progressTracker: ProgressTracker): Promise<SearchResult | void>
|
||||
|
||||
isNewSearch(query: string, restriction: SearchRestriction): boolean
|
||||
|
||||
sendCancelSignal(): void
|
||||
}
|
|
@ -22,7 +22,8 @@ import { GENERATED_MAX_ID } from "../api/common/utils/EntityUtils.js"
|
|||
import { locator } from "../api/main/CommonLocator.js"
|
||||
import { PlanType } from "../api/common/TutanotaConstants.js"
|
||||
import { getWhitelabelDomainInfo } from "../api/common/utils/CustomerUtils.js"
|
||||
import { insertInlineImageB64ClickHandler } from "../../mail-app/mail/view/MailViewerUtils.js"
|
||||
|
||||
import { insertInlineImageB64ClickHandler } from "../mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
export function showAddOrEditNotificationEmailDialog(userController: UserController, selectedNotificationLanguage?: string) {
|
||||
let existingTemplate: NotificationMailTemplate | undefined = undefined
|
||||
|
|
|
@ -14,7 +14,7 @@ import { IconButton, IconButtonAttrs } from "../gui/base/IconButton.js"
|
|||
import { ButtonSize } from "../gui/base/ButtonSize.js"
|
||||
import { EmailDomainData } from "./mailaddress/MailAddressesUtils.js"
|
||||
import { BootIcons } from "../gui/base/icons/BootIcons.js"
|
||||
import { isTutanotaMailAddress } from "../mailFunctionality/SharedMailUtils.js"
|
||||
import { isTutaMailAddress } from "../mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
@ -177,7 +177,7 @@ export class SelectMailAddressForm implements Component<SelectMailAddressFormAtt
|
|||
this.onBusyStateChanged(false, onBusyStateChanged)
|
||||
|
||||
return
|
||||
} else if (!isMailAddress(cleanMailAddress, true) || (isTutanotaMailAddress(cleanMailAddress) && cleanUsername.length < 3)) {
|
||||
} else if (!isMailAddress(cleanMailAddress, true) || (isTutaMailAddress(cleanMailAddress) && cleanUsername.length < 3)) {
|
||||
this.onValidationFinished(
|
||||
cleanMailAddress,
|
||||
{
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { AddressToName, MailAddressNameChanger } from "../../../common/settings/mailaddress/MailAddressTableModel.js"
|
||||
import { MailAddressFacade } from "../../../common/api/worker/facades/lazy/MailAddressFacade.js"
|
||||
import { assertNotNull } from "@tutao/tutanota-utils"
|
||||
import { AddressToName, MailAddressNameChanger } from "./MailAddressTableModel.js"
|
||||
import { MailAddressFacade } from "../../api/worker/facades/lazy/MailAddressFacade.js"
|
||||
|
||||
/**
|
||||
* A {@link MailAddressNameChanger} intended for admins to set names for aliases bound to user mailboxes.
|
|
@ -13,7 +13,7 @@ import { UpgradeRequiredError } from "../../api/main/UpgradeRequiredError.js"
|
|||
import { IServiceExecutor } from "../../api/common/ServiceRequest.js"
|
||||
import { getAvailableMatchingPlans } from "../../subscription/SubscriptionUtils.js"
|
||||
import { EntityUpdateData, isUpdateFor, isUpdateForTypeRef } from "../../api/common/utils/EntityUpdateUtils.js"
|
||||
import { isTutanotaMailAddress } from "../../mailFunctionality/SharedMailUtils.js"
|
||||
import { isTutaMailAddress } from "../../mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
export enum AddressStatus {
|
||||
Primary,
|
||||
|
@ -99,8 +99,8 @@ export class MailAddressTableModel {
|
|||
.sort((a, b) => (a.mailAddress > b.mailAddress ? 1 : -1))
|
||||
.map(({ mailAddress, enabled }) => {
|
||||
const status =
|
||||
// O(aliases * TUTANOTA_MAIL_ADDRESS_DOMAINS)
|
||||
isTutanotaMailAddress(mailAddress) ? (enabled ? AddressStatus.Alias : AddressStatus.DisabledAlias) : AddressStatus.Custom
|
||||
// O(aliases * TUTA_MAIL_ADDRESS_DOMAINS)
|
||||
isTutaMailAddress(mailAddress) ? (enabled ? AddressStatus.Alias : AddressStatus.DisabledAlias) : AddressStatus.Custom
|
||||
|
||||
return {
|
||||
name: nameMappings.get(mailAddress) ?? "",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { AddressToName, MailAddressNameChanger } from "../../../common/settings/mailaddress/MailAddressTableModel.js"
|
||||
import { MailboxModel } from "../../../common/mailFunctionality/MailboxModel.js"
|
||||
import { createMailAddressProperties, MailboxProperties } from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { EntityClient } from "../../../common/api/common/EntityClient.js"
|
||||
import { AddressToName, MailAddressNameChanger } from "./MailAddressTableModel.js"
|
||||
import { MailboxModel } from "../../mailFunctionality/MailboxModel.js"
|
||||
import { createMailAddressProperties, MailboxProperties } from "../../api/entities/tutanota/TypeRefs.js"
|
||||
import { EntityClient } from "../../api/common/EntityClient.js"
|
||||
import { findAndRemove } from "@tutao/tutanota-utils"
|
||||
|
||||
/** Name changer for personal mailbox of the currently logged-in user. */
|
|
@ -97,7 +97,9 @@ import("./translations/en.js")
|
|||
const { BottomNav } = await import("./gui/BottomNav.js")
|
||||
|
||||
// this needs to stay after client.init
|
||||
windowFacade.init(mailLocator.logins, mailLocator.indexerFacade, mailLocator.connectivityModel)
|
||||
windowFacade.init(mailLocator.logins, mailLocator.connectivityModel, (visible) => {
|
||||
mailLocator.indexerFacade?.onVisibilityChanged(!document.hidden)
|
||||
})
|
||||
if (isDesktop()) {
|
||||
import("../common/native/main/UpdatePrompt.js").then(({ registerForUpdates }) => registerForUpdates(mailLocator.desktopSettingsFacade))
|
||||
}
|
||||
|
@ -126,11 +128,10 @@ import("./translations/en.js")
|
|||
mailLocator.fileApp.clearFileData().catch((e) => console.log("Failed to clean file data", e))
|
||||
mailLocator.nativeContactsSyncManager()?.syncContacts()
|
||||
}
|
||||
},
|
||||
async onFullLoginSuccess() {
|
||||
await mailLocator.mailboxModel.init()
|
||||
await mailLocator.mailModel.init()
|
||||
},
|
||||
async onFullLoginSuccess() {},
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -5,40 +5,13 @@ import { MailState } from "../../../common/api/common/TutanotaConstants"
|
|||
import { getLetId } from "../../../common/api/common/utils/EntityUtils"
|
||||
import type { HtmlSanitizer } from "../../../common/misc/HtmlSanitizer"
|
||||
import { promiseMap } from "@tutao/tutanota-utils"
|
||||
import { DataFile } from "../../../common/api/common/DataFile"
|
||||
import { FileController } from "../../../common/file/FileController"
|
||||
import { MailFacade } from "../../../common/api/worker/facades/lazy/MailFacade.js"
|
||||
import { CryptoFacade } from "../../../common/api/worker/crypto/CryptoFacade.js"
|
||||
import { getDisplayedSender, getMailBodyText, MailAddressAndName } from "../../../common/api/common/CommonMailUtils.js"
|
||||
import { loadMailDetails } from "../view/MailViewerUtils.js"
|
||||
import { loadMailHeaders } from "../model/MailUtils.js"
|
||||
|
||||
/**
|
||||
* Used to pass all downloaded mail stuff to the desktop side to be exported as a file
|
||||
* Ideally this would just be {Mail, Headers, Body, FileReference[]}
|
||||
* but we can't send Dates over to the native side, so we may as well just extract everything here
|
||||
*/
|
||||
export type MailBundleRecipient = {
|
||||
address: string
|
||||
name?: string
|
||||
}
|
||||
export type MailBundle = {
|
||||
mailId: IdTuple
|
||||
subject: string
|
||||
body: string
|
||||
sender: MailBundleRecipient
|
||||
to: MailBundleRecipient[]
|
||||
cc: MailBundleRecipient[]
|
||||
bcc: MailBundleRecipient[]
|
||||
replyTo: MailBundleRecipient[]
|
||||
isDraft: boolean
|
||||
isRead: boolean
|
||||
sentOn: number
|
||||
// UNIX timestamp
|
||||
receivedOn: number // UNIX timestamp,
|
||||
headers: string | null
|
||||
attachments: DataFile[]
|
||||
}
|
||||
import { MailBundle } from "../../../common/mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
/**
|
||||
* Downloads the mail body and the attachments for an email, to prepare for exporting
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
uint8ArrayToBase64,
|
||||
} from "@tutao/tutanota-utils"
|
||||
import { createDataFile, DataFile, getCleanedMimeType } from "../../../common/api/common/DataFile"
|
||||
import type { MailBundle, MailBundleRecipient } from "./Bundler"
|
||||
import { makeMailBundle } from "./Bundler"
|
||||
import { isDesktop } from "../../../common/api/common/Env"
|
||||
import { sanitizeFilename } from "../../../common/api/common/utils/FileUtils"
|
||||
|
@ -22,8 +21,7 @@ import { MailFacade } from "../../../common/api/worker/facades/lazy/MailFacade.j
|
|||
import { OperationId } from "../../../common/api/main/OperationProgressTracker.js"
|
||||
import { CancelledError } from "../../../common/api/common/error/CancelledError.js"
|
||||
import { CryptoFacade } from "../../../common/api/worker/crypto/CryptoFacade.js"
|
||||
// .msg export is handled in DesktopFileExport because it uses APIs that can't be loaded web side
|
||||
export type MailExportMode = "msg" | "eml"
|
||||
import { MailBundle, MailBundleRecipient, MailExportMode } from "../../../common/mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
export async function generateMailFile(bundle: MailBundle, fileName: string, mode: MailExportMode): Promise<DataFile> {
|
||||
return mode === "eml" ? mailToEmlFile(bundle, fileName) : locator.fileApp.mailToMsg(bundle, fileName)
|
||||
|
|
42
src/mail-app/mail/model/MailChecks.ts
Normal file
42
src/mail-app/mail/model/MailChecks.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
//@bundleInto:common
|
||||
|
||||
import { Mail, MailFolder } from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { MailModel } from "./MailModel.js"
|
||||
import { FolderSystem } from "../../../common/api/common/mail/FolderSystem.js"
|
||||
import { MailSetKind } from "../../../common/api/common/TutanotaConstants.js"
|
||||
|
||||
export function isSubfolderOfType(system: FolderSystem, folder: MailFolder, type: MailSetKind): boolean {
|
||||
const systemFolder = system.getSystemFolderByType(type)
|
||||
return systemFolder != null && system.checkFolderForAncestor(folder, systemFolder._id)
|
||||
}
|
||||
|
||||
export function isDraft(mail: Mail): boolean {
|
||||
return mail.mailDetailsDraft != null
|
||||
}
|
||||
|
||||
export async function isMailInSpamOrTrash(mail: Mail, mailModel: MailModel): Promise<boolean> {
|
||||
const folders = await mailModel.getMailboxFoldersForMail(mail)
|
||||
const mailFolder = folders?.getFolderByMail(mail)
|
||||
if (folders && mailFolder) {
|
||||
return isSpamOrTrashFolder(folders, mailFolder)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given folder is the {@link MailFolderType.SPAM} or {@link MailFolderType.TRASH} folder, or a descendant of those folders.
|
||||
*/
|
||||
export function isSpamOrTrashFolder(system: FolderSystem, folder: MailFolder): boolean {
|
||||
// not using isOfTypeOrSubfolderOf because checking the type first is cheaper
|
||||
return (
|
||||
folder.folderType === MailSetKind.TRASH ||
|
||||
folder.folderType === MailSetKind.SPAM ||
|
||||
isSubfolderOfType(system, folder, MailSetKind.TRASH) ||
|
||||
isSubfolderOfType(system, folder, MailSetKind.SPAM)
|
||||
)
|
||||
}
|
||||
|
||||
export function isOfTypeOrSubfolderOf(system: FolderSystem, folder: MailFolder, type: MailSetKind): boolean {
|
||||
return folder.folderType === type || isSubfolderOfType(system, folder, type)
|
||||
}
|
|
@ -35,7 +35,8 @@ import { WebsocketConnectivityModel } from "../../../common/misc/WebsocketConnec
|
|||
import { EntityClient } from "../../../common/api/common/EntityClient.js"
|
||||
import { LoginController } from "../../../common/api/main/LoginController.js"
|
||||
import { MailFacade } from "../../../common/api/worker/facades/lazy/MailFacade.js"
|
||||
import { assertSystemFolderOfType, isSpamOrTrashFolder } from "./MailUtils.js"
|
||||
import { assertSystemFolderOfType } from "./MailUtils.js"
|
||||
import { isSpamOrTrashFolder } from "./MailChecks.js"
|
||||
|
||||
export class MailModel {
|
||||
readonly mailboxCounters: Stream<MailboxCounters> = stream({})
|
||||
|
@ -422,11 +423,7 @@ export class MailModel {
|
|||
someNonEmpty = true
|
||||
}
|
||||
}
|
||||
if (
|
||||
(await this.isEmptyFolder(folder)) &&
|
||||
mailboxDetail.folders.getCustomFoldersOfParent(folder._id).every((f) => deleted.has(getElementId(f))) &&
|
||||
!someNonEmpty
|
||||
) {
|
||||
if ((await this.isEmptyFolder(folder)) && mailboxDetail.folders.getCustomFoldersOfParent(folder._id).every((f) => deleted.has(getElementId(f))) && !someNonEmpty) {
|
||||
await this.finallyDeleteCustomMailFolder(folder)
|
||||
return true
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { FolderSystem, type IndentedFolder } from "../../../common/api/common/mail/FolderSystem.js"
|
||||
import { Header, InboxRule, Mail, MailDetails, MailFolder, TutanotaProperties } from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { assertNotNull, contains, first, neverNull } from "@tutao/tutanota-utils"
|
||||
import { getListId } from "../../../common/api/common/utils/EntityUtils.js"
|
||||
import { assertNotNull, contains, first, isNotEmpty, neverNull } from "@tutao/tutanota-utils"
|
||||
import { getListId, isSameId } from "../../../common/api/common/utils/EntityUtils.js"
|
||||
import { MailModel } from "./MailModel.js"
|
||||
import { lang } from "../../../common/misc/LanguageViewModel.js"
|
||||
import { UserController } from "../../../common/api/main/UserController.js"
|
||||
|
@ -56,39 +56,14 @@ export async function getMoveTargetFolderSystems(foldersModel: MailModel, mails:
|
|||
}
|
||||
const folderStructures = foldersModel.folders()
|
||||
const folderSystem = folderStructures[mailboxDetails.mailbox.folders._id]
|
||||
return folderSystem.getIndentedList().filter((f: IndentedFolder) => f.folder.mails !== getListId(firstMail))
|
||||
}
|
||||
|
||||
export function isSubfolderOfType(system: FolderSystem, folder: MailFolder, type: MailSetKind): boolean {
|
||||
const systemFolder = system.getSystemFolderByType(type)
|
||||
return systemFolder != null && system.checkFolderForAncestor(folder, systemFolder._id)
|
||||
}
|
||||
|
||||
export function isDraft(mail: Mail): boolean {
|
||||
return mail.mailDetailsDraft != null
|
||||
}
|
||||
|
||||
export async function isMailInSpamOrTrash(mail: Mail, mailModel: MailModel): Promise<boolean> {
|
||||
const folders = await mailModel.getMailboxFoldersForMail(mail)
|
||||
const mailFolder = folders?.getFolderByMail(mail)
|
||||
if (folders && mailFolder) {
|
||||
return isSpamOrTrashFolder(folders, mailFolder)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given folder is the {@link MailFolderType.SPAM} or {@link MailFolderType.TRASH} folder, or a descendant of those folders.
|
||||
*/
|
||||
export function isSpamOrTrashFolder(system: FolderSystem, folder: MailFolder): boolean {
|
||||
// not using isOfTypeOrSubfolderOf because checking the type first is cheaper
|
||||
return (
|
||||
folder.folderType === MailSetKind.TRASH ||
|
||||
folder.folderType === MailSetKind.SPAM ||
|
||||
isSubfolderOfType(system, folder, MailSetKind.TRASH) ||
|
||||
isSubfolderOfType(system, folder, MailSetKind.SPAM)
|
||||
)
|
||||
return folderSystem.getIndentedList().filter((f: IndentedFolder) => {
|
||||
if (f.folder.isMailSet && isNotEmpty(firstMail.sets)) {
|
||||
const folderId = firstMail.sets[0]
|
||||
return !isSameId(f.folder._id, folderId)
|
||||
} else {
|
||||
return f.folder.mails !== getListId(firstMail)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,10 +76,6 @@ export function assertSystemFolderOfType(system: FolderSystem, type: Omit<MailSe
|
|||
return assertNotNull(system.getSystemFolderByType(type), "System folder of type does not exist!")
|
||||
}
|
||||
|
||||
export function isOfTypeOrSubfolderOf(system: FolderSystem, folder: MailFolder, type: MailSetKind): boolean {
|
||||
return folder.folderType === type || isSubfolderOfType(system, folder, type)
|
||||
}
|
||||
|
||||
export function getPathToFolderString(folderSystem: FolderSystem, folder: MailFolder, omitLast = false) {
|
||||
const folderPath = folderSystem.getPathToFolder(folder._id)
|
||||
if (omitLast) {
|
||||
|
|
|
@ -13,7 +13,8 @@ import { EntityUpdateData, isUpdateForTypeRef } from "../../../common/api/common
|
|||
import { ListAutoSelectBehavior } from "../../../common/misc/DeviceConfig.js"
|
||||
|
||||
import { MailModel } from "../model/MailModel.js"
|
||||
import { isOfTypeOrSubfolderOf } from "../model/MailUtils.js"
|
||||
|
||||
import { isOfTypeOrSubfolderOf } from "../model/MailChecks.js"
|
||||
|
||||
export type MailViewerViewModelFactory = (options: CreateMailViewerOptions) => MailViewerViewModel
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ import { groupByAndMap } from "@tutao/tutanota-utils"
|
|||
import { mailLocator } from "../../mailLocator.js"
|
||||
import { assertNotNull } from "@tutao/tutanota-utils"
|
||||
import type { FolderSystem, IndentedFolder } from "../../../common/api/common/mail/FolderSystem.js"
|
||||
import { getFolderName, getIndentedFolderNameForDropdown, getPathToFolderString, isSpamOrTrashFolder } from "../model/MailUtils.js"
|
||||
import { getFolderName, getIndentedFolderNameForDropdown, getPathToFolderString } from "../model/MailUtils.js"
|
||||
import { isSpamOrTrashFolder } from "../model/MailChecks.js"
|
||||
|
||||
/**
|
||||
* Dialog for Edit and Add folder are the same.
|
||||
|
|
|
@ -17,9 +17,10 @@ import { ButtonSize } from "../../../common/gui/base/ButtonSize.js"
|
|||
import { MailSetKind } from "../../../common/api/common/TutanotaConstants.js"
|
||||
import { px, size } from "../../../common/gui/size.js"
|
||||
import { RowButton } from "../../../common/gui/base/buttons/RowButton.js"
|
||||
import { getFolderName, isSpamOrTrashFolder, MAX_FOLDER_INDENT_LEVEL } from "../model/MailUtils.js"
|
||||
import { getFolderIcon } from "./MailGuiUtils.js"
|
||||
import { MailModel } from "../model/MailModel.js"
|
||||
import { getFolderName, MAX_FOLDER_INDENT_LEVEL } from "../model/MailUtils.js"
|
||||
import { getFolderIcon } from "./MailGuiUtils.js"
|
||||
import { isSpamOrTrashFolder } from "../model/MailChecks.js"
|
||||
|
||||
export interface MailFolderViewAttrs {
|
||||
mailModel: MailModel
|
||||
|
|
|
@ -30,16 +30,10 @@ import {
|
|||
hasValidEncryptionAuthForTeamOrSystemMail,
|
||||
} from "../../../common/mailFunctionality/SharedMailUtils.js"
|
||||
import { mailLocator } from "../../mailLocator.js"
|
||||
import {
|
||||
assertSystemFolderOfType,
|
||||
getFolderName,
|
||||
getIndentedFolderNameForDropdown,
|
||||
getMoveTargetFolderSystems,
|
||||
isOfTypeOrSubfolderOf,
|
||||
isSpamOrTrashFolder,
|
||||
} from "../model/MailUtils.js"
|
||||
import { assertSystemFolderOfType, getFolderName, getIndentedFolderNameForDropdown, getMoveTargetFolderSystems } from "../model/MailUtils.js"
|
||||
import { FontIcons } from "../../../common/gui/base/icons/FontIcons.js"
|
||||
import { ProgrammingError } from "../../../common/api/common/error/ProgrammingError.js"
|
||||
import { isOfTypeOrSubfolderOf, isSpamOrTrashFolder } from "../model/MailChecks.js"
|
||||
import type { FolderSystem } from "../../../common/api/common/mail/FolderSystem.js"
|
||||
|
||||
export async function showDeleteConfirmationDialog(mails: ReadonlyArray<Mail>): Promise<boolean> {
|
||||
|
|
|
@ -31,8 +31,9 @@ import { VirtualRow } from "../../../common/gui/base/ListUtils.js"
|
|||
import { isKeyPressed } from "../../../common/misc/KeyManager.js"
|
||||
import { ListModel } from "../../../common/misc/ListModel.js"
|
||||
import { mailLocator } from "../../mailLocator.js"
|
||||
import { assertSystemFolderOfType, isOfTypeOrSubfolderOf } from "../model/MailUtils.js"
|
||||
import { assertSystemFolderOfType } from "../model/MailUtils.js"
|
||||
import { canDoDragAndDropExport } from "./MailViewerUtils.js"
|
||||
import { isOfTypeOrSubfolderOf } from "../model/MailChecks.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
|
|
@ -60,8 +60,9 @@ import { getMailboxName } from "../../../common/mailFunctionality/SharedMailUtil
|
|||
import { BottomNav } from "../../gui/BottomNav.js"
|
||||
import { mailLocator } from "../../mailLocator.js"
|
||||
import { showSnackBar } from "../../../common/gui/base/SnackBar.js"
|
||||
import { getFolderName, isSpamOrTrashFolder } from "../model/MailUtils.js"
|
||||
import { getFolderName } from "../model/MailUtils.js"
|
||||
import { canDoDragAndDropExport } from "./MailViewerUtils.js"
|
||||
import { isSpamOrTrashFolder } from "../model/MailChecks.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
|
|
@ -46,9 +46,10 @@ import { ListFetchResult } from "../../../common/gui/base/ListUtils.js"
|
|||
import { EntityUpdateData, isUpdateForTypeRef } from "../../../common/api/common/utils/EntityUpdateUtils.js"
|
||||
import { EventController } from "../../../common/api/main/EventController.js"
|
||||
import { MailModel } from "../model/MailModel.js"
|
||||
import { assertSystemFolderOfType, isOfTypeOrSubfolderOf, isSpamOrTrashFolder, isSubfolderOfType } from "../model/MailUtils.js"
|
||||
import { assertSystemFolderOfType } from "../model/MailUtils.js"
|
||||
import { getMailFilterForType, MailFilterType } from "./MailViewerUtils.js"
|
||||
import { CacheMode } from "../../../common/api/worker/rest/EntityRestClient.js"
|
||||
import { isOfTypeOrSubfolderOf, isSpamOrTrashFolder, isSubfolderOfType } from "../model/MailChecks.js"
|
||||
|
||||
export interface MailOpenedListener {
|
||||
onEmailOpened(mail: Mail): unknown
|
||||
|
|
|
@ -1,17 +1,7 @@
|
|||
import {
|
||||
ALLOWED_IMAGE_FORMATS,
|
||||
Keys,
|
||||
MailReportType,
|
||||
MailState,
|
||||
MAX_BASE64_IMAGE_SIZE,
|
||||
ReplyType,
|
||||
SYSTEM_GROUP_MAIL_ADDRESS,
|
||||
} from "../../../common/api/common/TutanotaConstants"
|
||||
import { assertNotNull, neverNull, ofClass, uint8ArrayToBase64 } from "@tutao/tutanota-utils"
|
||||
import { Keys, MailReportType, MailState, ReplyType, SYSTEM_GROUP_MAIL_ADDRESS } from "../../../common/api/common/TutanotaConstants"
|
||||
import { assertNotNull, neverNull, ofClass } from "@tutao/tutanota-utils"
|
||||
import { InfoLink, lang } from "../../../common/misc/LanguageViewModel"
|
||||
import { Dialog } from "../../../common/gui/base/Dialog"
|
||||
import { DataFile } from "../../../common/api/common/DataFile"
|
||||
import { showFileChooser } from "../../../common/file/FileController.js"
|
||||
import m from "mithril"
|
||||
import { Button, ButtonType } from "../../../common/gui/base/Button.js"
|
||||
import { progressIcon } from "../../../common/gui/base/Icon.js"
|
||||
|
@ -34,35 +24,9 @@ import { Mail, MailDetails } from "../../../common/api/entities/tutanota/TypeRef
|
|||
import { getDisplayedSender } from "../../../common/api/common/CommonMailUtils.js"
|
||||
import { MailFacade } from "../../../common/api/worker/facades/lazy/MailFacade.js"
|
||||
|
||||
import { isDraft } from "../model/MailUtils.js"
|
||||
import { ListFilter } from "../../../common/misc/ListModel.js"
|
||||
import { isDesktop } from "../../../common/api/common/Env.js"
|
||||
|
||||
export function insertInlineImageB64ClickHandler(ev: Event, handler: ImageHandler) {
|
||||
showFileChooser(true, ALLOWED_IMAGE_FORMATS).then((files) => {
|
||||
const tooBig: DataFile[] = []
|
||||
|
||||
for (let file of files) {
|
||||
if (file.size > MAX_BASE64_IMAGE_SIZE) {
|
||||
tooBig.push(file)
|
||||
} else {
|
||||
const b64 = uint8ArrayToBase64(file.data)
|
||||
const dataUrlString = `data:${file.mimeType};base64,${b64}`
|
||||
handler.insertImage(dataUrlString, {
|
||||
style: "max-width: 100%",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (tooBig.length > 0) {
|
||||
Dialog.message(() =>
|
||||
lang.get("tooBigInlineImages_msg", {
|
||||
"{size}": MAX_BASE64_IMAGE_SIZE / 1024,
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
import { isDraft } from "../model/MailChecks.js"
|
||||
|
||||
export async function showHeaderDialog(headersPromise: Promise<string | null>) {
|
||||
let state: { state: "loading" } | { state: "loaded"; headers: string | null } = { state: "loading" }
|
||||
|
|
|
@ -22,8 +22,8 @@ import { CalendarFacade } from "../common/api/worker/facades/lazy/CalendarFacade
|
|||
import { MailFacade } from "../common/api/worker/facades/lazy/MailFacade.js"
|
||||
import { ShareFacade } from "../common/api/worker/facades/lazy/ShareFacade.js"
|
||||
import { CounterFacade } from "../common/api/worker/facades/lazy/CounterFacade.js"
|
||||
import { Indexer } from "../common/api/worker/search/Indexer.js"
|
||||
import { SearchFacade } from "../common/api/worker/search/SearchFacade.js"
|
||||
import { Indexer } from "./workerUtils/index/Indexer.js"
|
||||
import { SearchFacade } from "./workerUtils/index/SearchFacade.js"
|
||||
import { BookingFacade } from "../common/api/worker/facades/lazy/BookingFacade.js"
|
||||
import { MailAddressFacade } from "../common/api/worker/facades/lazy/MailAddressFacade.js"
|
||||
import { BlobFacade } from "../common/api/worker/facades/lazy/BlobFacade.js"
|
||||
|
@ -44,7 +44,6 @@ import { InterWindowEventFacadeSendDispatcher } from "../common/native/common/ge
|
|||
import { ExposedCacheStorage } from "../common/api/worker/rest/DefaultEntityRestCache.js"
|
||||
import { WorkerFacade } from "../common/api/worker/facades/WorkerFacade.js"
|
||||
import { PageContextLoginListener } from "../common/api/main/PageContextLoginListener.js"
|
||||
import { WorkerRandomizer } from "../common/api/worker/WorkerImpl.js"
|
||||
import { WebsocketConnectivityModel } from "../common/misc/WebsocketConnectivityModel.js"
|
||||
import { OperationProgressTracker } from "../common/api/main/OperationProgressTracker.js"
|
||||
import { InfoMessageHandler } from "../common/gui/InfoMessageHandler.js"
|
||||
|
@ -117,12 +116,16 @@ import { AppStorePaymentPicker } from "../common/misc/AppStorePaymentPicker.js"
|
|||
import { MAIL_PREFIX } from "../common/misc/RouteChange.js"
|
||||
import { getDisplayedSender } from "../common/api/common/CommonMailUtils.js"
|
||||
import { MailModel } from "./mail/model/MailModel.js"
|
||||
import { AppType } from "../common/misc/ClientConstants.js"
|
||||
import type { ParsedEvent } from "../common/calendar/import/CalendarImporter.js"
|
||||
import { assertSystemFolderOfType } from "./mail/model/MailUtils.js"
|
||||
import { WorkerRandomizer } from "../common/api/worker/workerInterfaces.js"
|
||||
import { SearchCategoryTypes } from "./search/model/SearchUtils.js"
|
||||
import { WorkerInterface } from "./workerUtils/worker/WorkerImpl.js"
|
||||
import { isMailInSpamOrTrash } from "./mail/model/MailChecks.js"
|
||||
import type { ContactImporter } from "./contacts/ContactImporter.js"
|
||||
import { ExternalCalendarFacade } from "../common/native/common/generatedipc/ExternalCalendarFacade.js"
|
||||
import m from "mithril"
|
||||
import { assertSystemFolderOfType, isMailInSpamOrTrash } from "./mail/model/MailUtils.js"
|
||||
import { AppType } from "../common/misc/ClientConstants.js"
|
||||
import { ParsedEvent } from "../common/calendar/import/CalendarImporter.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
@ -545,12 +548,12 @@ class MailLocator {
|
|||
}
|
||||
|
||||
async ownMailAddressNameChanger(): Promise<MailAddressNameChanger> {
|
||||
const { OwnMailAddressNameChanger } = await import("../mail-app/settings/mailaddress/OwnMailAddressNameChanger.js")
|
||||
const { OwnMailAddressNameChanger } = await import("../common/settings/mailaddress/OwnMailAddressNameChanger.js")
|
||||
return new OwnMailAddressNameChanger(this.mailboxModel, this.entityClient)
|
||||
}
|
||||
|
||||
async adminNameChanger(mailGroupId: Id, userId: Id): Promise<MailAddressNameChanger> {
|
||||
const { AnotherUserMailAddressNameChanger } = await import("../mail-app/settings/mailaddress/AnotherUserMailAddressNameChanger.js")
|
||||
const { AnotherUserMailAddressNameChanger } = await import("../common/settings/mailaddress/AnotherUserMailAddressNameChanger.js")
|
||||
return new AnotherUserMailAddressNameChanger(this.mailAddressFacade, mailGroupId, userId)
|
||||
}
|
||||
|
||||
|
@ -570,7 +573,12 @@ class MailLocator {
|
|||
const { NoopCredentialRemovalHandler, AppsCredentialRemovalHandler } = await import("../common/login/CredentialRemovalHandler.js")
|
||||
return isBrowser()
|
||||
? new NoopCredentialRemovalHandler()
|
||||
: new AppsCredentialRemovalHandler(this.indexerFacade, this.pushService, this.configFacade, isApp() ? this.nativeContactsSyncManager() : null)
|
||||
: new AppsCredentialRemovalHandler(this.pushService, this.configFacade, async (login, userId) => {
|
||||
if (isApp()) {
|
||||
await mailLocator.nativeContactsSyncManager().disableSync(userId, login)
|
||||
}
|
||||
await mailLocator.indexerFacade.deleteIndex(userId)
|
||||
})
|
||||
}
|
||||
|
||||
async loginViewModelFactory(): Promise<lazy<LoginViewModel>> {
|
||||
|
@ -660,7 +668,7 @@ class MailLocator {
|
|||
workerFacade,
|
||||
sqlCipherFacade,
|
||||
contactFacade,
|
||||
} = this.worker.getWorkerInterface()
|
||||
} = this.worker.getWorkerInterface() as WorkerInterface
|
||||
this.loginFacade = loginFacade
|
||||
this.customerFacade = customerFacade
|
||||
this.giftCardFacade = giftCardFacade
|
||||
|
@ -867,7 +875,20 @@ class MailLocator {
|
|||
: new FileControllerNative(blobFacade, guiDownload, this.nativeInterfaces.fileApp)
|
||||
|
||||
const { ContactModel } = await import("../common/contactsFunctionality/ContactModel.js")
|
||||
this.contactModel = new ContactModel(this.searchFacade, this.entityClient, this.logins, this.eventController)
|
||||
this.contactModel = new ContactModel(
|
||||
this.entityClient,
|
||||
this.logins,
|
||||
this.eventController,
|
||||
async (query: string, field: string, minSuggestionCount: number, maxResults?: number) => {
|
||||
const { createRestriction } = await import("./search/model/SearchUtils.js")
|
||||
return mailLocator.searchFacade.search(
|
||||
query,
|
||||
createRestriction(SearchCategoryTypes.contact, null, null, field, [], null),
|
||||
minSuggestionCount,
|
||||
maxResults,
|
||||
)
|
||||
},
|
||||
)
|
||||
this.minimizedMailModel = new MinimizedMailEditorViewModel()
|
||||
this.appStorePaymentPicker = new AppStorePaymentPicker()
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import { formatEventDuration } from "../../calendar-app/calendar/gui/CalendarGui
|
|||
import { getContactListName } from "../../common/contactsFunctionality/ContactUtils.js"
|
||||
|
||||
import { getSenderOrRecipientHeading } from "../mail/view/MailViewerUtils.js"
|
||||
import { mailLocator } from "../mailLocator.js"
|
||||
|
||||
type SearchBarOverlayAttrs = {
|
||||
state: SearchBarState
|
||||
|
@ -107,7 +108,7 @@ export class SearchBarOverlay implements Component<SearchBarOverlayAttrs> {
|
|||
},
|
||||
m(Button, {
|
||||
label: "cancel_action",
|
||||
click: () => locator.indexerFacade.cancelMailIndexing(),
|
||||
click: () => mailLocator.indexerFacade.cancelMailIndexing(),
|
||||
//icon: () => Icons.Cancel
|
||||
type: ButtonType.Secondary,
|
||||
}),
|
||||
|
@ -148,7 +149,7 @@ export class SearchBarOverlay implements Component<SearchBarOverlayAttrs> {
|
|||
},
|
||||
m(Button, {
|
||||
label: "retry_action",
|
||||
click: () => locator.indexerFacade.extendMailIndex(failedIndexingUpTo),
|
||||
click: () => mailLocator.indexerFacade.extendMailIndex(failedIndexingUpTo),
|
||||
type: ButtonType.Secondary,
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -5,12 +5,13 @@ import { NOTHING_INDEXED_TIMESTAMP } from "../../../common/api/common/TutanotaCo
|
|||
import { DbError } from "../../../common/api/common/error/DbError"
|
||||
import type { SearchIndexStateInfo, SearchRestriction, SearchResult } from "../../../common/api/worker/search/SearchTypes"
|
||||
import { arrayEquals, assertNonNull, assertNotNull, incrementMonth, isSameTypeRef, lazyAsync, ofClass, tokenize } from "@tutao/tutanota-utils"
|
||||
import type { SearchFacade } from "../../../common/api/worker/search/SearchFacade"
|
||||
import type { SearchFacade } from "../../workerUtils/index/SearchFacade.js"
|
||||
import { assertMainOrNode } from "../../../common/api/common/Env"
|
||||
import { listIdPart } from "../../../common/api/common/utils/EntityUtils.js"
|
||||
import { IProgressMonitor } from "../../../common/api/common/utils/ProgressMonitor.js"
|
||||
import { ProgressTracker } from "../../../common/api/main/ProgressTracker.js"
|
||||
import { CalendarEventsRepository } from "../../../common/calendar/date/CalendarEventsRepository.js"
|
||||
import { CommonSearchModel } from "../../../common/search/CommonSearchModel.js"
|
||||
|
||||
assertMainOrNode()
|
||||
export type SearchQuery = {
|
||||
|
@ -28,8 +29,8 @@ export class SearchModel {
|
|||
lastQueryString: Stream<string | null>
|
||||
indexingSupported: boolean
|
||||
_searchFacade: SearchFacade
|
||||
private _lastQuery: SearchQuery | null
|
||||
_lastSearchPromise: Promise<SearchResult | void>
|
||||
private lastQuery: SearchQuery | null
|
||||
private lastSearchPromise: Promise<SearchResult | void>
|
||||
cancelSignal: Stream<boolean>
|
||||
|
||||
constructor(searchFacade: SearchFacade, private readonly calendarModel: lazyAsync<CalendarEventsRepository>) {
|
||||
|
@ -46,17 +47,17 @@ export class SearchModel {
|
|||
indexedMailCount: 0,
|
||||
failedIndexingUpTo: null,
|
||||
})
|
||||
this._lastQuery = null
|
||||
this._lastSearchPromise = Promise.resolve()
|
||||
this.lastQuery = null
|
||||
this.lastSearchPromise = Promise.resolve()
|
||||
this.cancelSignal = stream(false)
|
||||
}
|
||||
|
||||
async search(searchQuery: SearchQuery, progressTracker: ProgressTracker): Promise<SearchResult | void> {
|
||||
if (this._lastQuery && searchQueryEquals(searchQuery, this._lastQuery)) {
|
||||
return this._lastSearchPromise
|
||||
if (this.lastQuery && searchQueryEquals(searchQuery, this.lastQuery)) {
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
|
||||
this._lastQuery = searchQuery
|
||||
this.lastQuery = searchQuery
|
||||
const { query, restriction, minSuggestionCount, maxResults } = searchQuery
|
||||
this.lastQueryString(query)
|
||||
let result = this.result()
|
||||
|
@ -83,7 +84,7 @@ export class SearchModel {
|
|||
moreResultsEntries: [],
|
||||
}
|
||||
this.result(result)
|
||||
this._lastSearchPromise = Promise.resolve(result)
|
||||
this.lastSearchPromise = Promise.resolve(result)
|
||||
} else if (isSameTypeRef(CalendarEventTypeRef, restriction.type)) {
|
||||
// we interpret restriction.start as the start of the first day of the first month we want to search
|
||||
// restriction.end is the end of the last day of the last month we want to search
|
||||
|
@ -114,8 +115,8 @@ export class SearchModel {
|
|||
|
||||
if (this.cancelSignal()) {
|
||||
this.result(calendarResult)
|
||||
this._lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this._lastSearchPromise
|
||||
this.lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
|
||||
await calendarModel.loadMonthsIfNeeded(daysInMonths, monitor, this.cancelSignal)
|
||||
|
@ -133,8 +134,8 @@ export class SearchModel {
|
|||
|
||||
if (this.cancelSignal()) {
|
||||
this.result(calendarResult)
|
||||
this._lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this._lastSearchPromise
|
||||
this.lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
|
||||
if (tokens.length > 0) {
|
||||
|
@ -183,17 +184,17 @@ export class SearchModel {
|
|||
|
||||
if (this.cancelSignal()) {
|
||||
this.result(calendarResult)
|
||||
this._lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this._lastSearchPromise
|
||||
this.lastSearchPromise = Promise.resolve(calendarResult)
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.result(calendarResult)
|
||||
this._lastSearchPromise = Promise.resolve(calendarResult)
|
||||
this.lastSearchPromise = Promise.resolve(calendarResult)
|
||||
} else {
|
||||
this._lastSearchPromise = this._searchFacade
|
||||
this.lastSearchPromise = this._searchFacade
|
||||
.search(query, restriction, minSuggestionCount, maxResults ?? undefined)
|
||||
.then((result) => {
|
||||
this.result(result)
|
||||
|
@ -207,12 +208,12 @@ export class SearchModel {
|
|||
)
|
||||
}
|
||||
|
||||
return this._lastSearchPromise
|
||||
return this.lastSearchPromise
|
||||
}
|
||||
|
||||
isNewSearch(query: string, restriction: SearchRestriction): boolean {
|
||||
let isNew = false
|
||||
let lastQuery = this._lastQuery
|
||||
let lastQuery = this.lastQuery
|
||||
if (lastQuery == null) {
|
||||
isNew = true
|
||||
} else if (lastQuery.query !== query) {
|
||||
|
|
|
@ -48,9 +48,9 @@ import {
|
|||
} from "../model/SearchUtils.js"
|
||||
import Stream from "mithril/stream"
|
||||
import { MailboxDetail, MailboxModel } from "../../../common/mailFunctionality/MailboxModel.js"
|
||||
import { SearchFacade } from "../../../common/api/worker/search/SearchFacade.js"
|
||||
import { SearchFacade } from "../../workerUtils/index/SearchFacade.js"
|
||||
import { LoginController } from "../../../common/api/main/LoginController.js"
|
||||
import { Indexer } from "../../../common/api/worker/search/Indexer.js"
|
||||
import { Indexer } from "../../workerUtils/index/Indexer.js"
|
||||
import { EntityClient, loadMultipleFromLists } from "../../../common/api/common/EntityClient.js"
|
||||
import { SearchRouter } from "../../../common/search/view/SearchRouter.js"
|
||||
import { MailOpenedListener } from "../../mail/view/MailViewModel.js"
|
||||
|
|
|
@ -12,7 +12,6 @@ import { DropDownSelector } from "../../common/gui/base/DropDownSelector.js"
|
|||
import { Dialog } from "../../common/gui/base/Dialog"
|
||||
import type { UpdateHelpLabelAttrs } from "./DesktopUpdateHelpLabel"
|
||||
import { DesktopUpdateHelpLabel } from "./DesktopUpdateHelpLabel"
|
||||
import type { MailExportMode } from "../mail/export/Exporter"
|
||||
import { DesktopConfigKey } from "../../common/desktop/config/ConfigKeys"
|
||||
import { getCurrentSpellcheckLanguageLabel, showSpellcheckLanguageDialog } from "../../common/gui/dialogs/SpellcheckLanguageDialog"
|
||||
import { ifAllowedTutaLinks } from "../../common/gui/base/GuiUtils"
|
||||
|
@ -22,6 +21,7 @@ import { IconButton, IconButtonAttrs } from "../../common/gui/base/IconButton.js
|
|||
import { ButtonSize } from "../../common/gui/base/ButtonSize.js"
|
||||
import { MoreInfoLink } from "../../common/misc/news/MoreInfoLink.js"
|
||||
import { UpdatableSettingsViewer } from "../../common/settings/Interfaces.js"
|
||||
import { MailExportMode } from "../../common/mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
|
|
@ -4,13 +4,13 @@ import { lang } from "../../common/misc/LanguageViewModel"
|
|||
import { EmailSignatureType, FeatureType } from "../../common/api/common/TutanotaConstants"
|
||||
import { HtmlEditor } from "../../common/gui/editor/HtmlEditor"
|
||||
import type { TutanotaProperties } from "../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { insertInlineImageB64ClickHandler } from "../mail/view/MailViewerUtils"
|
||||
import { PayloadTooLargeError } from "../../common/api/common/error/RestError"
|
||||
import { showProgressDialog } from "../../common/gui/dialogs/ProgressDialog"
|
||||
import { neverNull, ofClass } from "@tutao/tutanota-utils"
|
||||
import { locator } from "../../common/api/main/CommonLocator"
|
||||
import { assertMainOrNode } from "../../common/api/common/Env"
|
||||
import { DropDownSelector } from "../../common/gui/base/DropDownSelector.js"
|
||||
import { insertInlineImageB64ClickHandler } from "../../common/mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
assertMainOrNode()
|
||||
// signatures can become large, for example if they include a base64 embedded image. we ask for confirmation in such cases
|
||||
|
|
|
@ -16,7 +16,7 @@ import { IconButton, IconButtonAttrs } from "../../common/gui/base/IconButton.js
|
|||
import { ButtonSize } from "../../common/gui/base/ButtonSize.js"
|
||||
import { MailAddressAvailability } from "../../common/api/entities/sys/TypeRefs.js"
|
||||
import { SearchDropDown } from "../../common/gui/SearchDropDown.js"
|
||||
import { isTutanotaMailAddress } from "../../common/mailFunctionality/SharedMailUtils.js"
|
||||
import { isTutaMailAddress } from "../../common/mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
assertMainOrNode()
|
||||
|
||||
|
@ -274,7 +274,7 @@ export class SelectMailAddressFormWithSuggestions implements Component<SelectMai
|
|||
this.onBusyStateChanged(false, onBusyStateChanged)
|
||||
|
||||
return { valid: false }
|
||||
} else if (!isMailAddress(cleanMailAddress, true) || (isTutanotaMailAddress(cleanMailAddress) && cleanUsername.length < 3)) {
|
||||
} else if (!isMailAddress(cleanMailAddress, true) || (isTutaMailAddress(cleanMailAddress) && cleanUsername.length < 3)) {
|
||||
this.onValidationFinished(
|
||||
cleanMailAddress,
|
||||
{
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { NotAuthorizedError, NotFoundError } from "../../common/error/RestError"
|
||||
import type { Contact, ContactList } from "../../entities/tutanota/TypeRefs.js"
|
||||
import { ContactTypeRef } from "../../entities/tutanota/TypeRefs.js"
|
||||
import { typeModels as tutanotaModels } from "../../entities/tutanota/TypeModels"
|
||||
import type { Db, GroupData, IndexUpdate, SearchIndexEntry } from "./SearchTypes"
|
||||
import { _createNewIndexUpdate, typeRefToTypeInfo } from "./IndexUtils"
|
||||
import { NotAuthorizedError, NotFoundError } from "../../../common/api/common/error/RestError.js"
|
||||
import type { Contact, ContactList } from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { ContactTypeRef } from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { typeModels as tutanotaModels } from "../../../common/api/entities/tutanota/TypeModels.js"
|
||||
import type { Db, GroupData, IndexUpdate, SearchIndexEntry } from "../../../common/api/worker/search/SearchTypes.js"
|
||||
import { _createNewIndexUpdate, typeRefToTypeInfo } from "../../../common/api/worker/search/IndexUtils.js"
|
||||
import { neverNull, noOp, ofClass, promiseMap } from "@tutao/tutanota-utils"
|
||||
import { FULL_INDEXED_TIMESTAMP, OperationType } from "../../common/TutanotaConstants"
|
||||
import { IndexerCore } from "./IndexerCore"
|
||||
import { SuggestionFacade } from "./SuggestionFacade"
|
||||
import { FULL_INDEXED_TIMESTAMP, OperationType } from "../../../common/api/common/TutanotaConstants.js"
|
||||
import { IndexerCore } from "./IndexerCore.js"
|
||||
import { SuggestionFacade } from "./SuggestionFacade.js"
|
||||
import { tokenize } from "@tutao/tutanota-utils"
|
||||
import type { EntityUpdate } from "../../entities/sys/TypeRefs.js"
|
||||
import { EntityClient } from "../../common/EntityClient"
|
||||
import { GroupDataOS, MetaDataOS } from "./IndexTables.js"
|
||||
import type { EntityUpdate } from "../../../common/api/entities/sys/TypeRefs.js"
|
||||
import { EntityClient } from "../../../common/api/common/EntityClient.js"
|
||||
import { GroupDataOS, MetaDataOS } from "../../../common/api/worker/search/IndexTables.js"
|
||||
|
||||
export class ContactIndexer {
|
||||
_core: IndexerCore
|
|
@ -1,9 +1,15 @@
|
|||
import { ENTITY_EVENT_BATCH_TTL_DAYS, getMembershipGroupType, GroupType, NOTHING_INDEXED_TIMESTAMP, OperationType } from "../../common/TutanotaConstants"
|
||||
import { ConnectionError, NotAuthorizedError, NotFoundError } from "../../common/error/RestError"
|
||||
import type { EntityUpdate, GroupMembership, User } from "../../entities/sys/TypeRefs.js"
|
||||
import { EntityEventBatch, EntityEventBatchTypeRef, UserTypeRef } from "../../entities/sys/TypeRefs.js"
|
||||
import type { DatabaseEntry, DbKey, DbTransaction } from "./DbFacade"
|
||||
import { b64UserIdHash, DbFacade } from "./DbFacade"
|
||||
import {
|
||||
ENTITY_EVENT_BATCH_TTL_DAYS,
|
||||
getMembershipGroupType,
|
||||
GroupType,
|
||||
NOTHING_INDEXED_TIMESTAMP,
|
||||
OperationType,
|
||||
} from "../../../common/api/common/TutanotaConstants.js"
|
||||
import { ConnectionError, NotAuthorizedError, NotFoundError } from "../../../common/api/common/error/RestError.js"
|
||||
import type { EntityUpdate, GroupMembership, User } from "../../../common/api/entities/sys/TypeRefs.js"
|
||||
import { EntityEventBatch, EntityEventBatchTypeRef, UserTypeRef } from "../../../common/api/entities/sys/TypeRefs.js"
|
||||
import type { DatabaseEntry, DbKey, DbTransaction } from "../../../common/api/worker/search/DbFacade.js"
|
||||
import { b64UserIdHash, DbFacade } from "../../../common/api/worker/search/DbFacade.js"
|
||||
import {
|
||||
assertNotNull,
|
||||
contains,
|
||||
|
@ -22,27 +28,34 @@ import {
|
|||
promiseMap,
|
||||
TypeRef,
|
||||
} from "@tutao/tutanota-utils"
|
||||
import { firstBiggerThanSecond, GENERATED_MAX_ID, generatedIdToTimestamp, getElementId, isSameId, timestampToGeneratedId } from "../../common/utils/EntityUtils"
|
||||
import { _createNewIndexUpdate, filterIndexMemberships, markEnd, markStart, typeRefToTypeInfo } from "./IndexUtils"
|
||||
import type { Db, GroupData } from "./SearchTypes"
|
||||
import { IndexingErrorReason } from "./SearchTypes"
|
||||
import { ContactIndexer } from "./ContactIndexer"
|
||||
import { ContactList, ContactListTypeRef, ContactTypeRef, MailTypeRef } from "../../entities/tutanota/TypeRefs.js"
|
||||
import { MailIndexer } from "./MailIndexer"
|
||||
import { IndexerCore } from "./IndexerCore"
|
||||
import type { EntityRestClient } from "../rest/EntityRestClient"
|
||||
import { OutOfSyncError } from "../../common/error/OutOfSyncError"
|
||||
import { SuggestionFacade } from "./SuggestionFacade"
|
||||
import { DbError } from "../../common/error/DbError"
|
||||
import type { QueuedBatch } from "../EventQueue.js"
|
||||
import { EventQueue } from "../EventQueue.js"
|
||||
import { CancelledError } from "../../common/error/CancelledError"
|
||||
import { MembershipRemovedError } from "../../common/error/MembershipRemovedError"
|
||||
import type { BrowserData } from "../../../misc/ClientConstants"
|
||||
import { InvalidDatabaseStateError } from "../../common/error/InvalidDatabaseStateError"
|
||||
import { LocalTimeDateProvider } from "../DateProvider"
|
||||
import { EntityClient } from "../../common/EntityClient"
|
||||
import { deleteObjectStores } from "../utils/DbUtils"
|
||||
import {
|
||||
firstBiggerThanSecond,
|
||||
GENERATED_MAX_ID,
|
||||
generatedIdToTimestamp,
|
||||
getElementId,
|
||||
isSameId,
|
||||
timestampToGeneratedId,
|
||||
} from "../../../common/api/common/utils/EntityUtils.js"
|
||||
import { _createNewIndexUpdate, filterIndexMemberships, markEnd, markStart, typeRefToTypeInfo } from "../../../common/api/worker/search/IndexUtils.js"
|
||||
import type { Db, GroupData } from "../../../common/api/worker/search/SearchTypes.js"
|
||||
import { IndexingErrorReason } from "../../../common/api/worker/search/SearchTypes.js"
|
||||
import { ContactIndexer } from "./ContactIndexer.js"
|
||||
import { ContactList, ContactListTypeRef, ContactTypeRef, MailTypeRef } from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { MailIndexer } from "./MailIndexer.js"
|
||||
import { IndexerCore } from "./IndexerCore.js"
|
||||
import type { EntityRestClient } from "../../../common/api/worker/rest/EntityRestClient.js"
|
||||
import { OutOfSyncError } from "../../../common/api/common/error/OutOfSyncError.js"
|
||||
import { SuggestionFacade } from "./SuggestionFacade.js"
|
||||
import { DbError } from "../../../common/api/common/error/DbError.js"
|
||||
import type { QueuedBatch } from "../../../common/api/worker/EventQueue.js"
|
||||
import { EventQueue } from "../../../common/api/worker/EventQueue.js"
|
||||
import { CancelledError } from "../../../common/api/common/error/CancelledError.js"
|
||||
import { MembershipRemovedError } from "../../../common/api/common/error/MembershipRemovedError.js"
|
||||
import type { BrowserData } from "../../../common/misc/ClientConstants.js"
|
||||
import { InvalidDatabaseStateError } from "../../../common/api/common/error/InvalidDatabaseStateError.js"
|
||||
import { LocalTimeDateProvider } from "../../../common/api/worker/DateProvider.js"
|
||||
import { EntityClient } from "../../../common/api/common/EntityClient.js"
|
||||
import { deleteObjectStores } from "../../../common/api/worker/utils/DbUtils.js"
|
||||
import {
|
||||
aes256EncryptSearchIndexEntry,
|
||||
aes256RandomKey,
|
||||
|
@ -53,9 +66,9 @@ import {
|
|||
unauthenticatedAesDecrypt,
|
||||
BitArray,
|
||||
} from "@tutao/tutanota-crypto"
|
||||
import { DefaultEntityRestCache } from "../rest/DefaultEntityRestCache.js"
|
||||
import { CacheInfo } from "../facades/LoginFacade.js"
|
||||
import { InfoMessageHandler } from "../../../gui/InfoMessageHandler.js"
|
||||
import { DefaultEntityRestCache } from "../../../common/api/worker/rest/DefaultEntityRestCache.js"
|
||||
import { CacheInfo } from "../../../common/api/worker/facades/LoginFacade.js"
|
||||
import { InfoMessageHandler } from "../../../common/gui/InfoMessageHandler.js"
|
||||
import {
|
||||
ElementDataOS,
|
||||
EncryptedIndexerMetaData,
|
||||
|
@ -66,11 +79,11 @@ import {
|
|||
SearchIndexOS,
|
||||
SearchIndexWordsIndex,
|
||||
SearchTermSuggestionsOS,
|
||||
} from "./IndexTables.js"
|
||||
import { MailFacade } from "../facades/lazy/MailFacade.js"
|
||||
import { encryptKeyWithVersionedKey, VersionedKey } from "../crypto/CryptoFacade.js"
|
||||
import { KeyLoaderFacade } from "../facades/KeyLoaderFacade.js"
|
||||
import { getIndexerMetaData, updateEncryptionMetadata } from "../facades/lazy/ConfigurationDatabase.js"
|
||||
} from "../../../common/api/worker/search/IndexTables.js"
|
||||
import { MailFacade } from "../../../common/api/worker/facades/lazy/MailFacade.js"
|
||||
import { encryptKeyWithVersionedKey, VersionedKey } from "../../../common/api/worker/crypto/CryptoFacade.js"
|
||||
import { KeyLoaderFacade } from "../../../common/api/worker/facades/KeyLoaderFacade.js"
|
||||
import { getIndexerMetaData, updateEncryptionMetadata } from "../../../common/api/worker/facades/lazy/ConfigurationDatabase.js"
|
||||
|
||||
export type InitParams = {
|
||||
user: User
|
|
@ -1,4 +1,4 @@
|
|||
import type { DbTransaction } from "./DbFacade"
|
||||
import type { DbTransaction } from "../../../common/api/worker/search/DbFacade.js"
|
||||
import type { $Promisable, DeferredObject, PromiseMapFn } from "@tutao/tutanota-utils"
|
||||
import {
|
||||
arrayHash,
|
||||
|
@ -17,7 +17,7 @@ import {
|
|||
TypeRef,
|
||||
uint8ArrayToBase64,
|
||||
} from "@tutao/tutanota-utils"
|
||||
import { elementIdPart, firstBiggerThanSecond, generatedIdToTimestamp, listIdPart } from "../../common/utils/EntityUtils"
|
||||
import { elementIdPart, firstBiggerThanSecond, generatedIdToTimestamp, listIdPart } from "../../../common/api/common/utils/EntityUtils.js"
|
||||
import {
|
||||
compareMetaEntriesOldest,
|
||||
decryptIndexKey,
|
||||
|
@ -29,7 +29,7 @@ import {
|
|||
getIdFromEncSearchIndexEntry,
|
||||
getPerformanceTimestamp,
|
||||
typeRefToTypeInfo,
|
||||
} from "./IndexUtils"
|
||||
} from "../../../common/api/worker/search/IndexUtils.js"
|
||||
import type {
|
||||
AttributeHandler,
|
||||
B64EncIndexKey,
|
||||
|
@ -45,13 +45,13 @@ import type {
|
|||
SearchIndexMetaDataDbRow,
|
||||
SearchIndexMetadataEntry,
|
||||
SearchIndexMetaDataRow,
|
||||
} from "./SearchTypes"
|
||||
import type { QueuedBatch } from "../EventQueue.js"
|
||||
import { EventQueue } from "../EventQueue.js"
|
||||
import { CancelledError } from "../../common/error/CancelledError"
|
||||
import { ProgrammingError } from "../../common/error/ProgrammingError"
|
||||
import type { BrowserData } from "../../../misc/ClientConstants"
|
||||
import { InvalidDatabaseStateError } from "../../common/error/InvalidDatabaseStateError"
|
||||
} from "../../../common/api/worker/search/SearchTypes.js"
|
||||
import type { QueuedBatch } from "../../../common/api/worker/EventQueue.js"
|
||||
import { EventQueue } from "../../../common/api/worker/EventQueue.js"
|
||||
import { CancelledError } from "../../../common/api/common/error/CancelledError.js"
|
||||
import { ProgrammingError } from "../../../common/api/common/error/ProgrammingError.js"
|
||||
import type { BrowserData } from "../../../common/misc/ClientConstants.js"
|
||||
import { InvalidDatabaseStateError } from "../../../common/api/common/error/InvalidDatabaseStateError.js"
|
||||
import {
|
||||
appendBinaryBlocks,
|
||||
calculateNeededSpaceForNumbers,
|
||||
|
@ -59,10 +59,17 @@ import {
|
|||
encodeNumbers,
|
||||
iterateBinaryBlocks,
|
||||
removeBinaryBlockRanges,
|
||||
} from "./SearchIndexEncoding"
|
||||
import type { EntityUpdate } from "../../entities/sys/TypeRefs.js"
|
||||
} from "../../../common/api/worker/search/SearchIndexEncoding.js"
|
||||
import type { EntityUpdate } from "../../../common/api/entities/sys/TypeRefs.js"
|
||||
import { aes256EncryptSearchIndexEntry, aesDecrypt, unauthenticatedAesDecrypt } from "@tutao/tutanota-crypto"
|
||||
import { ElementDataOS, GroupDataOS, MetaDataOS, SearchIndexMetaDataOS, SearchIndexOS, SearchIndexWordsIndex } from "./IndexTables.js"
|
||||
import {
|
||||
ElementDataOS,
|
||||
GroupDataOS,
|
||||
MetaDataOS,
|
||||
SearchIndexMetaDataOS,
|
||||
SearchIndexOS,
|
||||
SearchIndexWordsIndex,
|
||||
} from "../../../common/api/worker/search/IndexTables.js"
|
||||
|
||||
const SEARCH_INDEX_ROW_LENGTH = 1000
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { FULL_INDEXED_TIMESTAMP, MailSetKind, MailState, NOTHING_INDEXED_TIMESTAMP, OperationType } from "../../common/TutanotaConstants"
|
||||
import type { File as TutanotaFile, Mail, MailBox, MailDetails, MailFolder } from "../../entities/tutanota/TypeRefs.js"
|
||||
import { FULL_INDEXED_TIMESTAMP, MailSetKind, MailState, NOTHING_INDEXED_TIMESTAMP, OperationType } from "../../../common/api/common/TutanotaConstants"
|
||||
import type { File as TutanotaFile, Mail, MailBox, MailDetails, MailFolder } from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import {
|
||||
FileTypeRef,
|
||||
MailboxGroupRootTypeRef,
|
||||
|
@ -8,9 +8,9 @@ import {
|
|||
MailDetailsDraftTypeRef,
|
||||
MailFolderTypeRef,
|
||||
MailTypeRef,
|
||||
} from "../../entities/tutanota/TypeRefs.js"
|
||||
import { ConnectionError, NotAuthorizedError, NotFoundError } from "../../common/error/RestError"
|
||||
import { typeModels } from "../../entities/tutanota/TypeModels"
|
||||
} from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { ConnectionError, NotAuthorizedError, NotFoundError } from "../../../common/api/common/error/RestError.js"
|
||||
import { typeModels } from "../../../common/api/entities/tutanota/TypeModels.js"
|
||||
import { assertNotNull, first, groupBy, groupByAndMap, isNotNull, neverNull, noOp, ofClass, promiseMap, splitInChunks, TypeRef } from "@tutao/tutanota-utils"
|
||||
import {
|
||||
elementIdPart,
|
||||
|
@ -21,29 +21,36 @@ import {
|
|||
LEGACY_TO_RECIPIENTS_ID,
|
||||
listIdPart,
|
||||
timestampToGeneratedId,
|
||||
} from "../../common/utils/EntityUtils"
|
||||
import { _createNewIndexUpdate, encryptIndexKeyBase64, filterMailMemberships, getPerformanceTimestamp, htmlToText, typeRefToTypeInfo } from "./IndexUtils"
|
||||
import { Db, GroupData, IndexingErrorReason, IndexUpdate, SearchIndexEntry } from "./SearchTypes"
|
||||
import { CancelledError } from "../../common/error/CancelledError"
|
||||
import { IndexerCore } from "./IndexerCore"
|
||||
import { DbError } from "../../common/error/DbError"
|
||||
import { DefaultEntityRestCache } from "../rest/DefaultEntityRestCache.js"
|
||||
import type { DateProvider } from "../DateProvider"
|
||||
import type { EntityUpdate, GroupMembership, User } from "../../entities/sys/TypeRefs.js"
|
||||
import { EntityRestClient, OwnerEncSessionKeyProvider } from "../rest/EntityRestClient"
|
||||
import { EntityClient } from "../../common/EntityClient"
|
||||
import { ProgressMonitor } from "../../common/utils/ProgressMonitor"
|
||||
import type { SomeEntity } from "../../common/EntityTypes"
|
||||
import { EphemeralCacheStorage } from "../rest/EphemeralCacheStorage"
|
||||
import { InfoMessageHandler } from "../../../gui/InfoMessageHandler.js"
|
||||
import { ElementDataOS, GroupDataOS, Metadata, MetaDataOS } from "./IndexTables.js"
|
||||
import { MailFacade } from "../facades/lazy/MailFacade.js"
|
||||
import { containsEventOfType, EntityUpdateData } from "../../common/utils/EntityUpdateUtils.js"
|
||||
import { b64UserIdHash } from "./DbFacade.js"
|
||||
import { hasError } from "../../common/utils/ErrorUtils.js"
|
||||
import { getDisplayedSender, getMailBodyText, MailAddressAndName } from "../../common/CommonMailUtils.js"
|
||||
} from "../../../common/api/common/utils/EntityUtils.js"
|
||||
import {
|
||||
_createNewIndexUpdate,
|
||||
encryptIndexKeyBase64,
|
||||
filterMailMemberships,
|
||||
getPerformanceTimestamp,
|
||||
htmlToText,
|
||||
typeRefToTypeInfo,
|
||||
} from "../../../common/api/worker/search/IndexUtils.js"
|
||||
import { Db, GroupData, IndexingErrorReason, IndexUpdate, SearchIndexEntry } from "../../../common/api/worker/search/SearchTypes.js"
|
||||
import { CancelledError } from "../../../common/api/common/error/CancelledError.js"
|
||||
import { IndexerCore } from "./IndexerCore.js"
|
||||
import { DbError } from "../../../common/api/common/error/DbError.js"
|
||||
import { DefaultEntityRestCache } from "../../../common/api/worker/rest/DefaultEntityRestCache.js"
|
||||
import type { DateProvider } from "../../../common/api/worker/DateProvider.js"
|
||||
import type { EntityUpdate, GroupMembership, User } from "../../../common/api/entities/sys/TypeRefs.js"
|
||||
import { EntityRestClient, OwnerEncSessionKeyProvider } from "../../../common/api/worker/rest/EntityRestClient.js"
|
||||
import { EntityClient } from "../../../common/api/common/EntityClient.js"
|
||||
import { ProgressMonitor } from "../../../common/api/common/utils/ProgressMonitor.js"
|
||||
import type { SomeEntity } from "../../../common/api/common/EntityTypes.js"
|
||||
import { EphemeralCacheStorage } from "../../../common/api/worker/rest/EphemeralCacheStorage.js"
|
||||
import { InfoMessageHandler } from "../../../common/gui/InfoMessageHandler.js"
|
||||
import { ElementDataOS, GroupDataOS, Metadata, MetaDataOS } from "../../../common/api/worker/search/IndexTables.js"
|
||||
import { MailFacade } from "../../../common/api/worker/facades/lazy/MailFacade.js"
|
||||
import { containsEventOfType, EntityUpdateData } from "../../../common/api/common/utils/EntityUpdateUtils.js"
|
||||
import { b64UserIdHash } from "../../../common/api/worker/search/DbFacade.js"
|
||||
import { hasError } from "../../../common/api/common/utils/ErrorUtils.js"
|
||||
import { getDisplayedSender, getMailBodyText, MailAddressAndName } from "../../../common/api/common/CommonMailUtils.js"
|
||||
|
||||
import { isDraft } from "../../../../mail-app/mail/model/MailUtils.js"
|
||||
import { isDraft } from "../../mail/model/MailChecks.js"
|
||||
|
||||
export const INITIAL_MAIL_INDEX_INTERVAL_DAYS = 28
|
||||
const ENTITY_INDEXER_CHUNK = 20
|
|
@ -1,6 +1,6 @@
|
|||
import { MailTypeRef } from "../../entities/tutanota/TypeRefs.js"
|
||||
import { DbTransaction } from "./DbFacade"
|
||||
import { resolveTypeReference } from "../../common/EntityFunctions"
|
||||
import { MailTypeRef } from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { DbTransaction } from "../../../common/api/worker/search/DbFacade.js"
|
||||
import { resolveTypeReference } from "../../../common/api/common/EntityFunctions.js"
|
||||
import {
|
||||
arrayHash,
|
||||
asyncFind,
|
||||
|
@ -36,8 +36,8 @@ import type {
|
|||
SearchIndexMetaDataRow,
|
||||
SearchRestriction,
|
||||
SearchResult,
|
||||
} from "./SearchTypes"
|
||||
import type { TypeInfo } from "./IndexUtils"
|
||||
} from "../../../common/api/worker/search/SearchTypes.js"
|
||||
import type { TypeInfo } from "../../../common/api/worker/search/IndexUtils.js"
|
||||
import {
|
||||
decryptMetaData,
|
||||
decryptSearchIndexEntry,
|
||||
|
@ -48,19 +48,19 @@ import {
|
|||
markStart,
|
||||
printMeasure,
|
||||
typeRefToTypeInfo,
|
||||
} from "./IndexUtils"
|
||||
import { FULL_INDEXED_TIMESTAMP, NOTHING_INDEXED_TIMESTAMP } from "../../common/TutanotaConstants"
|
||||
import { compareNewestFirst, elementIdPart, firstBiggerThanSecond, getListId, timestampToGeneratedId } from "../../common/utils/EntityUtils"
|
||||
import { INITIAL_MAIL_INDEX_INTERVAL_DAYS, MailIndexer } from "./MailIndexer"
|
||||
import { SuggestionFacade } from "./SuggestionFacade"
|
||||
import { AssociationType, Cardinality, ValueType } from "../../common/EntityConstants.js"
|
||||
import { NotAuthorizedError, NotFoundError } from "../../common/error/RestError"
|
||||
import { iterateBinaryBlocks } from "./SearchIndexEncoding"
|
||||
import type { BrowserData } from "../../../misc/ClientConstants"
|
||||
import type { TypeModel } from "../../common/EntityTypes"
|
||||
import { EntityClient } from "../../common/EntityClient"
|
||||
import { UserFacade } from "../facades/UserFacade"
|
||||
import { ElementDataOS, SearchIndexMetaDataOS, SearchIndexOS, SearchIndexWordsIndex } from "./IndexTables.js"
|
||||
} from "../../../common/api/worker/search/IndexUtils.js"
|
||||
import { FULL_INDEXED_TIMESTAMP, NOTHING_INDEXED_TIMESTAMP } from "../../../common/api/common/TutanotaConstants.js"
|
||||
import { compareNewestFirst, elementIdPart, firstBiggerThanSecond, getListId, timestampToGeneratedId } from "../../../common/api/common/utils/EntityUtils.js"
|
||||
import { INITIAL_MAIL_INDEX_INTERVAL_DAYS, MailIndexer } from "./MailIndexer.js"
|
||||
import { SuggestionFacade } from "./SuggestionFacade.js"
|
||||
import { AssociationType, Cardinality, ValueType } from "../../../common/api/common/EntityConstants.js"
|
||||
import { NotAuthorizedError, NotFoundError } from "../../../common/api/common/error/RestError.js"
|
||||
import { iterateBinaryBlocks } from "../../../common/api/worker/search/SearchIndexEncoding.js"
|
||||
import type { BrowserData } from "../../../common/misc/ClientConstants.js"
|
||||
import type { TypeModel } from "../../../common/api/common/EntityTypes.js"
|
||||
import { EntityClient } from "../../../common/api/common/EntityClient.js"
|
||||
import { UserFacade } from "../../../common/api/worker/facades/UserFacade.js"
|
||||
import { ElementDataOS, SearchIndexMetaDataOS, SearchIndexOS, SearchIndexWordsIndex } from "../../../common/api/worker/search/IndexTables.js"
|
||||
|
||||
type RowsToReadForIndexKey = {
|
||||
indexKey: string
|
|
@ -1,7 +1,7 @@
|
|||
import type { Db } from "./SearchTypes"
|
||||
import type { Db } from "../../../common/api/worker/search/SearchTypes.js"
|
||||
import { stringToUtf8Uint8Array, TypeRef, utf8Uint8ArrayToString } from "@tutao/tutanota-utils"
|
||||
import { aesDecrypt, aes256EncryptSearchIndexEntry, unauthenticatedAesDecrypt } from "@tutao/tutanota-crypto"
|
||||
import { SearchTermSuggestionsOS } from "./IndexTables.js"
|
||||
import { SearchTermSuggestionsOS } from "../../../common/api/worker/search/IndexTables.js"
|
||||
|
||||
export type SuggestionsType = Record<string, string[]>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { UserTypeRef } from "../../common/api/entities/sys/TypeRefs.js"
|
||||
import { AccountType, OFFLINE_STORAGE_DEFAULT_TIME_RANGE_DAYS } from "../../common/api/common/TutanotaConstants.js"
|
||||
import { UserTypeRef } from "../../../common/api/entities/sys/TypeRefs.js"
|
||||
import { AccountType, OFFLINE_STORAGE_DEFAULT_TIME_RANGE_DAYS } from "../../../common/api/common/TutanotaConstants.js"
|
||||
import { assertNotNull, DAY_IN_MILLIS, groupByAndMap } from "@tutao/tutanota-utils"
|
||||
import {
|
||||
constructMailSetEntryId,
|
||||
|
@ -10,7 +10,7 @@ import {
|
|||
getElementId,
|
||||
listIdPart,
|
||||
timestampToGeneratedId,
|
||||
} from "../../common/api/common/utils/EntityUtils.js"
|
||||
} from "../../../common/api/common/utils/EntityUtils.js"
|
||||
import {
|
||||
FileTypeRef,
|
||||
MailBoxTypeRef,
|
||||
|
@ -19,10 +19,10 @@ import {
|
|||
MailFolderTypeRef,
|
||||
MailSetEntryTypeRef,
|
||||
MailTypeRef,
|
||||
} from "../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { FolderSystem } from "../../common/api/common/mail/FolderSystem.js"
|
||||
import { isDraft, isSpamOrTrashFolder } from "../mail/model/MailUtils.js"
|
||||
import { OfflineStorage, OfflineStorageCleaner } from "../../common/api/worker/offline/OfflineStorage.js"
|
||||
} from "../../../common/api/entities/tutanota/TypeRefs.js"
|
||||
import { FolderSystem } from "../../../common/api/common/mail/FolderSystem.js"
|
||||
import { OfflineStorage, OfflineStorageCleaner } from "../../../common/api/worker/offline/OfflineStorage.js"
|
||||
import { isDraft, isSpamOrTrashFolder } from "../../mail/model/MailChecks.js"
|
||||
|
||||
export class MailOfflineCleaner implements OfflineStorageCleaner {
|
||||
async cleanOfflineDb(offlineStorage: OfflineStorage, timeRangeDays: number | null, userId: Id, now: number): Promise<void> {
|
|
@ -1,61 +1,46 @@
|
|||
import type { Commands } from "../common/threading/MessageDispatcher.js"
|
||||
import { errorToObj, MessageDispatcher, Request } from "../common/threading/MessageDispatcher.js"
|
||||
import { BookingFacade } from "./facades/lazy/BookingFacade.js"
|
||||
import { NotAuthenticatedError } from "../common/error/RestError"
|
||||
import { ProgrammingError } from "../common/error/ProgrammingError"
|
||||
import { initLocator, locator, resetLocator } from "./WorkerLocator"
|
||||
import { assertWorkerOrNode, isMainOrNode } from "../common/Env"
|
||||
import type { BrowserData } from "../../misc/ClientConstants"
|
||||
import { CryptoFacade } from "./crypto/CryptoFacade"
|
||||
import type { GiftCardFacade } from "./facades/lazy/GiftCardFacade.js"
|
||||
import type { LoginFacade, LoginListener } from "./facades/LoginFacade"
|
||||
import type { CustomerFacade } from "./facades/lazy/CustomerFacade.js"
|
||||
import type { GroupManagementFacade } from "./facades/lazy/GroupManagementFacade.js"
|
||||
import { ConfigurationDatabase } from "./facades/lazy/ConfigurationDatabase.js"
|
||||
import { CalendarFacade } from "./facades/lazy/CalendarFacade.js"
|
||||
import { MailFacade } from "./facades/lazy/MailFacade.js"
|
||||
import { ShareFacade } from "./facades/lazy/ShareFacade.js"
|
||||
import { CounterFacade } from "./facades/lazy/CounterFacade.js"
|
||||
import type { Indexer } from "./search/Indexer"
|
||||
import { SearchFacade } from "./search/SearchFacade"
|
||||
import { MailAddressFacade } from "./facades/lazy/MailAddressFacade.js"
|
||||
import { UserManagementFacade } from "./facades/lazy/UserManagementFacade.js"
|
||||
import { DelayedImpls, exposeLocalDelayed, exposeRemote } from "../common/WorkerProxy"
|
||||
import type { Commands } from "../../../common/api/common/threading/MessageDispatcher.js"
|
||||
import { errorToObj, MessageDispatcher, Request } from "../../../common/api/common/threading/MessageDispatcher.js"
|
||||
import { BookingFacade } from "../../../common/api/worker/facades/lazy/BookingFacade.js"
|
||||
import { NotAuthenticatedError } from "../../../common/api/common/error/RestError.js"
|
||||
import { ProgrammingError } from "../../../common/api/common/error/ProgrammingError.js"
|
||||
import { initLocator, locator, resetLocator } from "./WorkerLocator.js"
|
||||
import { assertWorkerOrNode, isMainOrNode } from "../../../common/api/common/Env.js"
|
||||
import type { BrowserData } from "../../../common/misc/ClientConstants.js"
|
||||
import { CryptoFacade } from "../../../common/api/worker/crypto/CryptoFacade.js"
|
||||
import type { GiftCardFacade } from "../../../common/api/worker/facades/lazy/GiftCardFacade.js"
|
||||
import type { LoginFacade } from "../../../common/api/worker/facades/LoginFacade.js"
|
||||
import type { CustomerFacade } from "../../../common/api/worker/facades/lazy/CustomerFacade.js"
|
||||
import type { GroupManagementFacade } from "../../../common/api/worker/facades/lazy/GroupManagementFacade.js"
|
||||
import { ConfigurationDatabase } from "../../../common/api/worker/facades/lazy/ConfigurationDatabase.js"
|
||||
import { CalendarFacade } from "../../../common/api/worker/facades/lazy/CalendarFacade.js"
|
||||
import { MailFacade } from "../../../common/api/worker/facades/lazy/MailFacade.js"
|
||||
import { ShareFacade } from "../../../common/api/worker/facades/lazy/ShareFacade.js"
|
||||
import { CounterFacade } from "../../../common/api/worker/facades/lazy/CounterFacade.js"
|
||||
import type { Indexer } from "../index/Indexer.js"
|
||||
import { SearchFacade } from "../index/SearchFacade.js"
|
||||
import { MailAddressFacade } from "../../../common/api/worker/facades/lazy/MailAddressFacade.js"
|
||||
import { UserManagementFacade } from "../../../common/api/worker/facades/lazy/UserManagementFacade.js"
|
||||
import { DelayedImpls, exposeLocalDelayed, exposeRemote } from "../../../common/api/common/WorkerProxy.js"
|
||||
import { random } from "@tutao/tutanota-crypto"
|
||||
import type { NativeInterface } from "../../native/common/NativeInterface"
|
||||
import type { EntityRestInterface } from "./rest/EntityRestClient"
|
||||
import { RestClient } from "./rest/RestClient"
|
||||
import { IServiceExecutor } from "../common/ServiceRequest.js"
|
||||
import { BlobFacade } from "./facades/lazy/BlobFacade.js"
|
||||
import { ExposedCacheStorage } from "./rest/DefaultEntityRestCache.js"
|
||||
import { BlobAccessTokenFacade } from "./facades/BlobAccessTokenFacade.js"
|
||||
import { WebsocketConnectivityListener } from "../../misc/WebsocketConnectivityModel.js"
|
||||
import { EventBusClient } from "./EventBusClient.js"
|
||||
import { EntropyFacade } from "./facades/EntropyFacade.js"
|
||||
import { ExposedProgressTracker } from "../main/ProgressTracker.js"
|
||||
import { ExposedEventController } from "../main/EventController.js"
|
||||
import { ExposedOperationProgressTracker } from "../main/OperationProgressTracker.js"
|
||||
import { WorkerFacade } from "./facades/WorkerFacade.js"
|
||||
import { InfoMessageHandler } from "../../gui/InfoMessageHandler.js"
|
||||
import { SqlCipherFacade } from "../../native/common/generatedipc/SqlCipherFacade.js"
|
||||
import { WebWorkerTransport } from "../common/threading/Transport.js"
|
||||
import type { NativeInterface } from "../../../common/native/common/NativeInterface.js"
|
||||
import type { EntityRestInterface } from "../../../common/api/worker/rest/EntityRestClient.js"
|
||||
import { RestClient } from "../../../common/api/worker/rest/RestClient.js"
|
||||
import { IServiceExecutor } from "../../../common/api/common/ServiceRequest.js"
|
||||
import { BlobFacade } from "../../../common/api/worker/facades/lazy/BlobFacade.js"
|
||||
import { ExposedCacheStorage } from "../../../common/api/worker/rest/DefaultEntityRestCache.js"
|
||||
import { BlobAccessTokenFacade } from "../../../common/api/worker/facades/BlobAccessTokenFacade.js"
|
||||
import { EntropyFacade } from "../../../common/api/worker/facades/EntropyFacade.js"
|
||||
import { WorkerFacade } from "../../../common/api/worker/facades/WorkerFacade.js"
|
||||
import { SqlCipherFacade } from "../../../common/native/common/generatedipc/SqlCipherFacade.js"
|
||||
import { WebWorkerTransport } from "../../../common/api/common/threading/Transport.js"
|
||||
import { ContactFacade } from "../../../common/api/worker/facades/lazy/ContactFacade.js"
|
||||
import { RecoverCodeFacade } from "../../../common/api/worker/facades/lazy/RecoverCodeFacade.js"
|
||||
import { CacheManagementFacade } from "../../../common/api/worker/facades/lazy/CacheManagementFacade.js"
|
||||
import { ExposedEventBus, MainInterface, WorkerRandomizer } from "../../../common/api/worker/workerInterfaces.js"
|
||||
import { CryptoError } from "@tutao/tutanota-crypto/error.js"
|
||||
import { ContactFacade } from "./facades/lazy/ContactFacade.js"
|
||||
import { RecoverCodeFacade } from "./facades/lazy/RecoverCodeFacade.js"
|
||||
import { CacheManagementFacade } from "./facades/lazy/CacheManagementFacade.js"
|
||||
import { OfflineStorageCleaner } from "./offline/OfflineStorage.js"
|
||||
|
||||
assertWorkerOrNode()
|
||||
|
||||
export interface WorkerRandomizer {
|
||||
generateRandomNumber(numBytes: number): Promise<number>
|
||||
}
|
||||
|
||||
export interface ExposedEventBus {
|
||||
tryReconnect: EventBusClient["tryReconnect"]
|
||||
close: EventBusClient["close"]
|
||||
}
|
||||
|
||||
/** Interface of the facades exposed by the worker, basically interface for the worker itself */
|
||||
export interface WorkerInterface {
|
||||
readonly loginFacade: LoginFacade
|
||||
|
@ -88,16 +73,6 @@ export interface WorkerInterface {
|
|||
readonly contactFacade: ContactFacade
|
||||
}
|
||||
|
||||
/** Interface for the "main"/webpage context of the app, interface for the worker client. */
|
||||
export interface MainInterface {
|
||||
readonly loginListener: LoginListener
|
||||
readonly wsConnectivityListener: WebsocketConnectivityListener
|
||||
readonly progressTracker: ExposedProgressTracker
|
||||
readonly eventController: ExposedEventController
|
||||
readonly operationProgressTracker: ExposedOperationProgressTracker
|
||||
readonly infoMessageHandler: InfoMessageHandler
|
||||
}
|
||||
|
||||
type WorkerRequest = Request<WorkerRequestType>
|
||||
|
||||
export class WorkerImpl implements NativeInterface {
|
||||
|
@ -109,7 +84,7 @@ export class WorkerImpl implements NativeInterface {
|
|||
this._dispatcher = new MessageDispatcher(new WebWorkerTransport(this._scope), this.queueCommands(this.exposedInterface), "worker-main")
|
||||
}
|
||||
|
||||
async init(browserData: BrowserData, offlineStorageCleaner: OfflineStorageCleaner): Promise<void> {
|
||||
async init(browserData: BrowserData): Promise<void> {
|
||||
// import("tuta-sdk").then(async (module) => {
|
||||
// // await module.default("wasm/tutasdk.wasm")
|
||||
// const entityClient = new module.EntityClient()
|
||||
|
@ -119,7 +94,7 @@ export class WorkerImpl implements NativeInterface {
|
|||
// entityClient.free()
|
||||
// })
|
||||
|
||||
await initLocator(this, browserData, offlineStorageCleaner)
|
||||
await initLocator(this, browserData)
|
||||
const workerScope = this._scope
|
||||
|
||||
// only register oncaught error handler if we are in the *real* worker scope
|
|
@ -1,79 +1,90 @@
|
|||
import { CacheInfo, LoginFacade, LoginListener } from "./facades/LoginFacade"
|
||||
import type { WorkerImpl } from "./WorkerImpl"
|
||||
import type { Indexer } from "./search/Indexer"
|
||||
import type { EntityRestInterface } from "./rest/EntityRestClient"
|
||||
import { EntityRestClient } from "./rest/EntityRestClient"
|
||||
import type { UserManagementFacade } from "./facades/lazy/UserManagementFacade.js"
|
||||
import { CacheStorage, DefaultEntityRestCache } from "./rest/DefaultEntityRestCache.js"
|
||||
import type { GroupManagementFacade } from "./facades/lazy/GroupManagementFacade.js"
|
||||
import type { MailFacade } from "./facades/lazy/MailFacade.js"
|
||||
import type { MailAddressFacade } from "./facades/lazy/MailAddressFacade.js"
|
||||
import type { CustomerFacade } from "./facades/lazy/CustomerFacade.js"
|
||||
import type { CounterFacade } from "./facades/lazy/CounterFacade.js"
|
||||
import { EventBusClient } from "./EventBusClient"
|
||||
import { assertWorkerOrNode, getWebsocketBaseUrl, isAdminClient, isAndroidApp, isBrowser, isIOSApp, isOfflineStorageAvailable, isTest } from "../common/Env"
|
||||
import { Const } from "../common/TutanotaConstants"
|
||||
import type { BrowserData } from "../../misc/ClientConstants"
|
||||
import type { CalendarFacade } from "./facades/lazy/CalendarFacade.js"
|
||||
import type { ShareFacade } from "./facades/lazy/ShareFacade.js"
|
||||
import { RestClient } from "./rest/RestClient"
|
||||
import { SuspensionHandler } from "./SuspensionHandler"
|
||||
import { EntityClient } from "../common/EntityClient"
|
||||
import type { GiftCardFacade } from "./facades/lazy/GiftCardFacade.js"
|
||||
import type { ConfigurationDatabase } from "./facades/lazy/ConfigurationDatabase.js"
|
||||
import { DeviceEncryptionFacade } from "./facades/DeviceEncryptionFacade"
|
||||
import type { NativeInterface } from "../../native/common/NativeInterface"
|
||||
import { NativeFileApp } from "../../native/common/FileApp"
|
||||
import { AesApp } from "../../native/worker/AesApp"
|
||||
import type { RsaImplementation } from "./crypto/RsaImplementation"
|
||||
import { createRsaImplementation } from "./crypto/RsaImplementation"
|
||||
import { CryptoFacade } from "./crypto/CryptoFacade"
|
||||
import { InstanceMapper } from "./crypto/InstanceMapper"
|
||||
import { AdminClientDummyEntityRestCache } from "./rest/AdminClientDummyEntityRestCache.js"
|
||||
import { SleepDetector } from "./utils/SleepDetector.js"
|
||||
import { SchedulerImpl } from "../common/utils/Scheduler.js"
|
||||
import { NoZoneDateProvider } from "../common/utils/NoZoneDateProvider.js"
|
||||
import { LateInitializedCacheStorageImpl } from "./rest/CacheStorageProxy"
|
||||
import { IServiceExecutor } from "../common/ServiceRequest"
|
||||
import { ServiceExecutor } from "./rest/ServiceExecutor"
|
||||
import type { BookingFacade } from "./facades/lazy/BookingFacade.js"
|
||||
import type { BlobFacade } from "./facades/lazy/BlobFacade.js"
|
||||
import { UserFacade } from "./facades/UserFacade"
|
||||
import { OfflineStorage, OfflineStorageCleaner } from "./offline/OfflineStorage.js"
|
||||
import { OFFLINE_STORAGE_MIGRATIONS, OfflineStorageMigrator } from "./offline/OfflineStorageMigrator.js"
|
||||
import { modelInfos } from "../common/EntityFunctions.js"
|
||||
import { FileFacadeSendDispatcher } from "../../native/common/generatedipc/FileFacadeSendDispatcher.js"
|
||||
import { NativePushFacadeSendDispatcher } from "../../native/common/generatedipc/NativePushFacadeSendDispatcher.js"
|
||||
import { NativeCryptoFacadeSendDispatcher } from "../../native/common/generatedipc/NativeCryptoFacadeSendDispatcher"
|
||||
import { CacheInfo, LoginFacade, LoginListener } from "../../../common/api/worker/facades/LoginFacade.js"
|
||||
import type { WorkerImpl } from "./WorkerImpl.js"
|
||||
import type { Indexer } from "../index/Indexer.js"
|
||||
import type { EntityRestInterface } from "../../../common/api/worker/rest/EntityRestClient.js"
|
||||
import { EntityRestClient } from "../../../common/api/worker/rest/EntityRestClient.js"
|
||||
import type { UserManagementFacade } from "../../../common/api/worker/facades/lazy/UserManagementFacade.js"
|
||||
import { CacheStorage, DefaultEntityRestCache } from "../../../common/api/worker/rest/DefaultEntityRestCache.js"
|
||||
import type { GroupManagementFacade } from "../../../common/api/worker/facades/lazy/GroupManagementFacade.js"
|
||||
import type { MailFacade } from "../../../common/api/worker/facades/lazy/MailFacade.js"
|
||||
import type { MailAddressFacade } from "../../../common/api/worker/facades/lazy/MailAddressFacade.js"
|
||||
import type { CustomerFacade } from "../../../common/api/worker/facades/lazy/CustomerFacade.js"
|
||||
import type { CounterFacade } from "../../../common/api/worker/facades/lazy/CounterFacade.js"
|
||||
import { EventBusClient } from "../../../common/api/worker/EventBusClient.js"
|
||||
import {
|
||||
assertWorkerOrNode,
|
||||
getWebsocketBaseUrl,
|
||||
isAdminClient,
|
||||
isAndroidApp,
|
||||
isBrowser,
|
||||
isIOSApp,
|
||||
isOfflineStorageAvailable,
|
||||
isTest,
|
||||
} from "../../../common/api/common/Env.js"
|
||||
import { Const } from "../../../common/api/common/TutanotaConstants.js"
|
||||
import type { BrowserData } from "../../../common/misc/ClientConstants.js"
|
||||
import type { CalendarFacade } from "../../../common/api/worker/facades/lazy/CalendarFacade.js"
|
||||
import type { ShareFacade } from "../../../common/api/worker/facades/lazy/ShareFacade.js"
|
||||
import { RestClient } from "../../../common/api/worker/rest/RestClient.js"
|
||||
import { SuspensionHandler } from "../../../common/api/worker/SuspensionHandler.js"
|
||||
import { EntityClient } from "../../../common/api/common/EntityClient.js"
|
||||
import type { GiftCardFacade } from "../../../common/api/worker/facades/lazy/GiftCardFacade.js"
|
||||
import type { ConfigurationDatabase } from "../../../common/api/worker/facades/lazy/ConfigurationDatabase.js"
|
||||
import { DeviceEncryptionFacade } from "../../../common/api/worker/facades/DeviceEncryptionFacade.js"
|
||||
import type { NativeInterface } from "../../../common/native/common/NativeInterface.js"
|
||||
import { NativeFileApp } from "../../../common/native/common/FileApp.js"
|
||||
import { AesApp } from "../../../common/native/worker/AesApp.js"
|
||||
import type { RsaImplementation } from "../../../common/api/worker/crypto/RsaImplementation.js"
|
||||
import { createRsaImplementation } from "../../../common/api/worker/crypto/RsaImplementation.js"
|
||||
import { CryptoFacade } from "../../../common/api/worker/crypto/CryptoFacade.js"
|
||||
import { InstanceMapper } from "../../../common/api/worker/crypto/InstanceMapper.js"
|
||||
import { AdminClientDummyEntityRestCache } from "../../../common/api/worker/rest/AdminClientDummyEntityRestCache.js"
|
||||
import { SleepDetector } from "../../../common/api/worker/utils/SleepDetector.js"
|
||||
import { SchedulerImpl } from "../../../common/api/common/utils/Scheduler.js"
|
||||
import { NoZoneDateProvider } from "../../../common/api/common/utils/NoZoneDateProvider.js"
|
||||
import { LateInitializedCacheStorageImpl } from "../../../common/api/worker/rest/CacheStorageProxy.js"
|
||||
import { IServiceExecutor } from "../../../common/api/common/ServiceRequest.js"
|
||||
import { ServiceExecutor } from "../../../common/api/worker/rest/ServiceExecutor.js"
|
||||
import type { BookingFacade } from "../../../common/api/worker/facades/lazy/BookingFacade.js"
|
||||
import type { BlobFacade } from "../../../common/api/worker/facades/lazy/BlobFacade.js"
|
||||
import { UserFacade } from "../../../common/api/worker/facades/UserFacade.js"
|
||||
import { OfflineStorage, OfflineStorageCleaner } from "../../../common/api/worker/offline/OfflineStorage.js"
|
||||
import { OFFLINE_STORAGE_MIGRATIONS, OfflineStorageMigrator } from "../../../common/api/worker/offline/OfflineStorageMigrator.js"
|
||||
import { modelInfos } from "../../../common/api/common/EntityFunctions.js"
|
||||
import { FileFacadeSendDispatcher } from "../../../common/native/common/generatedipc/FileFacadeSendDispatcher.js"
|
||||
import { NativePushFacadeSendDispatcher } from "../../../common/native/common/generatedipc/NativePushFacadeSendDispatcher.js"
|
||||
import { NativeCryptoFacadeSendDispatcher } from "../../../common/native/common/generatedipc/NativeCryptoFacadeSendDispatcher.js"
|
||||
import { random } from "@tutao/tutanota-crypto"
|
||||
import { ExportFacadeSendDispatcher } from "../../native/common/generatedipc/ExportFacadeSendDispatcher.js"
|
||||
import { ExportFacadeSendDispatcher } from "../../../common/native/common/generatedipc/ExportFacadeSendDispatcher.js"
|
||||
import { assertNotNull, delay, lazyAsync, lazyMemoized } from "@tutao/tutanota-utils"
|
||||
import { InterWindowEventFacadeSendDispatcher } from "../../native/common/generatedipc/InterWindowEventFacadeSendDispatcher.js"
|
||||
import { SqlCipherFacadeSendDispatcher } from "../../native/common/generatedipc/SqlCipherFacadeSendDispatcher.js"
|
||||
import { EntropyFacade } from "./facades/EntropyFacade.js"
|
||||
import { BlobAccessTokenFacade } from "./facades/BlobAccessTokenFacade.js"
|
||||
import { OwnerEncSessionKeysUpdateQueue } from "./crypto/OwnerEncSessionKeysUpdateQueue.js"
|
||||
import { EventBusEventCoordinator } from "./EventBusEventCoordinator.js"
|
||||
import { WorkerFacade } from "./facades/WorkerFacade.js"
|
||||
import { SqlCipherFacade } from "../../native/common/generatedipc/SqlCipherFacade.js"
|
||||
import type { SearchFacade } from "./search/SearchFacade.js"
|
||||
import { Challenge } from "../entities/sys/TypeRefs.js"
|
||||
import { LoginFailReason } from "../main/PageContextLoginListener.js"
|
||||
import { ConnectionError, ServiceUnavailableError } from "../common/error/RestError.js"
|
||||
import { SessionType } from "../common/SessionType.js"
|
||||
import { Argon2idFacade, NativeArgon2idFacade, WASMArgon2idFacade } from "./facades/Argon2idFacade.js"
|
||||
import { DomainConfigProvider } from "../common/DomainConfigProvider.js"
|
||||
import { KyberFacade, NativeKyberFacade, WASMKyberFacade } from "./facades/KyberFacade.js"
|
||||
import { PQFacade } from "./facades/PQFacade.js"
|
||||
import { PdfWriter } from "./pdf/PdfWriter.js"
|
||||
import { ContactFacade } from "./facades/lazy/ContactFacade.js"
|
||||
import { KeyLoaderFacade } from "./facades/KeyLoaderFacade.js"
|
||||
import { KeyRotationFacade } from "./facades/KeyRotationFacade.js"
|
||||
import { KeyCache } from "./facades/KeyCache.js"
|
||||
import { cryptoWrapper } from "./crypto/CryptoWrapper.js"
|
||||
import { RecoverCodeFacade } from "./facades/lazy/RecoverCodeFacade.js"
|
||||
import { CacheManagementFacade } from "./facades/lazy/CacheManagementFacade.js"
|
||||
import type { Credentials } from "../../misc/credentials/Credentials.js"
|
||||
import { InterWindowEventFacadeSendDispatcher } from "../../../common/native/common/generatedipc/InterWindowEventFacadeSendDispatcher.js"
|
||||
import { SqlCipherFacadeSendDispatcher } from "../../../common/native/common/generatedipc/SqlCipherFacadeSendDispatcher.js"
|
||||
import { EntropyFacade } from "../../../common/api/worker/facades/EntropyFacade.js"
|
||||
import { BlobAccessTokenFacade } from "../../../common/api/worker/facades/BlobAccessTokenFacade.js"
|
||||
import { OwnerEncSessionKeysUpdateQueue } from "../../../common/api/worker/crypto/OwnerEncSessionKeysUpdateQueue.js"
|
||||
import { EventBusEventCoordinator } from "../../../common/api/worker/EventBusEventCoordinator.js"
|
||||
import { WorkerFacade } from "../../../common/api/worker/facades/WorkerFacade.js"
|
||||
import { SqlCipherFacade } from "../../../common/native/common/generatedipc/SqlCipherFacade.js"
|
||||
import type { SearchFacade } from "../index/SearchFacade.js"
|
||||
import { Challenge } from "../../../common/api/entities/sys/TypeRefs.js"
|
||||
import { LoginFailReason } from "../../../common/api/main/PageContextLoginListener.js"
|
||||
import { ConnectionError, ServiceUnavailableError } from "../../../common/api/common/error/RestError.js"
|
||||
import { SessionType } from "../../../common/api/common/SessionType.js"
|
||||
import { Argon2idFacade, NativeArgon2idFacade, WASMArgon2idFacade } from "../../../common/api/worker/facades/Argon2idFacade.js"
|
||||
import { DomainConfigProvider } from "../../../common/api/common/DomainConfigProvider.js"
|
||||
import { KyberFacade, NativeKyberFacade, WASMKyberFacade } from "../../../common/api/worker/facades/KyberFacade.js"
|
||||
import { PQFacade } from "../../../common/api/worker/facades/PQFacade.js"
|
||||
import { PdfWriter } from "../../../common/api/worker/pdf/PdfWriter.js"
|
||||
import { ContactFacade } from "../../../common/api/worker/facades/lazy/ContactFacade.js"
|
||||
import { KeyLoaderFacade } from "../../../common/api/worker/facades/KeyLoaderFacade.js"
|
||||
import { KeyRotationFacade } from "../../../common/api/worker/facades/KeyRotationFacade.js"
|
||||
import { KeyCache } from "../../../common/api/worker/facades/KeyCache.js"
|
||||
import { cryptoWrapper } from "../../../common/api/worker/crypto/CryptoWrapper.js"
|
||||
import { RecoverCodeFacade } from "../../../common/api/worker/facades/lazy/RecoverCodeFacade.js"
|
||||
import { CacheManagementFacade } from "../../../common/api/worker/facades/lazy/CacheManagementFacade.js"
|
||||
import { MailOfflineCleaner } from "../offline/MailOfflineCleaner.js"
|
||||
import type { QueuedBatch } from "../../../common/api/worker/EventQueue.js"
|
||||
import { Credentials } from "../../../common/misc/credentials/Credentials.js"
|
||||
|
||||
assertWorkerOrNode()
|
||||
|
||||
|
@ -131,15 +142,17 @@ export type WorkerLocatorType = {
|
|||
pdfWriter: lazyAsync<PdfWriter>
|
||||
|
||||
// used to cache between resets
|
||||
_worker: WorkerImpl
|
||||
_browserData: BrowserData
|
||||
_offlineStorageCleaner: OfflineStorageCleaner
|
||||
|
||||
//contact
|
||||
contactFacade: lazyAsync<ContactFacade>
|
||||
}
|
||||
export const locator: WorkerLocatorType = {} as any
|
||||
|
||||
export async function initLocator(worker: WorkerImpl, browserData: BrowserData, offlineStorageCleaner: OfflineStorageCleaner) {
|
||||
export async function initLocator(worker: WorkerImpl, browserData: BrowserData) {
|
||||
locator._worker = worker
|
||||
locator._browserData = browserData
|
||||
locator.keyCache = new KeyCache()
|
||||
locator.user = new UserFacade(locator.keyCache)
|
||||
locator.workerFacade = new WorkerFacade()
|
||||
|
@ -158,12 +171,10 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
locator.entropyFacade = new EntropyFacade(locator.user, locator.serviceExecutor, random, () => locator.keyLoader)
|
||||
locator.blobAccessToken = new BlobAccessTokenFacade(locator.serviceExecutor, dateProvider, locator.user)
|
||||
const entityRestClient = new EntityRestClient(locator.user, locator.restClient, () => locator.crypto, locator.instanceMapper, locator.blobAccessToken)
|
||||
locator._browserData = browserData
|
||||
locator._offlineStorageCleaner = offlineStorageCleaner
|
||||
|
||||
locator.native = worker
|
||||
locator.booking = lazyMemoized(async () => {
|
||||
const { BookingFacade } = await import("./facades/lazy/BookingFacade.js")
|
||||
const { BookingFacade } = await import("../../../common/api/worker/facades/lazy/BookingFacade.js")
|
||||
return new BookingFacade(locator.serviceExecutor)
|
||||
})
|
||||
|
||||
|
@ -176,18 +187,20 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
new InterWindowEventFacadeSendDispatcher(worker),
|
||||
dateProvider,
|
||||
new OfflineStorageMigrator(OFFLINE_STORAGE_MIGRATIONS, modelInfos),
|
||||
offlineStorageCleaner,
|
||||
new MailOfflineCleaner(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
offlineStorageProvider = async () => null
|
||||
}
|
||||
locator.pdfWriter = async () => {
|
||||
const { PdfWriter } = await import("./pdf/PdfWriter.js")
|
||||
const { PdfWriter } = await import("../../../common/api/worker/pdf/PdfWriter.js")
|
||||
return new PdfWriter(new TextEncoder(), undefined)
|
||||
}
|
||||
|
||||
const maybeUninitializedStorage = new LateInitializedCacheStorageImpl(worker, offlineStorageProvider)
|
||||
const maybeUninitializedStorage = new LateInitializedCacheStorageImpl(async (error: Error) => {
|
||||
await worker.sendError(error)
|
||||
}, offlineStorageProvider)
|
||||
|
||||
locator.cacheStorage = maybeUninitializedStorage
|
||||
|
||||
|
@ -205,12 +218,12 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
const nonCachingEntityClient = new EntityClient(entityRestClient)
|
||||
|
||||
locator.cacheManagement = lazyMemoized(async () => {
|
||||
const { CacheManagementFacade } = await import("./facades/lazy/CacheManagementFacade.js")
|
||||
const { CacheManagementFacade } = await import("../../../common/api/worker/facades/lazy/CacheManagementFacade.js")
|
||||
return new CacheManagementFacade(locator.user, locator.cachingEntityClient, assertNotNull(cache))
|
||||
})
|
||||
|
||||
locator.indexer = lazyMemoized(async () => {
|
||||
const { Indexer } = await import("./search/Indexer.js")
|
||||
const { Indexer } = await import("../index/Indexer.js")
|
||||
return new Indexer(entityRestClient, mainInterface.infoMessageHandler, browserData, locator.cache as DefaultEntityRestCache, await locator.mail())
|
||||
})
|
||||
|
||||
|
@ -238,19 +251,19 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
)
|
||||
|
||||
locator.recoverCode = lazyMemoized(async () => {
|
||||
const { RecoverCodeFacade } = await import("./facades/lazy/RecoverCodeFacade.js")
|
||||
const { RecoverCodeFacade } = await import("../../../common/api/worker/facades/lazy/RecoverCodeFacade.js")
|
||||
return new RecoverCodeFacade(locator.user, locator.cachingEntityClient, locator.login, locator.keyLoader)
|
||||
})
|
||||
locator.share = lazyMemoized(async () => {
|
||||
const { ShareFacade } = await import("./facades/lazy/ShareFacade.js")
|
||||
const { ShareFacade } = await import("../../../common/api/worker/facades/lazy/ShareFacade.js")
|
||||
return new ShareFacade(locator.user, locator.crypto, locator.serviceExecutor, locator.cachingEntityClient, locator.keyLoader)
|
||||
})
|
||||
locator.counters = lazyMemoized(async () => {
|
||||
const { CounterFacade } = await import("./facades/lazy/CounterFacade.js")
|
||||
const { CounterFacade } = await import("../../../common/api/worker/facades/lazy/CounterFacade.js")
|
||||
return new CounterFacade(locator.serviceExecutor)
|
||||
})
|
||||
locator.groupManagement = lazyMemoized(async () => {
|
||||
const { GroupManagementFacade } = await import("./facades/lazy/GroupManagementFacade.js")
|
||||
const { GroupManagementFacade } = await import("../../../common/api/worker/facades/lazy/GroupManagementFacade.js")
|
||||
return new GroupManagementFacade(
|
||||
locator.user,
|
||||
await locator.counters(),
|
||||
|
@ -303,10 +316,9 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
}
|
||||
|
||||
locator.deviceEncryptionFacade = new DeviceEncryptionFacade()
|
||||
const { DatabaseKeyFactory } = await import("../../misc/credentials/DatabaseKeyFactory.js")
|
||||
const { DatabaseKeyFactory } = await import("../../../common/misc/credentials/DatabaseKeyFactory.js")
|
||||
|
||||
locator.login = new LoginFacade(
|
||||
worker,
|
||||
locator.restClient,
|
||||
/**
|
||||
* we don't want to try to use the cache in the login facade, because it may not be available (when no user is logged in)
|
||||
|
@ -324,16 +336,19 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
new DatabaseKeyFactory(locator.deviceEncryptionFacade),
|
||||
argon2idFacade,
|
||||
nonCachingEntityClient,
|
||||
async (error: Error) => {
|
||||
await worker.sendError(error)
|
||||
},
|
||||
)
|
||||
|
||||
locator.search = lazyMemoized(async () => {
|
||||
const { SearchFacade } = await import("./search/SearchFacade.js")
|
||||
const { SearchFacade } = await import("../index/SearchFacade.js")
|
||||
const indexer = await locator.indexer()
|
||||
const suggestionFacades = [indexer._contact.suggestionFacade]
|
||||
return new SearchFacade(locator.user, indexer.db, indexer._mail, suggestionFacades, browserData, locator.cachingEntityClient)
|
||||
})
|
||||
locator.userManagement = lazyMemoized(async () => {
|
||||
const { UserManagementFacade } = await import("./facades/lazy/UserManagementFacade.js")
|
||||
const { UserManagementFacade } = await import("../../../common/api/worker/facades/lazy/UserManagementFacade.js")
|
||||
return new UserManagementFacade(
|
||||
locator.user,
|
||||
await locator.groupManagement(),
|
||||
|
@ -348,7 +363,7 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
)
|
||||
})
|
||||
locator.customer = lazyMemoized(async () => {
|
||||
const { CustomerFacade } = await import("./facades/lazy/CustomerFacade.js")
|
||||
const { CustomerFacade } = await import("../../../common/api/worker/facades/lazy/CustomerFacade.js")
|
||||
return new CustomerFacade(
|
||||
locator.user,
|
||||
await locator.groupManagement(),
|
||||
|
@ -368,7 +383,7 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
})
|
||||
const aesApp = new AesApp(new NativeCryptoFacadeSendDispatcher(worker), random)
|
||||
locator.blob = lazyMemoized(async () => {
|
||||
const { BlobFacade } = await import("./facades/lazy/BlobFacade.js")
|
||||
const { BlobFacade } = await import("../../../common/api/worker/facades/lazy/BlobFacade.js")
|
||||
return new BlobFacade(
|
||||
locator.user,
|
||||
locator.serviceExecutor,
|
||||
|
@ -383,7 +398,7 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
)
|
||||
})
|
||||
locator.mail = lazyMemoized(async () => {
|
||||
const { MailFacade } = await import("./facades/lazy/MailFacade.js")
|
||||
const { MailFacade } = await import("../../../common/api/worker/facades/lazy/MailFacade.js")
|
||||
return new MailFacade(
|
||||
locator.user,
|
||||
locator.cachingEntityClient,
|
||||
|
@ -397,7 +412,7 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
})
|
||||
const nativePushFacade = new NativePushFacadeSendDispatcher(worker)
|
||||
locator.calendar = lazyMemoized(async () => {
|
||||
const { CalendarFacade } = await import("./facades/lazy/CalendarFacade.js")
|
||||
const { CalendarFacade } = await import("../../../common/api/worker/facades/lazy/CalendarFacade.js")
|
||||
return new CalendarFacade(
|
||||
locator.user,
|
||||
await locator.groupManagement(),
|
||||
|
@ -413,7 +428,7 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
})
|
||||
|
||||
locator.mailAddress = lazyMemoized(async () => {
|
||||
const { MailAddressFacade } = await import("./facades/lazy/MailAddressFacade.js")
|
||||
const { MailAddressFacade } = await import("../../../common/api/worker/facades/lazy/MailAddressFacade.js")
|
||||
return new MailAddressFacade(
|
||||
locator.user,
|
||||
await locator.groupManagement(),
|
||||
|
@ -424,21 +439,27 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
const scheduler = new SchedulerImpl(dateProvider, self, self)
|
||||
|
||||
locator.configFacade = lazyMemoized(async () => {
|
||||
const { ConfigurationDatabase } = await import("./facades/lazy/ConfigurationDatabase.js")
|
||||
const { ConfigurationDatabase } = await import("../../../common/api/worker/facades/lazy/ConfigurationDatabase.js")
|
||||
return new ConfigurationDatabase(locator.keyLoader, locator.user)
|
||||
})
|
||||
|
||||
const eventBusCoordinator = new EventBusEventCoordinator(
|
||||
worker,
|
||||
mainInterface.wsConnectivityListener,
|
||||
locator.mail,
|
||||
locator.indexer,
|
||||
locator.user,
|
||||
locator.cachingEntityClient,
|
||||
mainInterface.eventController,
|
||||
locator.configFacade,
|
||||
locator.keyRotation,
|
||||
locator.cacheManagement,
|
||||
async (error: Error) => {
|
||||
await worker.sendError(error)
|
||||
},
|
||||
async (queuedBatch: QueuedBatch[]) => {
|
||||
const indexer = await locator.indexer()
|
||||
indexer.addBatchesToQueue(queuedBatch)
|
||||
indexer.startProcessing()
|
||||
},
|
||||
)
|
||||
|
||||
locator.eventBusClient = new EventBusClient(
|
||||
|
@ -454,11 +475,11 @@ export async function initLocator(worker: WorkerImpl, browserData: BrowserData,
|
|||
locator.login.init(locator.eventBusClient)
|
||||
locator.Const = Const
|
||||
locator.giftCards = lazyMemoized(async () => {
|
||||
const { GiftCardFacade } = await import("./facades/lazy/GiftCardFacade.js")
|
||||
const { GiftCardFacade } = await import("../../../common/api/worker/facades/lazy/GiftCardFacade.js")
|
||||
return new GiftCardFacade(locator.user, await locator.customer(), locator.serviceExecutor, locator.crypto, locator.keyLoader)
|
||||
})
|
||||
locator.contactFacade = lazyMemoized(async () => {
|
||||
const { ContactFacade } = await import("./facades/lazy/ContactFacade.js")
|
||||
const { ContactFacade } = await import("../../../common/api/worker/facades/lazy/ContactFacade.js")
|
||||
return new ContactFacade(new EntityClient(locator.cache))
|
||||
})
|
||||
}
|
||||
|
@ -498,7 +519,7 @@ async function initIndexer(worker: WorkerImpl, cacheInfo: CacheInfo, keyLoaderFa
|
|||
|
||||
export async function resetLocator(): Promise<void> {
|
||||
await locator.login.resetSession()
|
||||
await initLocator(locator.login.worker, locator._browserData, locator._offlineStorageCleaner)
|
||||
await initLocator(locator._worker, locator._browserData)
|
||||
}
|
||||
|
||||
if (typeof self !== "undefined") {
|
|
@ -1,6 +1,6 @@
|
|||
import { WorkerImpl } from "../common/api/worker/WorkerImpl.js"
|
||||
import { Logger, replaceNativeLogger } from "../common/api/common/Logger.js"
|
||||
import { MailOfflineCleaner } from "./offline/MailOfflineCleaner.js"
|
||||
import { WorkerImpl } from "./WorkerImpl.js"
|
||||
import { Logger, replaceNativeLogger } from "../../../common/api/common/Logger.js"
|
||||
import { MailOfflineCleaner } from "../offline/MailOfflineCleaner.js"
|
||||
|
||||
/**
|
||||
* Receives the first message from the client and initializes the WorkerImpl to receive all future messages. Sends a response to the client on this first message.
|
||||
|
@ -22,7 +22,7 @@ self.onmessage = function (msg) {
|
|||
|
||||
// @ts-ignore
|
||||
const workerImpl = new WorkerImpl(typeof self !== "undefined" ? self : null)
|
||||
await workerImpl.init(browserData, new MailOfflineCleaner())
|
||||
await workerImpl.init(browserData)
|
||||
workerImpl.exposedInterface.entropyFacade().then((entropyFacade) => entropyFacade.addEntropy(initialRandomizerEntropy))
|
||||
self.postMessage({
|
||||
id: data.id,
|
|
@ -12,7 +12,7 @@ import {
|
|||
MailTypeRef,
|
||||
} from "../../src/common/api/entities/tutanota/TypeRefs.js"
|
||||
import { neverNull } from "@tutao/tutanota-utils"
|
||||
import { initLocator, locator } from "../../src/common/api/worker/WorkerLocator.js"
|
||||
import { initLocator, locator } from "../../src/mail-app/workerUtils/worker/WorkerLocator.js"
|
||||
import { browserDataStub, createTestEntity } from "./TestUtils.js"
|
||||
import { SessionType } from "../../src/common/api/common/SessionType.js"
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ export async function run({ integration, filter }: { integration?: boolean; filt
|
|||
}
|
||||
|
||||
async function setupSuite({ integration }: { integration?: boolean }) {
|
||||
const { WorkerImpl } = await import("../../src/common/api/worker/WorkerImpl.js")
|
||||
const { WorkerImpl } = await import("../../src/mail-app/workerUtils/worker/WorkerImpl.js")
|
||||
globalThis.testWorker = WorkerImpl
|
||||
|
||||
if (typeof process != "undefined") {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { BrowserData } from "../../src/common/misc/ClientConstants.js"
|
||||
import type { Db } from "../../src/common/api/worker/search/SearchTypes.js"
|
||||
import { IndexerCore } from "../../src/common/api/worker/search/IndexerCore.js"
|
||||
import { IndexerCore } from "../../src/mail-app/workerUtils/index/IndexerCore.js"
|
||||
import { EventQueue } from "../../src/common/api/worker/EventQueue.js"
|
||||
import { DbFacade, DbTransaction } from "../../src/common/api/worker/search/DbFacade.js"
|
||||
import { assertNotNull, deepEqual, defer, Thunk, TypeRef } from "@tutao/tutanota-utils"
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
} from "../../../../src/common/api/entities/sys/TypeRefs.js"
|
||||
import { createTestEntity } from "../../TestUtils.js"
|
||||
import { AccountType, OperationType } from "../../../../src/common/api/common/TutanotaConstants.js"
|
||||
|
||||
import { UserFacade } from "../../../../src/common/api/worker/facades/UserFacade.js"
|
||||
import { EntityClient } from "../../../../src/common/api/common/EntityClient.js"
|
||||
import { lazyAsync, lazyMemoized } from "@tutao/tutanota-utils"
|
||||
|
@ -21,6 +20,7 @@ import { MailFacade } from "../../../../src/common/api/worker/facades/lazy/MailF
|
|||
import { EventController } from "../../../../src/common/api/main/EventController.js"
|
||||
import { KeyRotationFacade } from "../../../../src/common/api/worker/facades/KeyRotationFacade.js"
|
||||
import { CacheManagementFacade } from "../../../../src/common/api/worker/facades/lazy/CacheManagementFacade.js"
|
||||
import { QueuedBatch } from "../../../../src/common/api/worker/EventQueue.js"
|
||||
|
||||
o.spec("EventBusEventCoordinatorTest", () => {
|
||||
let eventBusEventCoordinator: EventBusEventCoordinator
|
||||
|
@ -49,16 +49,16 @@ o.spec("EventBusEventCoordinatorTest", () => {
|
|||
keyRotationFacadeMock = object()
|
||||
cacheManagementFacade = object()
|
||||
eventBusEventCoordinator = new EventBusEventCoordinator(
|
||||
object(),
|
||||
object(),
|
||||
lazyMailFacade,
|
||||
object(),
|
||||
userFacade,
|
||||
entityClient,
|
||||
eventController,
|
||||
object(),
|
||||
keyRotationFacadeMock,
|
||||
async () => cacheManagementFacade,
|
||||
async (error: Error) => {},
|
||||
(queuedBatch: QueuedBatch[]) => {},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import { LoginFacade, LoginListener, ResumeSessionErrorReason } from "../../../.
|
|||
import { IServiceExecutor } from "../../../../../src/common/api/common/ServiceRequest"
|
||||
import { EntityClient } from "../../../../../src/common/api/common/EntityClient"
|
||||
import { RestClient } from "../../../../../src/common/api/worker/rest/RestClient"
|
||||
import { WorkerImpl } from "../../../../../src/common/api/worker/WorkerImpl"
|
||||
import { InstanceMapper } from "../../../../../src/common/api/worker/crypto/InstanceMapper"
|
||||
import { CryptoFacade, encryptString } from "../../../../../src/common/api/worker/crypto/CryptoFacade"
|
||||
import { CacheStorageLateInitializer } from "../../../../../src/common/api/worker/rest/CacheStorageProxy"
|
||||
|
@ -83,7 +82,6 @@ async function makeUser(userId: Id, kdfVersion: KdfType = DEFAULT_KDF_TYPE, user
|
|||
|
||||
o.spec("LoginFacadeTest", function () {
|
||||
let facade: LoginFacade
|
||||
let workerMock: WorkerImpl
|
||||
let serviceExecutor: IServiceExecutor
|
||||
let restClientMock: RestClient
|
||||
let entityClientMock: EntityClient
|
||||
|
@ -103,7 +101,6 @@ o.spec("LoginFacadeTest", function () {
|
|||
const login = "born.slippy@tuta.io"
|
||||
|
||||
o.beforeEach(function () {
|
||||
workerMock = instance(WorkerImpl)
|
||||
serviceExecutor = object()
|
||||
when(serviceExecutor.get(SaltService, anything()), { ignoreExtraArgs: true }).thenResolve(
|
||||
createTestEntity(SaltReturnTypeRef, { salt: SALT, kdfVersion: DEFAULT_KDF_TYPE }),
|
||||
|
@ -143,7 +140,6 @@ o.spec("LoginFacadeTest", function () {
|
|||
when(argon2idFacade.generateKeyFromPassphrase(anything(), anything())).thenResolve(PASSWORD_KEY)
|
||||
|
||||
facade = new LoginFacade(
|
||||
workerMock,
|
||||
restClientMock,
|
||||
entityClientMock,
|
||||
loginListener,
|
||||
|
@ -158,6 +154,7 @@ o.spec("LoginFacadeTest", function () {
|
|||
databaseKeyFactoryMock,
|
||||
argon2idFacade,
|
||||
entityClientMock,
|
||||
async (error: Error) => {},
|
||||
)
|
||||
|
||||
eventBusClientMock = instance(EventBusClient)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import o from "@tutao/otest"
|
||||
import { WorkerImpl } from "../../../../../src/common/api/worker/WorkerImpl.js"
|
||||
import { WorkerImpl } from "../../../../../src/mail-app/workerUtils/worker/WorkerImpl.js"
|
||||
import { UserFacade } from "../../../../../src/common/api/worker/facades/UserFacade.js"
|
||||
import { GroupManagementFacade } from "../../../../../src/common/api/worker/facades/lazy/GroupManagementFacade.js"
|
||||
import { CounterFacade } from "../../../../../src/common/api/worker/facades/lazy/CounterFacade.js"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import o from "@tutao/otest"
|
||||
import { verify } from "@tutao/tutanota-test-utils"
|
||||
import { customTypeEncoders, ensureBase64Ext, OfflineStorage } from "../../../../../src/common/api/worker/offline/OfflineStorage.js"
|
||||
import { customTypeEncoders, ensureBase64Ext, OfflineStorage, OfflineStorageCleaner } from "../../../../../src/common/api/worker/offline/OfflineStorage.js"
|
||||
import { instance, object, when } from "testdouble"
|
||||
import * as cborg from "cborg"
|
||||
import {
|
||||
|
@ -38,11 +38,11 @@ import { BlobElementEntity, ElementEntity, ListElementEntity, SomeEntity } from
|
|||
import { resolveTypeReference } from "../../../../../src/common/api/common/EntityFunctions.js"
|
||||
import { Type as TypeId } from "../../../../../src/common/api/common/EntityConstants.js"
|
||||
import { expandId } from "../../../../../src/common/api/worker/rest/DefaultEntityRestCache.js"
|
||||
import { WorkerImpl } from "../../../../../src/common/api/worker/WorkerImpl.js"
|
||||
import { UserTypeRef } from "../../../../../src/common/api/entities/sys/TypeRefs.js"
|
||||
import { DesktopSqlCipher } from "../../../../../src/common/desktop/db/DesktopSqlCipher.js"
|
||||
import { createTestEntity } from "../../../TestUtils.js"
|
||||
import { sql } from "../../../../../src/common/api/worker/offline/Sql.js"
|
||||
import { MailOfflineCleaner } from "../../../../../src/mail-app/workerUtils/offline/MailOfflineCleaner.js"
|
||||
|
||||
function incrementId(id: Id, ms: number) {
|
||||
const timestamp = generatedIdToTimestamp(id)
|
||||
|
@ -80,8 +80,8 @@ o.spec("OfflineStorageDb", function () {
|
|||
let dateProviderMock: DateProvider
|
||||
let storage: OfflineStorage
|
||||
let migratorMock: OfflineStorageMigrator
|
||||
let offlineStorageCleanerMock: OfflineStorageCleaner
|
||||
let interWindowEventSenderMock: InterWindowEventFacadeSendDispatcher
|
||||
let worker: WorkerImpl
|
||||
|
||||
o.beforeEach(async function () {
|
||||
dbFacade = new DesktopSqlCipher(nativePath, database, false)
|
||||
|
@ -89,8 +89,9 @@ o.spec("OfflineStorageDb", function () {
|
|||
dateProviderMock = object<DateProvider>()
|
||||
migratorMock = instance(OfflineStorageMigrator)
|
||||
interWindowEventSenderMock = instance(InterWindowEventFacadeSendDispatcher)
|
||||
offlineStorageCleanerMock = new MailOfflineCleaner()
|
||||
when(dateProviderMock.now()).thenReturn(now.getTime())
|
||||
storage = new OfflineStorage(dbFacade, interWindowEventSenderMock, dateProviderMock, migratorMock)
|
||||
storage = new OfflineStorage(dbFacade, interWindowEventSenderMock, dateProviderMock, migratorMock, offlineStorageCleanerMock)
|
||||
})
|
||||
|
||||
o.afterEach(async function () {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { func, instance, when } from "testdouble"
|
|||
import { verify } from "@tutao/tutanota-test-utils"
|
||||
import { LateInitializedCacheStorageImpl, OfflineStorageArgs } from "../../../../../src/common/api/worker/rest/CacheStorageProxy.js"
|
||||
import { OfflineStorage } from "../../../../../src/common/api/worker/offline/OfflineStorage.js"
|
||||
import { WorkerImpl } from "../../../../../src/common/api/worker/WorkerImpl.js"
|
||||
import { WorkerImpl } from "../../../../../src/mail-app/workerUtils/worker/WorkerImpl.js"
|
||||
|
||||
o.spec("CacheStorageProxy", function () {
|
||||
const userId = "userId"
|
||||
|
@ -20,7 +20,9 @@ o.spec("CacheStorageProxy", function () {
|
|||
offlineStorageMock = instance(OfflineStorage)
|
||||
offlineStorageProviderMock = func() as () => Promise<null | OfflineStorage>
|
||||
|
||||
proxy = new LateInitializedCacheStorageImpl(workerMock, offlineStorageProviderMock)
|
||||
proxy = new LateInitializedCacheStorageImpl(async (error: Error) => {
|
||||
await workerMock.sendError(error)
|
||||
}, offlineStorageProviderMock)
|
||||
})
|
||||
|
||||
o.spec("initialization", function () {
|
||||
|
|
|
@ -46,7 +46,7 @@ import {
|
|||
MailDetailsTypeRef,
|
||||
MailTypeRef,
|
||||
} from "../../../../../src/common/api/entities/tutanota/TypeRefs.js"
|
||||
import { OfflineStorage } from "../../../../../src/common/api/worker/offline/OfflineStorage.js"
|
||||
import { OfflineStorage, OfflineStorageCleaner } from "../../../../../src/common/api/worker/offline/OfflineStorage.js"
|
||||
import { assertThrows, mockAttribute, spy, unmockAttribute, verify } from "@tutao/tutanota-test-utils"
|
||||
import { NoZoneDateProvider } from "../../../../../src/common/api/common/utils/NoZoneDateProvider.js"
|
||||
import { RestClient } from "../../../../../src/common/api/worker/rest/RestClient.js"
|
||||
|
@ -86,7 +86,8 @@ async function getOfflineStorage(userId: Id): Promise<CacheStorage> {
|
|||
const sqlCipherFacade = new PerWindowSqlCipherFacade(odbRefCounter)
|
||||
await sqlCipherFacade.openDb(userId, offlineDatabaseTestKey)
|
||||
const interWindowEventSender = instance(InterWindowEventFacadeSendDispatcher)
|
||||
const offlineStorage = new OfflineStorage(sqlCipherFacade, interWindowEventSender, new NoZoneDateProvider(), migratorMock)
|
||||
const offlineStorageCleanerMock = object<OfflineStorageCleaner>()
|
||||
const offlineStorage = new OfflineStorage(sqlCipherFacade, interWindowEventSender, new NoZoneDateProvider(), migratorMock, offlineStorageCleanerMock)
|
||||
await offlineStorage.init({ userId, databaseKey: offlineDatabaseTestKey, timeRangeDays: 42, forceNewDatabase: false })
|
||||
return offlineStorage
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
ContactSocialIdTypeRef,
|
||||
ContactTypeRef,
|
||||
} from "../../../../../src/common/api/entities/tutanota/TypeRefs.js"
|
||||
import { ContactIndexer } from "../../../../../src/common/api/worker/search/ContactIndexer.js"
|
||||
import { ContactIndexer } from "../../../../../src/mail-app/workerUtils/index/ContactIndexer.js"
|
||||
import { NotAuthorizedError, NotFoundError } from "../../../../../src/common/api/common/error/RestError.js"
|
||||
import { DbTransaction } from "../../../../../src/common/api/worker/search/DbFacade.js"
|
||||
import { FULL_INDEXED_TIMESTAMP, NOTHING_INDEXED_TIMESTAMP, OperationType } from "../../../../../src/common/api/common/TutanotaConstants.js"
|
||||
|
|
|
@ -30,7 +30,7 @@ import { EntityUpdateTypeRef } from "../../../../../src/common/api/entities/sys/
|
|||
import { EventQueue } from "../../../../../src/common/api/worker/EventQueue.js"
|
||||
import { CancelledError } from "../../../../../src/common/api/common/error/CancelledError.js"
|
||||
import { createSearchIndexDbStub, DbStub, DbStubTransaction } from "./DbStub.js"
|
||||
import { IndexerCore } from "../../../../../src/common/api/worker/search/IndexerCore.js"
|
||||
import { IndexerCore } from "../../../../../src/mail-app/workerUtils/index/IndexerCore.js"
|
||||
import { elementIdPart, generatedIdToTimestamp, listIdPart, timestampToGeneratedId } from "../../../../../src/common/api/common/utils/EntityUtils.js"
|
||||
import { createTestEntity, makeCore } from "../../../TestUtils.js"
|
||||
import { Aes256Key, aes256RandomKey, aesEncrypt, fixedIv, IV_BYTE_LENGTH, random, unauthenticatedAesDecrypt } from "@tutao/tutanota-crypto"
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
NOTHING_INDEXED_TIMESTAMP,
|
||||
OperationType,
|
||||
} from "../../../../../src/common/api/common/TutanotaConstants.js"
|
||||
import { Indexer } from "../../../../../src/common/api/worker/search/Indexer.js"
|
||||
import { Indexer } from "../../../../../src/mail-app/workerUtils/index/Indexer.js"
|
||||
import { NotAuthorizedError } from "../../../../../src/common/api/common/error/RestError.js"
|
||||
import { ContactListTypeRef, ContactTypeRef, MailTypeRef } from "../../../../../src/common/api/entities/tutanota/TypeRefs.js"
|
||||
import { OutOfSyncError } from "../../../../../src/common/api/common/error/OutOfSyncError.js"
|
||||
|
@ -25,11 +25,11 @@ import { func, instance, matchers, object, replace, reset, verify, when } from "
|
|||
import { CacheInfo } from "../../../../../src/common/api/worker/facades/LoginFacade.js"
|
||||
import { RestClient } from "../../../../../src/common/api/worker/rest/RestClient.js"
|
||||
import { EntityClient } from "../../../../../src/common/api/common/EntityClient.js"
|
||||
import { ContactIndexer } from "../../../../../src/common/api/worker/search/ContactIndexer.js"
|
||||
import { ContactIndexer } from "../../../../../src/mail-app/workerUtils/index/ContactIndexer.js"
|
||||
import { InfoMessageHandler } from "../../../../../src/common/gui/InfoMessageHandler.js"
|
||||
import { GroupDataOS, Metadata, MetaDataOS } from "../../../../../src/common/api/worker/search/IndexTables.js"
|
||||
import { MailFacade } from "../../../../../src/common/api/worker/facades/lazy/MailFacade.js"
|
||||
import { MailIndexer } from "../../../../../src/common/api/worker/search/MailIndexer.js"
|
||||
import { MailIndexer } from "../../../../../src/mail-app/workerUtils/index/MailIndexer.js"
|
||||
import { KeyLoaderFacade } from "../../../../../src/common/api/worker/facades/KeyLoaderFacade.js"
|
||||
|
||||
const SERVER_TIME = new Date("1994-06-08").getTime()
|
||||
|
|
|
@ -9,10 +9,10 @@ import {
|
|||
NOTHING_INDEXED_TIMESTAMP,
|
||||
OperationType,
|
||||
} from "../../../../../src/common/api/common/TutanotaConstants.js"
|
||||
import { IndexerCore } from "../../../../../src/common/api/worker/search/IndexerCore.js"
|
||||
import { IndexerCore } from "../../../../../src/mail-app/workerUtils/index/IndexerCore.js"
|
||||
import type { EntityUpdate } from "../../../../../src/common/api/entities/sys/TypeRefs.js"
|
||||
import { EntityUpdateTypeRef, GroupMembershipTypeRef, UserTypeRef } from "../../../../../src/common/api/entities/sys/TypeRefs.js"
|
||||
import { _getCurrentIndexTimestamp, INITIAL_MAIL_INDEX_INTERVAL_DAYS, MailIndexer } from "../../../../../src/common/api/worker/search/MailIndexer.js"
|
||||
import { _getCurrentIndexTimestamp, INITIAL_MAIL_INDEX_INTERVAL_DAYS, MailIndexer } from "../../../../../src/mail-app/workerUtils/index/MailIndexer.js"
|
||||
import {
|
||||
BodyTypeRef,
|
||||
EncryptedMailAddressTypeRef,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import o from "@tutao/otest"
|
||||
import { SearchFacade } from "../../../../../src/common/api/worker/search/SearchFacade.js"
|
||||
import { SearchFacade } from "../../../../../src/mail-app/workerUtils/index/SearchFacade.js"
|
||||
import { ContactTypeRef, MailTypeRef } from "../../../../../src/common/api/entities/tutanota/TypeRefs.js"
|
||||
import { UserTypeRef } from "../../../../../src/common/api/entities/sys/TypeRefs.js"
|
||||
import type { TypeInfo } from "../../../../../src/common/api/worker/search/IndexUtils.js"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
import o from "@tutao/otest"
|
||||
import { ContactTypeRef } from "../../../../../src/common/api/entities/tutanota/TypeRefs.js"
|
||||
import { SuggestionFacade } from "../../../../../src/common/api/worker/search/SuggestionFacade.js"
|
||||
import { SuggestionFacade } from "../../../../../src/mail-app/workerUtils/index/SuggestionFacade.js"
|
||||
import { downcast } from "@tutao/tutanota-utils"
|
||||
import { aes256RandomKey, fixedIv } from "@tutao/tutanota-crypto"
|
||||
import { SearchTermSuggestionsOS } from "../../../../../src/common/api/worker/search/IndexTables.js"
|
||||
|
|
|
@ -8,18 +8,21 @@ import { EntityClient } from "../../../src/common/api/common/EntityClient.js"
|
|||
import { EntityRestClientMock } from "../api/worker/rest/EntityRestClientMock.js"
|
||||
import { downcast } from "@tutao/tutanota-utils"
|
||||
import { LoginController } from "../../../src/common/api/main/LoginController.js"
|
||||
import { matchers, object, when } from "testdouble"
|
||||
import { instance, matchers, object, when } from "testdouble"
|
||||
import { UserController } from "../../../src/common/api/main/UserController.js"
|
||||
import { createTestEntity } from "../TestUtils.js"
|
||||
import { EntityUpdateData } from "../../../src/common/api/common/utils/EntityUpdateUtils.js"
|
||||
import { MailboxDetail, MailboxModel } from "../../../src/common/mailFunctionality/MailboxModel.js"
|
||||
import { InboxRuleHandler } from "../../../src/mail-app/mail/model/InboxRuleHandler.js"
|
||||
import { getElementId, getListId } from "../../../src/common/api/common/utils/EntityUtils.js"
|
||||
import { MailModel } from "../../../src/mail-app/mail/model/MailModel.js"
|
||||
import { EventController } from "../../../src/common/api/main/EventController.js"
|
||||
import { MailFacade } from "../../../src/common/api/worker/facades/lazy/MailFacade.js"
|
||||
|
||||
o.spec("MailModelTest", function () {
|
||||
let notifications: Partial<Notifications>
|
||||
let showSpy: Spy
|
||||
let model: MailboxModel
|
||||
let model: MailModel
|
||||
const inboxFolder = createTestEntity(MailFolderTypeRef, { _id: ["folderListId", "inboxId"], isMailSet: false })
|
||||
inboxFolder.mails = "instanceListId"
|
||||
inboxFolder.folderType = MailSetKind.INBOX
|
||||
|
@ -33,6 +36,9 @@ o.spec("MailModelTest", function () {
|
|||
|
||||
o.beforeEach(function () {
|
||||
notifications = {}
|
||||
const mailboxModel = instance(MailboxModel)
|
||||
const eventController = instance(EventController)
|
||||
const mailFacade = instance(MailFacade)
|
||||
showSpy = notifications.showNotification = spy()
|
||||
logins = object()
|
||||
let userController = object<UserController>()
|
||||
|
@ -40,43 +46,37 @@ o.spec("MailModelTest", function () {
|
|||
when(logins.getUserController()).thenReturn(userController)
|
||||
|
||||
inboxRuleHandler = object()
|
||||
model = new MailboxModel(downcast({}), new EntityClient(restClient), logins)
|
||||
model = new MailModel(downcast({}), mailboxModel, eventController, new EntityClient(restClient), logins, mailFacade, null, null)
|
||||
// not pretty, but works
|
||||
model.mailboxDetails(mailboxDetails as MailboxDetail[])
|
||||
// model.mailboxDetails(mailboxDetails as MailboxDetail[])
|
||||
})
|
||||
o("doesn't send notification for another folder", async function () {
|
||||
const mail = createTestEntity(MailTypeRef, { _id: [anotherFolder.mails, "mailId"], sets: [] })
|
||||
restClient.addListInstances(mail)
|
||||
await model.entityEventsReceived(
|
||||
[
|
||||
makeUpdate({
|
||||
instanceListId: getListId(mail),
|
||||
instanceId: getElementId(mail),
|
||||
operation: OperationType.CREATE,
|
||||
}),
|
||||
],
|
||||
"userGroupId",
|
||||
)
|
||||
await model.entityEventsReceived([
|
||||
makeUpdate({
|
||||
instanceListId: getListId(mail),
|
||||
instanceId: getElementId(mail),
|
||||
operation: OperationType.CREATE,
|
||||
}),
|
||||
])
|
||||
o(showSpy.invocations.length).equals(0)
|
||||
})
|
||||
o("doesn't send notification for move operation", async function () {
|
||||
const mail = createTestEntity(MailTypeRef, { _id: [inboxFolder.mails, "mailId"], sets: [] })
|
||||
restClient.addListInstances(mail)
|
||||
await model.entityEventsReceived(
|
||||
[
|
||||
makeUpdate({
|
||||
instanceListId: getListId(mail),
|
||||
instanceId: getElementId(mail),
|
||||
operation: OperationType.DELETE,
|
||||
}),
|
||||
makeUpdate({
|
||||
instanceListId: getListId(mail),
|
||||
instanceId: getElementId(mail),
|
||||
operation: OperationType.CREATE,
|
||||
}),
|
||||
],
|
||||
"userGroupId",
|
||||
)
|
||||
await model.entityEventsReceived([
|
||||
makeUpdate({
|
||||
instanceListId: getListId(mail),
|
||||
instanceId: getElementId(mail),
|
||||
operation: OperationType.DELETE,
|
||||
}),
|
||||
makeUpdate({
|
||||
instanceListId: getListId(mail),
|
||||
instanceId: getElementId(mail),
|
||||
operation: OperationType.CREATE,
|
||||
}),
|
||||
])
|
||||
o(showSpy.invocations.length).equals(0)
|
||||
})
|
||||
|
|
@ -3,7 +3,7 @@ import { Recipient, RecipientType } from "../../../src/common/api/common/recipie
|
|||
import { LazyLoaded } from "@tutao/tutanota-utils"
|
||||
import { Contact } from "../../../src/common/api/entities/tutanota/TypeRefs.js"
|
||||
import { User } from "../../../src/common/api/entities/sys/TypeRefs.js"
|
||||
import { createNewContact, isTutanotaMailAddress } from "../../../src/common/mailFunctionality/SharedMailUtils.js"
|
||||
import { createNewContact, isTutaMailAddress } from "../../../src/common/mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
/**
|
||||
* Creating actual ResolvableRecipients is annoying because you have to mock a bunch of stuff in other model classes
|
||||
|
@ -43,7 +43,7 @@ export class ResolvableRecipientMock implements ResolvableRecipient {
|
|||
private user: User,
|
||||
) {
|
||||
this.name = name ?? ""
|
||||
this.type = type ?? (isTutanotaMailAddress(address) ? RecipientType.INTERNAL : RecipientType.UNKNOWN)
|
||||
this.type = type ?? (isTutaMailAddress(address) ? RecipientType.INTERNAL : RecipientType.UNKNOWN)
|
||||
|
||||
if (resolveMode === ResolveMode.Eager) {
|
||||
this.lazyResolve.getAsync()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import o from "@tutao/otest"
|
||||
import type { MailBundle } from "../../../../src/mail-app/mail/export/Bundler.js"
|
||||
import { _formatSmtpDateTime, mailToEml } from "../../../../src/mail-app/mail/export/Exporter.js"
|
||||
import { base64ToUint8Array, stringToUtf8Uint8Array } from "@tutao/tutanota-utils"
|
||||
import { createDataFile } from "../../../../src/common/api/common/DataFile.js"
|
||||
import { MailBundle } from "../../../../src/common/mailFunctionality/SharedMailUtils.js"
|
||||
|
||||
o.spec("Exporter", function () {
|
||||
o.spec("mail to eml", function () {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue