WIP: data conflict resolve

pull/1/head
I Made Setia Baruna 2020-11-06 15:24:41 +07:00
parent 925355bc32
commit 8567c6b831
6 changed files with 99 additions and 14 deletions

View File

@ -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 />

View File

@ -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">

View File

@ -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 />

View File

@ -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);

View File

@ -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('');

View File

@ -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();
};