Skip to content

Commit

Permalink
release: 0.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
RyotaUshio committed Feb 18, 2024
1 parent 9d3c55a commit 06ca9ff
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 21 deletions.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "view-sync",
"name": "View Sync",
"version": "0.1.5",
"version": "0.2.0",
"minAppVersion": "1.3.5",
"description": "Sync the state of the active view, not files, among devices.",
"author": "Ryota Ushio",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "obsidian-view-sync",
"version": "0.1.5",
"version": "0.2.0",
"description": "An Obsidian.md plugin to sync the state of the active view, not files, among devices.",
"scripts": {
"dev": "node esbuild.config.mjs",
Expand Down
40 changes: 27 additions & 13 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ declare module 'obsidian' {
}
}

export default class MyPlugin extends Plugin {
export default class ViewSyncPlugin extends Plugin {
settings: ViewSyncSettings;
#lastViewStateSave: number = 0;
#lastWorkspaceLayoutSave: number = 0;

onload() {
this.loadSettings();
Expand Down Expand Up @@ -90,29 +92,36 @@ export default class MyPlugin extends Plugin {
// Make sure that only the active leaf's state is recorded
if (leaf !== this.app.workspace.activeLeaf) return;

const serialized = JSON.stringify(Object.assign(
leaf.getViewState(),
{ eState: view.getEphemeralState() }, // Ephemeral state is not included in the result of getViewState()
override
));
const timestamp = Date.now();
this.#lastViewStateSave = timestamp;
const serialized = JSON.stringify({
timestamp,
viewState: Object.assign(
leaf.getViewState(),
{ eState: view.getEphemeralState() }, // Ephemeral state is not included in the result of getViewState()
override
)
});
await this.writeFile(path, serialized);
}
}

/** Record the workspace layout to the specified file. */
onWorkspaceLayoutChange() {
async onWorkspaceLayoutChange() {
const path = normalizePath(this.settings.ownWorkspacePath);
// An empty string is normalized to `/`
if (path === '/') return;

const layout = this.app.workspace.getLayout();
const serialized = JSON.stringify(layout);
this.writeFile(path, serialized);
const timestamp = Date.now();
this.#lastWorkspaceLayoutSave = timestamp;
const serialized = JSON.stringify({ timestamp, layout });
await this.writeFile(path, serialized);
}

registerViewSyncEventPublisher() {
this.registerEvent(this.app.workspace.on('active-leaf-change', async (leaf) => {
if (leaf) await this.onViewStateChange(leaf.view);
this.registerEvent(this.app.workspace.on('active-leaf-change', (leaf) => {
if (leaf) this.onViewStateChange(leaf.view);
}));

this.registerEvent(this.app.workspace.on('view-sync:state-change', (view, override) => {
Expand All @@ -130,7 +139,9 @@ export default class MyPlugin extends Plugin {
if (!leaf) return;

const data = await this.app.vault.read(file);
const viewState = JSON.parse(data);
const { timestamp, viewState } = JSON.parse(data);

if (this.settings.syncOnlyIfNewer && this.#lastViewStateSave >= timestamp) return;

await leaf.setViewState(viewState);
if ('eState' in viewState) {
Expand Down Expand Up @@ -171,7 +182,10 @@ export default class MyPlugin extends Plugin {
this.registerEvent(this.app.vault.on('modify', async (file) => {
if (this.settings.watchAnotherWorkspace && file instanceof TFile && normalizePath(this.settings.watchWorkspacePath) === file.path) {
const data = await this.app.vault.read(file);
const layout = JSON.parse(data);
const { timestamp, layout } = JSON.parse(data);

if (this.settings.syncWorkspaceOnlyIfNewer && this.#lastWorkspaceLayoutSave >= timestamp) return;

await this.app.workspace.changeLayout(layout);
}
}));
Expand Down
18 changes: 14 additions & 4 deletions src/settings.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import { Component, IconName, MarkdownRenderer, Platform, PluginSettingTab, Setting, TFile, setIcon } from 'obsidian';
import MyPlugin from 'main';
import ViewSyncPlugin from 'main';


export interface ViewSyncSettings {
ownPath: string;
viewTypes: string[];
watchAnother: boolean;
watchPath: string;
syncOnlyIfNewer: boolean;
shareAfterSync: boolean;
ownWorkspacePath: string;
watchAnotherWorkspace: boolean;
watchWorkspacePath: string;
syncWorkspaceOnlyIfNewer: boolean;
}

export const DEFAULT_SETTINGS: ViewSyncSettings = {
ownPath: '',
viewTypes: ['markdown', 'canvas', 'pdf'],
watchAnother: false,
watchPath: '',
syncOnlyIfNewer: false,
shareAfterSync: false,
ownWorkspacePath: '',
watchAnotherWorkspace: false,
watchWorkspacePath: '',
syncWorkspaceOnlyIfNewer: false,
};

// Inspired by https://stackoverflow.com/a/50851710/13613783
Expand All @@ -32,7 +36,7 @@ export class ViewSyncSettingTab extends PluginSettingTab {
items: Partial<Record<keyof ViewSyncSettings, Setting>>;
promises: Promise<any>[];

constructor(public plugin: MyPlugin) {
constructor(public plugin: ViewSyncPlugin) {
super(plugin.app, plugin);
this.component = new Component();
this.items = {};
Expand Down Expand Up @@ -208,7 +212,7 @@ export class ViewSyncSettingTab extends PluginSettingTab {
.setDesc('Comma-separated list of view types to record. Other types of views will be ignored. Required if this device is the main device (= followed by other devices). Note that view types are case-sensitive. To get the type of the active view, you can run the "Copy active view type" command.');
this.addToggleSetting('watchAnother', () => this.redisplay())
.setName('Follow another device')
.setDesc('Note: It might be problematic if you let two devices follow each other. I recommend a one-way sync: one main device and one or more follower devices.');
.setDesc('Note: It might be problematic if you let two devices follow each other. I recommend a one-way sync: one main device and one or more follower devices. If you want them to follow each other, make sure to turn on the "Sync only if newer ..." option on both devices to avoid conflicts.');

if (this.settings.watchAnother) {
this.addPathSetting('watchPath', `view-sync-${exampleFollowedStr}.json`)
Expand All @@ -218,6 +222,9 @@ export class ViewSyncSettingTab extends PluginSettingTab {
.setName('Show "Share" menu after sync')
.setDesc('Useful for drawing on PDF files on tablet, for example.')
}
this.addToggleSetting('syncOnlyIfNewer')
.setName('Sync only if newer than this device\'s last view state update')
.setDesc('If this device has a newer view state than the followed device, the view state will not be updated.');
}

this.addHeading('Workspace sync', 'lucide-layout');
Expand All @@ -231,14 +238,17 @@ export class ViewSyncSettingTab extends PluginSettingTab {
this.renderMarkdown([
'Note: ',
'',
'- It might be problematic if you let two devices follow each other. I recommend a one-way sync: one main device and one or more follower devices.',
'- It might be problematic if you let two devices follow each other. I recommend a one-way sync: one main device and one or more follower devices. If you want them to follow each other, make sure to turn on the "Sync only if newer ..." option on both devices to avoid conflicts.',
'- It is not recommended to make a mobile device follow a desktop device or vice versa.',
], setting.descEl);
});

if (this.settings.watchAnotherWorkspace) {
this.addPathSetting('watchWorkspacePath', `workspace-sync-${exampleFollowedStr}.json`)
.setName('Path of the workspace layout file for the followed device');
this.addToggleSetting('syncWorkspaceOnlyIfNewer')
.setName('Sync only if newer than this device\'s last workspace layout update')
.setDesc('If this device has a newer workspace layout than the followed device, the workspace layout will not be updated.');
}

this.addFundingButton();
Expand Down

0 comments on commit 06ca9ff

Please sign in to comment.