2022-08-06 04:25:16 +08:00
|
|
|
<script>
|
2022-08-06 13:03:34 +08:00
|
|
|
import { createEventDispatcher } from "svelte";
|
|
|
|
import ApiClient from "@/utils/ApiClient";
|
2022-08-06 23:15:18 +08:00
|
|
|
import CommonHelper from "@/utils/CommonHelper";
|
2022-08-06 04:25:16 +08:00
|
|
|
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
|
2022-08-06 13:03:34 +08:00
|
|
|
import { addSuccessToast } from "@/stores/toasts";
|
2022-08-06 04:25:16 +08:00
|
|
|
|
2022-08-06 13:03:34 +08:00
|
|
|
const dispatch = createEventDispatcher();
|
2022-08-06 04:25:16 +08:00
|
|
|
|
|
|
|
let panel;
|
2022-08-06 13:03:34 +08:00
|
|
|
let oldCollections = [];
|
|
|
|
let newCollections = [];
|
2022-08-06 23:15:18 +08:00
|
|
|
let changes = [];
|
2022-08-06 13:03:34 +08:00
|
|
|
let isImporting = false;
|
2022-08-06 04:25:16 +08:00
|
|
|
|
2022-08-06 23:15:18 +08:00
|
|
|
$: if (Array.isArray(oldCollections) && Array.isArray(newCollections)) {
|
|
|
|
loadChanges();
|
|
|
|
}
|
|
|
|
|
2022-08-06 04:25:16 +08:00
|
|
|
export function show(a, b) {
|
2022-08-06 13:03:34 +08:00
|
|
|
oldCollections = a;
|
|
|
|
newCollections = b;
|
2022-08-06 04:25:16 +08:00
|
|
|
|
|
|
|
panel?.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
export function hide() {
|
|
|
|
return panel?.hide();
|
|
|
|
}
|
|
|
|
|
2022-08-06 23:15:18 +08:00
|
|
|
function loadChanges() {
|
|
|
|
changes = [];
|
|
|
|
|
|
|
|
// add deleted and modified collections
|
|
|
|
for (const oldCollection of oldCollections) {
|
|
|
|
const newCollection = CommonHelper.findByKey(newCollections, "id", oldCollection.id) || null;
|
|
|
|
if (!newCollection?.id || JSON.stringify(oldCollection) != JSON.stringify(newCollection)) {
|
|
|
|
changes.push({
|
|
|
|
old: oldCollection,
|
|
|
|
new: newCollection,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add only new collections
|
|
|
|
for (const newCollection of newCollections) {
|
|
|
|
const oldCollection = CommonHelper.findByKey(oldCollections, "id", newCollection.id) || null;
|
|
|
|
if (!oldCollection?.id) {
|
|
|
|
changes.push({
|
|
|
|
old: oldCollection,
|
|
|
|
new: newCollection,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-06 13:03:34 +08:00
|
|
|
function diffsToHtml(diffs, ops = [window.DIFF_INSERT, window.DIFF_DELETE, window.DIFF_EQUAL]) {
|
2022-08-06 04:25:16 +08:00
|
|
|
const html = [];
|
|
|
|
const pattern_amp = /&/g;
|
|
|
|
const pattern_lt = /</g;
|
|
|
|
const pattern_gt = />/g;
|
|
|
|
const pattern_para = /\n/g;
|
|
|
|
|
|
|
|
for (let i = 0; i < diffs.length; i++) {
|
|
|
|
const op = diffs[i][0]; // operation (insert, delete, equal)
|
|
|
|
|
|
|
|
if (!ops.includes(op)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const text = diffs[i][1]
|
|
|
|
.replace(pattern_amp, "&")
|
|
|
|
.replace(pattern_lt, "<")
|
|
|
|
.replace(pattern_gt, ">")
|
|
|
|
.replace(pattern_para, "<br>");
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case DIFF_INSERT:
|
|
|
|
html[i] = '<ins class="block">' + text + "</ins>";
|
|
|
|
break;
|
|
|
|
case DIFF_DELETE:
|
|
|
|
html[i] = '<del class="block">' + text + "</del>";
|
|
|
|
break;
|
|
|
|
case DIFF_EQUAL:
|
|
|
|
html[i] = text;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return html.join("");
|
|
|
|
}
|
|
|
|
|
2022-08-06 23:15:18 +08:00
|
|
|
function diff(obj1, obj2, ops = [window.DIFF_INSERT, window.DIFF_DELETE, window.DIFF_EQUAL]) {
|
2022-08-06 04:25:16 +08:00
|
|
|
const dmp = new diff_match_patch();
|
2022-08-06 13:03:34 +08:00
|
|
|
const lines = dmp.diff_linesToChars_(
|
2022-08-06 23:15:18 +08:00
|
|
|
obj1 ? JSON.stringify(obj1, null, 4) : "",
|
|
|
|
obj2 ? JSON.stringify(obj2, null, 4) : ""
|
2022-08-06 13:03:34 +08:00
|
|
|
);
|
2022-08-06 04:25:16 +08:00
|
|
|
const diffs = dmp.diff_main(lines.chars1, lines.chars2, false);
|
|
|
|
|
|
|
|
dmp.diff_charsToLines_(diffs, lines.lineArray);
|
|
|
|
|
|
|
|
return diffsToHtml(diffs, ops);
|
|
|
|
}
|
2022-08-06 13:03:34 +08:00
|
|
|
|
|
|
|
async function submitImport() {
|
|
|
|
if (isImporting) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
isImporting = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
await ApiClient.collections.import(newCollections);
|
2022-08-06 23:15:18 +08:00
|
|
|
addSuccessToast("Successfully imported the provided collections.");
|
2022-08-06 13:03:34 +08:00
|
|
|
dispatch("submit");
|
|
|
|
} catch (err) {
|
|
|
|
ApiClient.errorResponseHandler(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
hide();
|
|
|
|
|
|
|
|
isImporting = false;
|
|
|
|
}
|
2022-08-06 04:25:16 +08:00
|
|
|
</script>
|
|
|
|
|
2022-08-06 23:15:18 +08:00
|
|
|
<OverlayPanel
|
|
|
|
bind:this={panel}
|
|
|
|
class="full-width-popup import-popup"
|
|
|
|
overlayClose={false}
|
|
|
|
popup
|
|
|
|
on:show
|
|
|
|
on:hide
|
|
|
|
>
|
2022-08-06 04:25:16 +08:00
|
|
|
<svelte:fragment slot="header">
|
2022-08-06 13:03:34 +08:00
|
|
|
<h4 class="center txt-break">Side-by-side diff</h4>
|
2022-08-06 04:25:16 +08:00
|
|
|
</svelte:fragment>
|
|
|
|
|
2022-08-06 23:15:18 +08:00
|
|
|
<div class="grid grid-sm m-b-sm">
|
|
|
|
{#each changes as pair (pair.old?.id + pair.new?.id)}
|
|
|
|
<div class="col-12">
|
|
|
|
<div class="flex flex-gap-10">
|
|
|
|
{#if !pair.old?.id}
|
|
|
|
<span class="label label-success">New</span>
|
|
|
|
<strong>{pair.new?.name}</strong>
|
|
|
|
{:else if !pair.new?.id}
|
|
|
|
<span class="label label-danger">Deleted</span>
|
|
|
|
<strong>{pair.old?.name}</strong>
|
|
|
|
{:else}
|
|
|
|
<span class="label label-warning">Modified</span>
|
|
|
|
<div class="inline-flex fleg-gap-5">
|
|
|
|
{#if pair.old.name !== pair.new.name}
|
|
|
|
<strong class="txt-strikethrough txt-hint">{pair.old.name}</strong>
|
|
|
|
<i class="ri-arrow-right-line txt-sm" />
|
|
|
|
{/if}
|
|
|
|
<strong class="txt">{pair.new.name}</strong>
|
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="col-6 p-b-10">
|
|
|
|
<code class="code-block">
|
|
|
|
{@html diff(pair.old, pair.new, [window.DIFF_DELETE, window.DIFF_EQUAL])}
|
|
|
|
</code>
|
|
|
|
</div>
|
|
|
|
<div class="col-6 p-b-10">
|
|
|
|
<code class="code-block">
|
|
|
|
{@html diff(pair.old, pair.new, [window.DIFF_INSERT, window.DIFF_EQUAL])}
|
|
|
|
</code>
|
|
|
|
</div>
|
|
|
|
{/each}
|
2022-08-06 04:25:16 +08:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<svelte:fragment slot="footer">
|
|
|
|
<button type="button" class="btn btn-secondary" on:click={hide}>Close</button>
|
2022-08-06 13:03:34 +08:00
|
|
|
<button
|
|
|
|
type="button"
|
2022-08-06 23:15:18 +08:00
|
|
|
class="btn btn-expanded"
|
2022-08-06 13:03:34 +08:00
|
|
|
class:btn-loading={isImporting}
|
|
|
|
on:click={() => submitImport()}
|
|
|
|
>
|
|
|
|
<span class="txt">Confirm and import</span>
|
|
|
|
</button>
|
2022-08-06 04:25:16 +08:00
|
|
|
</svelte:fragment>
|
|
|
|
</OverlayPanel>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
code {
|
|
|
|
color: var(--txtHintColor);
|
|
|
|
min-height: 100%;
|
|
|
|
}
|
|
|
|
</style>
|