basic sync
parent
1817fbf87d
commit
1f1d3b6a89
@ -0,0 +1,131 @@
|
||||
// SPDX-FileCopyrightText: © 2019 EteSync Authors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
import * as Etebase from "etebase";
|
||||
|
||||
import { store, persistor, StoreState } from "../store";
|
||||
|
||||
import { credentialsSelector } from "../credentials";
|
||||
import { setSyncCollection, setSyncGeneral, setCacheItem, setCacheCollection, unsetCacheCollection, unsetCacheItem } from "../store/actions";
|
||||
|
||||
const cachedSyncManager = new Map<string, SyncManager>();
|
||||
export class SyncManager {
|
||||
private COLLECTION_TYPES = ["etebase.vcard", "etebase.vevent", "etebase.vtodo"];
|
||||
private BATCH_SIZE = 40;
|
||||
|
||||
public static getManager(etebase: Etebase.Account) {
|
||||
const cached = cachedSyncManager.get(etebase.user.username);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const ret = new SyncManager();
|
||||
cachedSyncManager.set(etebase.user.username, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static removeManager(etebase: Etebase.Account) {
|
||||
cachedSyncManager.delete(etebase.user.username);
|
||||
}
|
||||
|
||||
protected etebase: Etebase.Account;
|
||||
protected isSyncing: boolean;
|
||||
|
||||
public async fetchCollection(col: Etebase.Collection) {
|
||||
const storeState = store.getState() as unknown as StoreState;
|
||||
const etebase = (await credentialsSelector(storeState))!;
|
||||
const syncCollection = storeState.sync.collections.get(col.uid, undefined);
|
||||
|
||||
const colMgr = etebase.getCollectionManager();
|
||||
const itemMgr = colMgr.getItemManager(col);
|
||||
|
||||
let stoken = syncCollection?.stoken;
|
||||
const limit = this.BATCH_SIZE;
|
||||
let done = false;
|
||||
while (!done) {
|
||||
const items = await itemMgr.list({ stoken, limit });
|
||||
for (const item of items.data) {
|
||||
if (item.isDeleted) {
|
||||
store.dispatch(unsetCacheItem(col.uid, itemMgr, item.uid));
|
||||
} else {
|
||||
store.dispatch(setCacheItem(col, itemMgr, item));
|
||||
}
|
||||
}
|
||||
done = items.done;
|
||||
stoken = items.stoken;
|
||||
}
|
||||
|
||||
if (syncCollection?.stoken !== stoken) {
|
||||
store.dispatch(setSyncCollection(col.uid, stoken!));
|
||||
}
|
||||
}
|
||||
|
||||
public async fetchAllCollections() {
|
||||
const storeState = store.getState() as unknown as StoreState;
|
||||
const etebase = (await credentialsSelector(storeState))!;
|
||||
const syncGeneral = storeState.sync.general;
|
||||
|
||||
const colMgr = etebase.getCollectionManager();
|
||||
const limit = this.BATCH_SIZE;
|
||||
let stoken = syncGeneral?.stoken;
|
||||
let done = false;
|
||||
while (!done) {
|
||||
const collections = await colMgr.list({ stoken, limit });
|
||||
for (const col of collections.data) {
|
||||
const meta = await col.getMeta();
|
||||
if (this.COLLECTION_TYPES.includes(meta.type)) {
|
||||
// We only get the changed collections here, so always fetch
|
||||
if (col.isDeleted) {
|
||||
store.dispatch(unsetCacheCollection(colMgr, col.uid));
|
||||
} else {
|
||||
store.dispatch(setCacheCollection(colMgr, col));
|
||||
}
|
||||
await this.fetchCollection(col);
|
||||
}
|
||||
}
|
||||
if (collections.removedMemberships) {
|
||||
for (const removed of collections.removedMemberships) {
|
||||
store.dispatch(unsetCacheCollection(colMgr, removed.uid));
|
||||
}
|
||||
}
|
||||
done = collections.done;
|
||||
stoken = collections.stoken;
|
||||
}
|
||||
|
||||
if (syncGeneral?.stoken !== stoken) {
|
||||
store.dispatch(setSyncGeneral(stoken));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public async sync() {
|
||||
if (this.isSyncing) {
|
||||
return false;
|
||||
}
|
||||
this.isSyncing = true;
|
||||
|
||||
try {
|
||||
const stoken = await this.fetchAllCollections();
|
||||
return stoken;
|
||||
} catch (e) {
|
||||
if (e instanceof Etebase.NetworkError) {
|
||||
// Ignore network errors
|
||||
return null;
|
||||
} else if (e instanceof Etebase.HTTPError) {
|
||||
switch (e.status) {
|
||||
case 401: // INVALID TOKEN
|
||||
case 403: // FORBIDDEN
|
||||
case 503: // UNAVAILABLE
|
||||
// FIXME store.dispatch(addNonFatalError(this.etebase, e));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
} finally {
|
||||
this.isSyncing = false;
|
||||
|
||||
// Force flusing the store to disk
|
||||
persistor.persist();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue