mirror of
https://github.com/MadeBaruna/paimon-moe.git
synced 2025-01-03 09:49:06 +01:00
WIP: data conflict resolve
This commit is contained in:
parent
925355bc32
commit
8567c6b831
6 changed files with 99 additions and 14 deletions
|
@ -2,13 +2,13 @@
|
||||||
// doc: /static/images.save_sync_flow.png
|
// doc: /static/images.save_sync_flow.png
|
||||||
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { onMount, getContext } from 'svelte';
|
import { onMount, getContext, setContext } from 'svelte';
|
||||||
import { driveSignedIn, driveLoading, saveId } from '../stores/dataSync';
|
import { driveSignedIn, driveLoading, saveId, synced } from '../stores/dataSync';
|
||||||
import { getLocalSaveJson, updateSave, updateTime, UPDATE_TIME_KEY } from '../stores/saveManager';
|
import { getLocalSaveJson, updateSave, updateTime, UPDATE_TIME_KEY } from '../stores/saveManager';
|
||||||
|
|
||||||
import SyncConflictModal from '../components/SyncConflictModal.svelte';
|
import SyncConflictModal from '../components/SyncConflictModal.svelte';
|
||||||
|
|
||||||
const { open: openModal } = getContext('simple-modal');
|
const { open: openModal, close: closeModal } = getContext('simple-modal');
|
||||||
|
|
||||||
const CLIENT_ID = __paimon.env.GOOGLE_DRIVE_CLIENT_ID;
|
const CLIENT_ID = __paimon.env.GOOGLE_DRIVE_CLIENT_ID;
|
||||||
const API_KEY = __paimon.env.GOOGLE_DRIVE_API_KEY;
|
const API_KEY = __paimon.env.GOOGLE_DRIVE_API_KEY;
|
||||||
|
@ -19,12 +19,21 @@
|
||||||
|
|
||||||
$: localSaveExists = $updateTime !== null;
|
$: localSaveExists = $updateTime !== null;
|
||||||
|
|
||||||
|
setContext('sync', {
|
||||||
|
startSync,
|
||||||
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
startSync();
|
||||||
|
});
|
||||||
|
|
||||||
|
function startSync() {
|
||||||
|
synced.set(false);
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.onload = handleClientLoad;
|
script.onload = handleClientLoad;
|
||||||
script.src = 'https://apis.google.com/js/api.js';
|
script.src = 'https://apis.google.com/js/api.js';
|
||||||
document.body.appendChild(script);
|
document.body.appendChild(script);
|
||||||
});
|
}
|
||||||
|
|
||||||
function handleClientLoad() {
|
function handleClientLoad() {
|
||||||
gapi.load('client:auth2', initClient);
|
gapi.load('client:auth2', initClient);
|
||||||
|
@ -37,6 +46,8 @@
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
getFiles();
|
getFiles();
|
||||||
|
} else {
|
||||||
|
synced.set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +62,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function useRemoteData() {
|
||||||
|
for (const k in remoteSave) {
|
||||||
|
updateSave(k, remoteSave[k], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
synced.set(true);
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function useLocalData() {
|
||||||
|
await saveData(getLocalSaveJson());
|
||||||
|
|
||||||
|
synced.set(true);
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
|
||||||
async function compareLocalSave() {
|
async function compareLocalSave() {
|
||||||
try {
|
try {
|
||||||
const data = await getData();
|
const data = await getData();
|
||||||
|
@ -65,6 +92,8 @@
|
||||||
remoteTime: remoteTime,
|
remoteTime: remoteTime,
|
||||||
localTime: $updateTime,
|
localTime: $updateTime,
|
||||||
downloadBackup: exportData,
|
downloadBackup: exportData,
|
||||||
|
useRemote: useRemoteData,
|
||||||
|
useLocal: useLocalData,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
closeButton: false,
|
closeButton: false,
|
||||||
|
@ -73,6 +102,8 @@
|
||||||
styleWindow: { background: '#25294A' },
|
styleWindow: { background: '#25294A' },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
synced.set(true);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -100,6 +131,7 @@
|
||||||
await compareLocalSave();
|
await compareLocalSave();
|
||||||
} else {
|
} else {
|
||||||
await copyRemoteToLocal();
|
await copyRemoteToLocal();
|
||||||
|
synced.set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -125,6 +157,7 @@
|
||||||
await saveData(getLocalSaveJson());
|
await saveData(getLocalSaveJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synced.set(true);
|
||||||
console.log(result);
|
console.log(result);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -140,6 +173,7 @@
|
||||||
alt: 'media',
|
alt: 'media',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(result);
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -199,3 +233,5 @@
|
||||||
fileLink.click();
|
fileLink.click();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { mdiCloudAlert, mdiContentSave, mdiDownload, mdiFile, mdiGoogleDrive, mdiUpload } from '@mdi/js';
|
import { mdiCloudAlert, mdiContentSave, mdiDownload, mdiFile, mdiGoogleDrive, mdiLoading, mdiUpload } from '@mdi/js';
|
||||||
import Button from './Button.svelte';
|
import Button from './Button.svelte';
|
||||||
|
|
||||||
import Icon from './Icon.svelte';
|
import Icon from './Icon.svelte';
|
||||||
|
@ -7,6 +7,20 @@
|
||||||
export let remoteTime;
|
export let remoteTime;
|
||||||
export let localTime;
|
export let localTime;
|
||||||
export let downloadBackup = () => {};
|
export let downloadBackup = () => {};
|
||||||
|
export let useRemote = () => {};
|
||||||
|
export let useLocal = () => {};
|
||||||
|
|
||||||
|
let loading = false;
|
||||||
|
|
||||||
|
function useLocalData() {
|
||||||
|
loading = true;
|
||||||
|
useLocal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function useRemoteData() {
|
||||||
|
loading = true;
|
||||||
|
useRemote();
|
||||||
|
}
|
||||||
|
|
||||||
const remoteFormatted = remoteTime.format('dddd, MMMM D, YYYY h:mm A');
|
const remoteFormatted = remoteTime.format('dddd, MMMM D, YYYY h:mm A');
|
||||||
const localFormatted = localTime.format('dddd, MMMM D, YYYY h:mm A');
|
const localFormatted = localTime.format('dddd, MMMM D, YYYY h:mm A');
|
||||||
|
@ -26,8 +40,8 @@
|
||||||
{remoteNewer ? 'NEWER' : 'OLDER'}
|
{remoteNewer ? 'NEWER' : 'OLDER'}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-400 mt-1">Last modified: {remoteFormatted}</p>
|
<p class="text-gray-400 mt-1">Last modified: {remoteFormatted}</p>
|
||||||
<Button className="mt-2 w-full">
|
<Button disabled={loading} className="mt-2 w-full" on:click={useRemoteData}>
|
||||||
<Icon path={mdiDownload} className="mr-1" />Replace Local Data
|
<Icon path={loading ? mdiLoading : mdiDownload} spin={loading} className="mr-1" />Replace Local Data
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-2 text-white text-center">OR</p>
|
<p class="mt-2 text-white text-center">OR</p>
|
||||||
|
@ -38,8 +52,8 @@
|
||||||
{remoteNewer ? 'OLDER' : 'NEWER'}
|
{remoteNewer ? 'OLDER' : 'NEWER'}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-400 mt-1">Last modified: {localFormatted}</p>
|
<p class="text-gray-400 mt-1">Last modified: {localFormatted}</p>
|
||||||
<Button className="mt-2 w-full">
|
<Button disabled={loading} className="mt-2 w-full" on:click={useLocalData}>
|
||||||
<Icon path={mdiUpload} className="mr-1" />Replace Google Drive Data
|
<Icon path={loading ? mdiLoading : mdiUpload} spin={loading} className="mr-1" />Replace Google Drive Data
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex mt-6 justify-end">
|
<div class="flex mt-6 justify-end">
|
||||||
|
|
|
@ -30,10 +30,11 @@
|
||||||
<Sidebar {segment} mobile />
|
<Sidebar {segment} mobile />
|
||||||
{/if}
|
{/if}
|
||||||
<Modal>
|
<Modal>
|
||||||
<main style="flex: 1 0 auto;">
|
<DataSync>
|
||||||
<slot />
|
<main style="flex: 1 0 auto;">
|
||||||
</main>
|
<slot />
|
||||||
<DataSync />
|
</main>
|
||||||
|
</DataSync>
|
||||||
</Modal>
|
</Modal>
|
||||||
<p class="lg:ml-64 px-8 py-4 text-gray-600">
|
<p class="lg:ml-64 px-8 py-4 text-gray-600">
|
||||||
Paimon.moe is not affiliated with miHoYo.<br />
|
Paimon.moe is not affiliated with miHoYo.<br />
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
function readLocalData() {
|
function readLocalData() {
|
||||||
|
console.log('wish read local');
|
||||||
const data = readSave(path);
|
const data = readSave(path);
|
||||||
if (data !== null) {
|
if (data !== null) {
|
||||||
const counterData = JSON.parse(data);
|
const counterData = JSON.parse(data);
|
||||||
|
|
|
@ -2,5 +2,6 @@ import { writable } from 'svelte/store';
|
||||||
|
|
||||||
export const driveSignedIn = writable(false);
|
export const driveSignedIn = writable(false);
|
||||||
export const driveLoading = writable(true);
|
export const driveLoading = writable(true);
|
||||||
export const synced = writable(true);
|
export const lastSyncTime = writable(null);
|
||||||
|
export const synced = writable(false);
|
||||||
export const saveId = writable('');
|
export const saveId = writable('');
|
||||||
|
|
|
@ -1,11 +1,25 @@
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
import { synced } from './dataSync';
|
||||||
|
|
||||||
export const updateTime = writable(null);
|
export const updateTime = writable(null);
|
||||||
export const fromRemote = writable(false);
|
export const fromRemote = writable(false);
|
||||||
|
|
||||||
export const UPDATE_TIME_KEY = 'update-time';
|
export const UPDATE_TIME_KEY = 'update-time';
|
||||||
|
|
||||||
|
let pendingQueue = [];
|
||||||
|
let queueSave = true;
|
||||||
|
|
||||||
|
const unsubscribe = synced.subscribe((value) => {
|
||||||
|
console.log('synced:', value);
|
||||||
|
queueSave = !value;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
flushPendingQueue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export const checkLocalSave = () => {
|
export const checkLocalSave = () => {
|
||||||
const localUpdateTime = localStorage.getItem(UPDATE_TIME_KEY);
|
const localUpdateTime = localStorage.getItem(UPDATE_TIME_KEY);
|
||||||
if (localUpdateTime !== null) {
|
if (localUpdateTime !== null) {
|
||||||
|
@ -19,6 +33,11 @@ export const getLocalSaveJson = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateSave = (key, data, isFromRemote) => {
|
export const updateSave = (key, data, isFromRemote) => {
|
||||||
|
if (queueSave && !isFromRemote) {
|
||||||
|
pendingQueue.push({ key, data });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
localStorage.setItem(key, data);
|
localStorage.setItem(key, data);
|
||||||
|
|
||||||
if (!isFromRemote) {
|
if (!isFromRemote) {
|
||||||
|
@ -34,3 +53,16 @@ export const readSave = (key) => {
|
||||||
const data = localStorage.getItem(key);
|
const data = localStorage.getItem(key);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const flushPendingQueue = () => {
|
||||||
|
console.log('flushing save queue');
|
||||||
|
console.log(pendingQueue);
|
||||||
|
|
||||||
|
for (const item of pendingQueue) {
|
||||||
|
updateSave(item.key, item.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingQueue = [];
|
||||||
|
queueSave = false;
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue