mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-19 21:19:31 +01:00
feat: recalculate conflicts on sync
This commit is contained in:
18
packages/core/api/conflicts.js
Normal file
18
packages/core/api/conflicts.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import Database from "./index";
|
||||||
|
|
||||||
|
class Conflicts {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Database} db
|
||||||
|
*/
|
||||||
|
constructor(db) {
|
||||||
|
this._db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
recalculate = async () => {
|
||||||
|
if (this._db.notes.conflicted.length < 0) {
|
||||||
|
await this._db.context.write("hasConflicts", false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default Conflicts;
|
||||||
@@ -7,6 +7,7 @@ import Sync from "./sync";
|
|||||||
import Vault from "./vault";
|
import Vault from "./vault";
|
||||||
import Lookup from "./lookup";
|
import Lookup from "./lookup";
|
||||||
import Content from "../collections/content";
|
import Content from "../collections/content";
|
||||||
|
import Conflict from "./conflicts";
|
||||||
|
|
||||||
class Database {
|
class Database {
|
||||||
constructor(context) {
|
constructor(context) {
|
||||||
@@ -37,6 +38,7 @@ class Database {
|
|||||||
await this.trash.init(this.notes, this.notebooks, this.delta, this.text);
|
await this.trash.init(this.notes, this.notebooks, this.delta, this.text);
|
||||||
this.syncer = new Sync(this);
|
this.syncer = new Sync(this);
|
||||||
this.vault = new Vault(this, this.context);
|
this.vault = new Vault(this, this.context);
|
||||||
|
this.conflicts = new Conflicts(this);
|
||||||
this.lookup = new Lookup(this);
|
this.lookup = new Lookup(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default class Sync {
|
|||||||
let token = await this.db.user.token();
|
let token = await this.db.user.token();
|
||||||
if (!token) throw new Error("You are not logged in");
|
if (!token) throw new Error("You are not logged in");
|
||||||
let response = await fetch(`${HOST}sync?lst=${lastSyncedTimestamp}`, {
|
let response = await fetch(`${HOST}sync?lst=${lastSyncedTimestamp}`, {
|
||||||
headers: { ...HEADERS, Authorization: `Bearer ${token}` }
|
headers: { ...HEADERS, Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
//TODO decrypt the response.
|
//TODO decrypt the response.
|
||||||
return await response.json();
|
return await response.json();
|
||||||
@@ -66,6 +66,7 @@ export default class Sync {
|
|||||||
let user = await this.db.user.get();
|
let user = await this.db.user.get();
|
||||||
if (!user) throw new Error("You need to login to sync.");
|
if (!user) throw new Error("You need to login to sync.");
|
||||||
|
|
||||||
|
await this.db.conflicts.recalculate();
|
||||||
await this.throwOnConflicts();
|
await this.throwOnConflicts();
|
||||||
|
|
||||||
let lastSyncedTimestamp = user.lastSynced || 0;
|
let lastSyncedTimestamp = user.lastSynced || 0;
|
||||||
@@ -94,7 +95,7 @@ export default class Sync {
|
|||||||
let response = await fetch(`${HOST}sync`, {
|
let response = await fetch(`${HOST}sync`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { ...HEADERS, Authorization: `Bearer ${token}` },
|
headers: { ...HEADERS, Authorization: `Bearer ${token}` },
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data),
|
||||||
});
|
});
|
||||||
return response.ok;
|
return response.ok;
|
||||||
}
|
}
|
||||||
@@ -120,7 +121,7 @@ class Merger {
|
|||||||
|
|
||||||
async _mergeArray(array, get, set) {
|
async _mergeArray(array, get, set) {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
array.map(async item => await this._mergeItem(item, get, set))
|
array.map(async (item) => await this._mergeItem(item, get, set))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +138,7 @@ class Merger {
|
|||||||
async _mergeArrayWithConflicts(array, get, set, resolve) {
|
async _mergeArrayWithConflicts(array, get, set, resolve) {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
array.map(
|
array.map(
|
||||||
async item =>
|
async (item) =>
|
||||||
await this._mergeItemWithConflicts(item, get, set, resolve)
|
await this._mergeItemWithConflicts(item, get, set, resolve)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -152,26 +153,26 @@ class Merger {
|
|||||||
text,
|
text,
|
||||||
tags,
|
tags,
|
||||||
colors,
|
colors,
|
||||||
trash
|
trash,
|
||||||
} = serverResponse;
|
} = serverResponse;
|
||||||
|
|
||||||
if (synced || areAllEmpty(serverResponse)) return false;
|
if (synced || areAllEmpty(serverResponse)) return false;
|
||||||
|
|
||||||
await this._mergeArray(
|
await this._mergeArray(
|
||||||
notes,
|
notes,
|
||||||
id => this._db.notes.note(id),
|
(id) => this._db.notes.note(id),
|
||||||
item => this._db.notes.add(item)
|
(item) => this._db.notes.add(item)
|
||||||
);
|
);
|
||||||
await this._mergeArray(
|
await this._mergeArray(
|
||||||
notebooks,
|
notebooks,
|
||||||
id => this._db.notebooks.notebook(id),
|
(id) => this._db.notebooks.notebook(id),
|
||||||
item => this._db.notebooks.add(item)
|
(item) => this._db.notebooks.add(item)
|
||||||
);
|
);
|
||||||
|
|
||||||
await this._mergeArrayWithConflicts(
|
await this._mergeArrayWithConflicts(
|
||||||
delta,
|
delta,
|
||||||
id => this._db.delta.raw(id),
|
(id) => this._db.delta.raw(id),
|
||||||
item => this._db.delta.add(item),
|
(item) => this._db.delta.add(item),
|
||||||
async (local, remote) => {
|
async (local, remote) => {
|
||||||
await this._db.delta.add({ ...local, conflicted: remote });
|
await this._db.delta.add({ ...local, conflicted: remote });
|
||||||
await this._db.notes.add({ id: local.noteId, conflicted: true });
|
await this._db.notes.add({ id: local.noteId, conflicted: true });
|
||||||
@@ -181,26 +182,26 @@ class Merger {
|
|||||||
|
|
||||||
await this._mergeArray(
|
await this._mergeArray(
|
||||||
text,
|
text,
|
||||||
id => this._db.text.raw(id),
|
(id) => this._db.text.raw(id),
|
||||||
item => this._db.text.add(item)
|
(item) => this._db.text.add(item)
|
||||||
);
|
);
|
||||||
|
|
||||||
await this._mergeArray(
|
await this._mergeArray(
|
||||||
tags,
|
tags,
|
||||||
id => this._db.tags.tag(id),
|
(id) => this._db.tags.tag(id),
|
||||||
item => this._db.tags.merge(item)
|
(item) => this._db.tags.merge(item)
|
||||||
);
|
);
|
||||||
|
|
||||||
await this._mergeArray(
|
await this._mergeArray(
|
||||||
colors,
|
colors,
|
||||||
id => this._db.colors.tag(id),
|
(id) => this._db.colors.tag(id),
|
||||||
item => this._db.colors.merge(item)
|
(item) => this._db.colors.merge(item)
|
||||||
);
|
);
|
||||||
|
|
||||||
await this._mergeArray(
|
await this._mergeArray(
|
||||||
trash,
|
trash,
|
||||||
() => undefined,
|
() => undefined,
|
||||||
item => this._db.trash.add(item)
|
(item) => this._db.trash.add(item)
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -229,21 +230,21 @@ class Prepare {
|
|||||||
tags: this._prepareForServer(this._db.tags.raw),
|
tags: this._prepareForServer(this._db.tags.raw),
|
||||||
colors: this._prepareForServer(this._db.colors.raw),
|
colors: this._prepareForServer(this._db.colors.raw),
|
||||||
trash: this._prepareForServer(this._db.trash.raw),
|
trash: this._prepareForServer(this._db.trash.raw),
|
||||||
lastSynced: Date.now()
|
lastSynced: Date.now(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_prepareForServer(array) {
|
_prepareForServer(array) {
|
||||||
return tfun
|
return tfun
|
||||||
.filter(item => item.dateEdited > this._lastSyncedTimestamp)
|
.filter((item) => item.dateEdited > this._lastSyncedTimestamp)
|
||||||
.map(item => ({
|
.map((item) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
data: JSON.stringify(item)
|
data: JSON.stringify(item),
|
||||||
}))(array);
|
}))(array);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function areAllEmpty(obj) {
|
function areAllEmpty(obj) {
|
||||||
const arrays = Object.values(obj).filter(v => v.length !== undefined);
|
const arrays = Object.values(obj).filter((v) => v.length !== undefined);
|
||||||
return arrays.every(array => array.length === 0);
|
return arrays.every((array) => array.length === 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
getWeekGroupFromTimestamp,
|
getWeekGroupFromTimestamp,
|
||||||
months,
|
months,
|
||||||
getLastWeekTimestamp,
|
getLastWeekTimestamp,
|
||||||
get7DayTimestamp
|
get7DayTimestamp,
|
||||||
} from "../utils/date";
|
} from "../utils/date";
|
||||||
import Notebooks from "./notebooks";
|
import Notebooks from "./notebooks";
|
||||||
import Note from "../models/note";
|
import Note from "../models/note";
|
||||||
@@ -62,7 +62,7 @@ export default class Notes {
|
|||||||
|
|
||||||
let note = {
|
let note = {
|
||||||
...oldNote,
|
...oldNote,
|
||||||
...noteArg
|
...noteArg,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isNoteEmpty(note)) {
|
if (isNoteEmpty(note)) {
|
||||||
@@ -79,7 +79,7 @@ export default class Notes {
|
|||||||
deltaId = await this._deltaCollection.add({
|
deltaId = await this._deltaCollection.add({
|
||||||
noteId: id,
|
noteId: id,
|
||||||
id: deltaId,
|
id: deltaId,
|
||||||
data: delta
|
data: delta,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ export default class Notes {
|
|||||||
textId = await this._textCollection.add({
|
textId = await this._textCollection.add({
|
||||||
noteId: id,
|
noteId: id,
|
||||||
id: textId,
|
id: textId,
|
||||||
data: text
|
data: text,
|
||||||
});
|
});
|
||||||
note.title = getNoteTitle(note);
|
note.title = getNoteTitle(note);
|
||||||
note.headline = getNoteHeadline(note);
|
note.headline = getNoteHeadline(note);
|
||||||
@@ -106,7 +106,7 @@ export default class Notes {
|
|||||||
favorite: !!note.favorite,
|
favorite: !!note.favorite,
|
||||||
headline: note.headline,
|
headline: note.headline,
|
||||||
dateCreated: note.dateCreated,
|
dateCreated: note.dateCreated,
|
||||||
conflicted: !!note.conflicted
|
conflicted: !!note.conflicted,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!oldNote) {
|
if (!oldNote) {
|
||||||
@@ -147,6 +147,10 @@ export default class Notes {
|
|||||||
return tfun.filter(".pinned === true")(this.all);
|
return tfun.filter(".pinned === true")(this.all);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get conflicted() {
|
||||||
|
return tfun.filter(".conflicted === true")(this.all);
|
||||||
|
}
|
||||||
|
|
||||||
get favorites() {
|
get favorites() {
|
||||||
return tfun.filter(".favorite === true")(this.all);
|
return tfun.filter(".favorite === true")(this.all);
|
||||||
}
|
}
|
||||||
@@ -154,49 +158,49 @@ export default class Notes {
|
|||||||
tagged(tag) {
|
tagged(tag) {
|
||||||
return this._tagsCollection
|
return this._tagsCollection
|
||||||
.notes(tag)
|
.notes(tag)
|
||||||
.map(id => this._collection.getItem(id));
|
.map((id) => this._collection.getItem(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
colored(color) {
|
colored(color) {
|
||||||
return this._colorsCollection
|
return this._colorsCollection
|
||||||
.notes(color)
|
.notes(color)
|
||||||
.map(id => this._collection.getItem(id));
|
.map((id) => this._collection.getItem(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
group(by, special = false) {
|
group(by, special = false) {
|
||||||
let notes = !special
|
let notes = !special
|
||||||
? tfun.filter(".pinned === false")(this.all)
|
? tfun.filter(".pinned === false")(this.all)
|
||||||
: this.all;
|
: this.all;
|
||||||
notes = sort(notes).desc(t => t.dateCreated);
|
notes = sort(notes).desc((t) => t.dateCreated);
|
||||||
switch (by) {
|
switch (by) {
|
||||||
case "abc":
|
case "abc":
|
||||||
return groupBy(notes, note => note.title[0].toUpperCase(), special);
|
return groupBy(notes, (note) => note.title[0].toUpperCase(), special);
|
||||||
case "month":
|
case "month":
|
||||||
return groupBy(
|
return groupBy(
|
||||||
notes,
|
notes,
|
||||||
note => months[new Date(note.dateCreated).getMonth()],
|
(note) => months[new Date(note.dateCreated).getMonth()],
|
||||||
special
|
special
|
||||||
);
|
);
|
||||||
case "week":
|
case "week":
|
||||||
return groupBy(
|
return groupBy(
|
||||||
notes,
|
notes,
|
||||||
note => getWeekGroupFromTimestamp(note.dateCreated),
|
(note) => getWeekGroupFromTimestamp(note.dateCreated),
|
||||||
special
|
special
|
||||||
);
|
);
|
||||||
case "year":
|
case "year":
|
||||||
return groupBy(
|
return groupBy(
|
||||||
notes,
|
notes,
|
||||||
note => new Date(note.dateCreated).getFullYear().toString(),
|
(note) => new Date(note.dateCreated).getFullYear().toString(),
|
||||||
special
|
special
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
let timestamps = {
|
let timestamps = {
|
||||||
recent: getLastWeekTimestamp(7),
|
recent: getLastWeekTimestamp(7),
|
||||||
lastWeek: getLastWeekTimestamp(7) - get7DayTimestamp() //seven day timestamp value
|
lastWeek: getLastWeekTimestamp(7) - get7DayTimestamp(), //seven day timestamp value
|
||||||
};
|
};
|
||||||
return groupBy(
|
return groupBy(
|
||||||
notes,
|
notes,
|
||||||
note =>
|
(note) =>
|
||||||
note.dateCreated >= timestamps.recent
|
note.dateCreated >= timestamps.recent
|
||||||
? "Recent"
|
? "Recent"
|
||||||
: note.dateCreated >= timestamps.lastWeek
|
: note.dateCreated >= timestamps.lastWeek
|
||||||
@@ -247,7 +251,7 @@ function isNoteEmpty(note) {
|
|||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
content: { delta, text },
|
content: { delta, text },
|
||||||
locked
|
locked,
|
||||||
} = note;
|
} = note;
|
||||||
const isTitleEmpty = !title || !title.trim().length;
|
const isTitleEmpty = !title || !title.trim().length;
|
||||||
const isTextEmpty = !isHex(text) && (!text || !text.trim().length);
|
const isTextEmpty = !isHex(text) && (!text || !text.trim().length);
|
||||||
@@ -265,9 +269,5 @@ function getNoteHeadline(note) {
|
|||||||
|
|
||||||
function getNoteTitle(note) {
|
function getNoteTitle(note) {
|
||||||
if (note.title && note.title.length > 0) return note.title.trim();
|
if (note.title && note.title.length > 0) return note.title.trim();
|
||||||
return note.content.text
|
return note.content.text.split(" ").slice(0, 3).join(" ").trim();
|
||||||
.split(" ")
|
|
||||||
.slice(0, 3)
|
|
||||||
.join(" ")
|
|
||||||
.trim();
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user