Skip to content

Commit f63a1aa

Browse files
committed
feat(diff): Conflict view files.
1 parent f4cf393 commit f63a1aa

File tree

13 files changed

+283
-20
lines changed

13 files changed

+283
-20
lines changed

src/diff/abstract_diff_view.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type InvioPlugin from '../main';
77
import type { LangType, LangTypeAndAuto, TransItemType } from "../i18n";
88

99
type TviewOutputFormat = `side-by-side` | `line-by-line`
10-
export type TDiffType = `LocalToRemote` | `RemoteToLocal`
10+
export type TDiffType = `LocalToRemote` | `RemoteToLocal` | `Conflict`
1111

1212
export default abstract class DiffView extends Modal {
1313
plugin: InvioPlugin;
@@ -183,7 +183,7 @@ export default abstract class DiffView extends Modal {
183183
cls: 'sync-history-list',
184184
});
185185
const title = syncHistoryList.createDiv({
186-
cls: 'sync-history-list-item title',
186+
cls: ['sync-history-list-item', 'title'],
187187
text: this.t('diff_edit_list')
188188
});
189189

@@ -206,7 +206,7 @@ export default abstract class DiffView extends Modal {
206206

207207
public basicHtml(diff: string, diffType: TDiffType): void {
208208
// set title
209-
this.titleEl.setText(diffType === `LocalToRemote` ? this.t('diff_view_local_title') : this.t('diff_view_remote_title'));
209+
this.titleEl.setText(diffType === `LocalToRemote` ? this.t('diff_view_local_title') : (diffType === `RemoteToLocal` ? this.t('diff_view_remote_title') : this.t('diff_view_conflict_title')));
210210
// add diff to container
211211
this.syncHistoryContentContainer.innerHTML = diff;
212212

src/diff/conflict_diff_view.ts

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import {
2+
Plugin,
3+
App,
4+
TFile,
5+
Notice,
6+
// @ts-ignore
7+
setTooltip
8+
} from 'obsidian';
9+
import type InvioPlugin from '../main';
10+
import type { recResult, vRecoveryItem } from './interfaces';
11+
import { FILE_REC_WARNING } from './constants';
12+
import DiffView, { TDiffType } from './abstract_diff_view';
13+
14+
export default class ConflictDiffView extends DiffView {
15+
remote: recResult
16+
versions: recResult[];
17+
rightVList: vRecoveryItem[];
18+
constructor(plugin: InvioPlugin, app: App, file: TFile, remoteFile: recResult, fileChangedHook: (f: TFile) => void, cancelHook: () => void) {
19+
super(plugin, app, file, fileChangedHook, cancelHook);
20+
this.versions = [];
21+
this.rightVList = [];
22+
this.remote = remoteFile;
23+
this.leftContent = remoteFile?.data;
24+
this.leftName = 'remote version'
25+
this.rightName = 'local version'
26+
this.htmlConfig = {
27+
drawFileList: true,
28+
diffStyle: 'word',
29+
matchWordsThreshold: 0.25,
30+
outputFormat: this.viewOutputFormat,
31+
rawTemplates: {
32+
'line-by-line-file-diff': `<div id="{{fileHtmlId}}" class="d2h-file-wrapper" data-lang="{{file.language}}">
33+
<div class="d2h-file-header">
34+
{{{filePath}}}
35+
</div>
36+
<div class="d2h-file-diff">
37+
<div class="d2h-code-wrapper">
38+
<table class="d2h-diff-table">
39+
<tbody class="d2h-diff-tbody">
40+
{{{diffs}}}
41+
</tbody>
42+
</table>
43+
</div>
44+
</div>
45+
</div>`,
46+
'side-by-side-file-diff': `<div id="{{fileHtmlId}}" class="d2h-file-wrapper" data-lang="{{file.language}}">
47+
<div class="d2h-files-diff">
48+
<div class="d2h-file-side-diff">
49+
<div class="d2h-code-title">${this.leftName}</div>
50+
<div class="d2h-code-wrapper">
51+
<table class="d2h-diff-table">
52+
<tbody class="d2h-diff-tbody">
53+
{{{diffs.left}}}
54+
</tbody>
55+
</table>
56+
</div>
57+
</div>
58+
<div class="d2h-file-side-diff">
59+
<div class="d2h-code-title">${this.rightName}</div>
60+
<div class="d2h-code-wrapper">
61+
<table class="d2h-diff-table">
62+
<tbody class="d2h-diff-tbody">
63+
{{{diffs.right}}}
64+
</tbody>
65+
</table>
66+
</div>
67+
</div>
68+
</div>
69+
</div>`
70+
}
71+
};
72+
}
73+
74+
async onOpen() {
75+
super.onOpen();
76+
await this.getInitialVersions();
77+
const diff = this.getDiff();
78+
this.makeHistoryLists(FILE_REC_WARNING);
79+
this.basicHtml(diff as string);
80+
this.appendVersions();
81+
this.makeMoreGeneralHtml();
82+
}
83+
84+
public basicHtml(diff: string): void {
85+
// set title
86+
this.titleEl.setText(this.t('diff_view_conflict_title'));
87+
// set top action bar
88+
89+
const contentParent = this.syncHistoryContentContainer.parentElement;
90+
const topAction = contentParent.createDiv({
91+
cls: 'sync-history-content-container-top'
92+
});
93+
94+
const viewChangeBtn = topAction.createDiv({
95+
cls: ['view-action', 'btn'],
96+
text: this.t('view_change_btn')
97+
})
98+
99+
const diffResetBtn = topAction.createDiv({
100+
cls: ['view-action', 'btn'],
101+
text: this.t('diff_reset_btn')
102+
})
103+
setTooltip(diffResetBtn, 'Click to replace the file with online version', {
104+
placement: 'top',
105+
});
106+
diffResetBtn.addEventListener('click', e => {
107+
e.preventDefault();
108+
this.changeFileAndCloseModal(this.leftContent);
109+
110+
new Notice(
111+
`The ${this.file.basename} file has been overwritten with the online remote version.`
112+
);
113+
})
114+
setTooltip(viewChangeBtn, 'Click to change diff view', {
115+
placement: 'top',
116+
});
117+
viewChangeBtn.addEventListener('click', e => {
118+
e.preventDefault();
119+
this.viewOutputFormat = ('line-by-line' === this.viewOutputFormat) ? 'side-by-side' : 'line-by-line';
120+
console.log('diff styles changed to ', this.viewOutputFormat)
121+
this.reload({
122+
outputFormat: this.viewOutputFormat
123+
});
124+
})
125+
126+
// add diff to container
127+
this.syncHistoryContentContainer.innerHTML = diff;
128+
129+
// add history lists and diff to DOM
130+
// this.contentEl.appendChild(this.leftHistory[0]);
131+
this.contentEl.appendChild(this.syncHistoryContentContainer?.parentNode);
132+
this.contentEl.appendChild(this.rightHistory[0]);
133+
}
134+
135+
async getInitialVersions() {
136+
const fileRecovery = await this.app.internalPlugins.plugins[
137+
'file-recovery'
138+
].instance.db
139+
.transaction('backups', 'readonly')
140+
.store.index('path')
141+
.getAll();
142+
const fileContent = await this.app.vault.read(this.file);
143+
// correct date is calculated later
144+
this.versions.push({ path: this.file.path, ts: Date.now(), data: fileContent });
145+
const len = fileRecovery.length - 1;
146+
for (let i = len; i >= 0; i--) {
147+
const version = fileRecovery[i];
148+
if (version.path === this.file.path) {
149+
this.versions.push(version);
150+
}
151+
}
152+
if (!(this.versions.length > 1)) {
153+
this.close();
154+
new Notice(
155+
'There is not at least on version in the file recovery.'
156+
);
157+
return;
158+
}
159+
160+
// insert remote record
161+
const remoteUpdateTS = this.remote.ts;
162+
const idx = this.versions.findIndex(item => item.ts <= remoteUpdateTS)
163+
this.versions.splice(idx, 0, {
164+
...this.remote,
165+
isRemote: true
166+
})
167+
this.rightContent = this.versions[0].data;
168+
}
169+
170+
appendVersions() {
171+
// add the inner HTML element (the sync list) and keep a record
172+
// of references to the elements
173+
this.rightVList.push(
174+
...this.appendRecoveryVersions(
175+
this.rightHistory[1],
176+
this.versions,
177+
)
178+
);
179+
}
180+
181+
private appendRecoveryVersions(
182+
el: HTMLElement,
183+
versions: recResult[],
184+
): vRecoveryItem[] {
185+
const versionList: vRecoveryItem[] = [];
186+
for (let i = 0; i < versions.length; i++) {
187+
const version = versions[i];
188+
let date = new Date(version.ts);
189+
190+
let div = el.createDiv({
191+
cls: 'sync-history-list-item',
192+
attr: {
193+
id: this.ids.right,
194+
},
195+
});
196+
197+
this.ids.right += 1;
198+
if (i === 0) {
199+
div.createDiv({ text: this.t('diff_version_list_title') });
200+
} else if (version.isRemote) {
201+
div.createDiv({
202+
cls: 'sync-history-list-item-remote',
203+
text: 'Remote Updated At: ' + date.toLocaleString(),
204+
});
205+
} else {
206+
div.createDiv({
207+
text: date.toLocaleString(),
208+
});
209+
}
210+
versionList.push({
211+
html: div,
212+
data: version.data,
213+
});
214+
if (!version.isRemote) {
215+
div.addEventListener('click', async () => {
216+
const clickedEl = (await this.generateVersionListener(
217+
div,
218+
this.rightVList,
219+
this.rightActive
220+
)) as vRecoveryItem;
221+
this.rightContent = version.data;
222+
this.syncHistoryContentContainer.innerHTML =
223+
this.getDiff() as string;
224+
});
225+
}
226+
}
227+
return versionList;
228+
}
229+
}

src/diff/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { App, TFile, Notice, Vault } from 'obsidian';
22
import { diffChars } from 'diff';
33
import LocalToRemoteDiffView, { IRemoteFile } from './local_to_remote_diff_view';
44
import RemoteToLocalDiffView from './remote_to_local_diff_view';
5+
import ConflictDiffView from './conflict_diff_view';
56
import { TDiffType } from './abstract_diff_view';
67
import type InvioPlugin from '../main';
78

@@ -12,6 +13,9 @@ export function openDiffModal(app: App, plugin: InvioPlugin, file: TFile, remote
1213
new LocalToRemoteDiffView(plugin, app, file, remoteFile, hook, hook).open();
1314
} else if (diffType === `RemoteToLocal`) {
1415
new RemoteToLocalDiffView(plugin, app, file, remoteFile, hook, hook).open();
16+
} else if (diffType === `Conflict`) {
17+
// 计算文件更改时间,对比local和remote文件状态
18+
new ConflictDiffView(plugin, app, file, remoteFile, hook, hook).open();
1519
} else {
1620
new Notice(`Not supported diff view type`);
1721
}

src/diff/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export interface recResult {
7070
path: string;
7171
ts: number;
7272
data: string;
73+
isRemote?: boolean;
7374
}
7475

7576
export interface vItem {

src/langs/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
"diff_view_local_title": "Local File Contents Edit Diff",
1010
"diff_view_remote_title": "Remote File Contents Edit Diff",
11+
"diff_view_conflict_title": "Conflict File Contents Edit Diff",
1112
"diff_reset_btn": "Discard Local Changes",
1213
"view_change_btn": "ViewStyle",
1314
"diff_edit_list": "Local Edit Version List",

src/langs/zh_cn.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
"diff_view_local_title": "本地文件修改记录",
1010
"diff_view_remote_title": "线上文件修改记录",
11+
"diff_view_conflict_title": "冲突文件修改记录",
1112
"diff_reset_btn": "丢弃本地所有更改",
1213
"view_change_btn": "diff样式",
1314
"diff_edit_list": "本地编辑历史",

src/langs/zh_tw.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
"diff_view_local_title": "本地文件修改记录",
1010
"diff_view_remote_title": "线上文件修改记录",
11+
"diff_view_conflict_title": "冲突文件修改记录",
1112
"diff_reset_btn": "丢弃本地所有更改",
1213
"view_change_btn": "diff样式",
1314
"diff_edit_list": "本地编辑历史",

src/main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ export default class InvioPlugin extends Plugin {
128128
log.info('remote md: ', remoteMD);
129129
return new Promise((resolve, reject) => {
130130
openDiffModal(this.app, this, file, {
131-
data: remoteMD,
132-
ts: 0,
131+
data: remoteMD.data,
132+
ts: remoteMD?.lastModified,
133133
path: filePath
134134
}, diffType, (file: TFile) => {
135135
if (file) {

src/remote.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ export class RemoteClient {
187187
}
188188
};
189189

190+
getRemoteMeta = async (fileOrFolderPath: string) => {
191+
const s3Client = await s3.getS3Client(this.s3Config, this.hostConfig, this.useHost, this.localWatchDir);
192+
return await s3.getRemoteMeta(s3Client, this.s3Config, fileOrFolderPath);
193+
}
194+
190195
downloadFromRemote = async (
191196
fileOrFolderPath: string,
192197
prefix: string,

src/styles/diff-styles.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ mode from @SlRvb, which fall under the same license (MIT license, @kometenstaub
481481
/*max-width: 11vw;*/
482482
padding: 0.5rem;
483483
border-radius: 0.25em;
484+
width: auto;
484485
}
485486

486487
.sync-history-list-container {
@@ -512,6 +513,12 @@ mode from @SlRvb, which fall under the same license (MIT license, @kometenstaub
512513
.sync-history-list-item {
513514
border-radius: 0.25em;
514515
cursor: pointer;
516+
.disbled {
517+
opacity: 0.6;
518+
}
519+
}
520+
.sync-history-list-item-remote {
521+
opacity: 0.3;
515522
}
516523
.title {
517524
text-align: center;

0 commit comments

Comments
 (0)