Skip to content

Commit

Permalink
refactor(app): media view and note open logic
Browse files Browse the repository at this point in the history
  • Loading branch information
aidenlx committed Jan 19, 2024
1 parent 4777e1d commit adab478
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 195 deletions.
3 changes: 1 addition & 2 deletions apps/app/src/media-note/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { MEDIA_WEBPAGE_VIEW_TYPE } from "@/media-view/webpage-view";
import type MxPlugin from "@/mx-main";
import { parseUrl } from "./manager/url-info";
import { takeTimestampOnFile, takeTimestampOnUrl } from "./timestamp";
import { openMedia } from "./utils";

export function handleMediaNote(this: MxPlugin) {
const { workspace } = this.app;
Expand All @@ -24,7 +23,7 @@ export function handleMediaNote(this: MxPlugin) {
.setSection("view")
.setIcon("play")
.setTitle("Open linked media")
.onClick(() => openMedia(mediaInfo, this.app)),
.onClick(() => this.leafOpener.openMedia(mediaInfo)),
);
}),
);
Expand Down
166 changes: 166 additions & 0 deletions apps/app/src/media-note/leaf-open/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/* eslint-disable deprecation/deprecation */
import type {
PaneType,
App,
SplitDirection,
WorkspaceLeaf,
TFile,
MarkdownView,
Editor,
} from "obsidian";
import type { MediaEmbedViewState } from "@/media-view/iframe-view";
import type { MediaUrlViewState } from "@/media-view/url-view";
import type { MediaWebpageViewState } from "@/media-view/webpage-view";
import type MxPlugin from "@/mx-main";
import type { MediaInfo } from "../manager";
import { isFileMediaInfo } from "../manager/file-info";
import type { UrlMediaInfo } from "../manager/url-info";
import { filterFileLeaf, filterUrlLeaf, sortByMtime } from "./utils";

interface NewNoteInfo {
title: string;
fm: (newNotePath: string) => Record<string, any>;
sourcePath?: string;
}

export class LeafOpener {
app: App;
constructor(public plugin: MxPlugin) {
this.app = plugin.app;
}

async openMedia(
mediaInfo: MediaInfo,
newLeaf?: "split",
direction?: SplitDirection,
): Promise<void>;
async openMedia(
mediaInfo: MediaInfo,
newLeaf?: PaneType | boolean,
): Promise<void>;
async openMedia(
mediaInfo: MediaInfo,
newLeaf?: PaneType | boolean,
direction?: SplitDirection,
): Promise<void> {
const { workspace } = this.app;
if (!newLeaf && this.#openInExistingPlayer(mediaInfo)) {
return;
}
const leaf = workspace.getLeaf(newLeaf as any, direction);
if (isFileMediaInfo(mediaInfo)) {
await leaf.openFile(mediaInfo.file, {
eState: { subpath: mediaInfo.hash },
});
} else {
await openInLeaf(mediaInfo, leaf);
}
}

#getLeavesOfMedia(info: MediaInfo) {
const { workspace } = this.app;

return workspace.getLeavesOfType(info.viewType).filter((leaf) => {
if (isFileMediaInfo(info)) {
return filterFileLeaf(leaf, info);
} else {
return filterUrlLeaf(leaf, info);
}
});
}

#openInExistingPlayer(info: MediaInfo): boolean {
const opened = this.#getLeavesOfMedia(info);
if (opened.length > 0) {
updateHash(info.hash, opened[0]);
return true;
}
return false;
}

async openNote(
mediaInfo: MediaInfo,
newNoteInfo: NewNoteInfo,
newLeaf?: "split",
direction?: SplitDirection,
): Promise<{ file: TFile; editor: Editor }>;
async openNote(
mediaInfo: MediaInfo,
newNoteInfo: NewNoteInfo,
newLeaf?: PaneType | boolean,
): Promise<{ file: TFile; editor: Editor }>;
async openNote(
mediaInfo: MediaInfo,
newNoteInfo: NewNoteInfo,
newLeaf?: PaneType | boolean,
direction?: SplitDirection,
): Promise<{ file: TFile; editor: Editor }> {
const notes = this.plugin.mediaNote.findNotes(mediaInfo);
if (!newLeaf) {
const opened = this.#getOpenedNote(notes);
if (opened) return opened;
}

let targetNote: TFile;
if (notes.length === 0) {
const filename = `Media Note - ${newNoteInfo.title}.md`;
targetNote = await this.#createNewNote(
filename,
newNoteInfo.fm,
newNoteInfo.sourcePath ?? "",
);
} else {
targetNote = notes.sort(sortByMtime).at(0)!;
}

const leaf = this.app.workspace.getLeaf(newLeaf as any, direction);
await leaf.openFile(targetNote);
return {
file: targetNote,
editor: (leaf.view as MarkdownView).editor,
};
}

#getOpenedNote(notes: TFile[]): { file: TFile; editor: Editor } | null {
const { workspace } = this.app;
const opened = workspace.getLeavesOfType("markdown").filter((leaf) => {
const filePath = (leaf.view as MarkdownView).file?.path;
return !!filePath && notes.some((note) => note.path === filePath);
}) as (WorkspaceLeaf & { view: MarkdownView & { file: TFile } })[];
if (opened.length === 0) return null;
const view = (
opened.find((leaf) => leaf === workspace.activeLeaf) ??
opened.sort((a, b) => sortByMtime(a.view.file, b.view.file)).at(0)!
).view;
return { file: view.file, editor: view.editor };
}
async #createNewNote(
filename: string,
fm: (sourcePath: string) => Record<string, any>,
sourcePath = "",
) {
const { fileManager, vault } = this.app;
const folder = fileManager.getNewFileParent(sourcePath, filename);
const newNote = await vault.create(`${folder.path}/${filename}`, "");
await fileManager.processFrontMatter(newNote, (fn) => {
Object.assign(fn, fm(newNote.path));
});
return newNote;
}
}

function updateHash(hash: string, leaf: WorkspaceLeaf) {
leaf.setEphemeralState({ subpath: hash });
}
async function openInLeaf(info: UrlMediaInfo, leaf: WorkspaceLeaf) {
const state: MediaEmbedViewState | MediaWebpageViewState | MediaUrlViewState =
{ source: info.original };
await leaf.setViewState(
{
type: info.viewType,
state,
active: true,
},
{ subpath: info.hash },
);
}
45 changes: 45 additions & 0 deletions apps/app/src/media-note/leaf-open/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { TFile, WorkspaceLeaf } from "obsidian";
import { AudioFileView, VideoFileView } from "@/media-view/file-view";
import { MediaEmbedView } from "@/media-view/iframe-view";
import { VideoUrlView, AudioUrlView } from "@/media-view/url-view";
import { MediaWebpageView } from "@/media-view/webpage-view";
import { type FileMediaInfo } from "../manager/file-info";
import type { UrlMediaInfo } from "../manager/url-info";

export function filterFileLeaf(
leaf: WorkspaceLeaf,
info: FileMediaInfo,
): boolean {
if (
!(leaf.view instanceof VideoFileView || leaf.view instanceof AudioFileView)
) {
return false;
}
const { file: filePath } = leaf.view.getState() as { file: string };
return filePath === info.file.path;
}

export function filterUrlLeaf(
leaf: WorkspaceLeaf,
info: UrlMediaInfo,
): boolean {
if (
!(
leaf.view instanceof MediaEmbedView ||
leaf.view instanceof MediaWebpageView ||
leaf.view instanceof VideoUrlView ||
leaf.view instanceof AudioUrlView
)
) {
return false;
}
const { source } = leaf.view.getState();
return !!source && info.isSameSource(source);
}

export function sortByMtime(a: TFile, b: TFile) {
const aDate = a.stat.mtime;
const bDate = b.stat.mtime;
if (!aDate || !bDate) return 0;
return bDate - aDate;
}
30 changes: 1 addition & 29 deletions apps/app/src/media-note/link-click/external.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,16 @@
import type { WorkspaceLeaf } from "obsidian";
import type { MediaEmbedViewState } from "@/media-view/iframe-view";
import type { MediaWebpageViewState } from "@/media-view/webpage-view";
import type MxPlugin from "@/mx-main";
import type { UrlMediaInfo } from "../manager/url-info";
import { parseUrl } from "../manager/url-info";
import { openInOpenedPlayer } from "./opened";

export async function onExternalLinkClick(
this: MxPlugin,
url: string,
newLeaf: boolean,
fallback: () => void,
) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const { workspace } = this.app;

const urlInfo = parseUrl(url);
if (!urlInfo) {
fallback();
return;
}
if (!newLeaf && openInOpenedPlayer(urlInfo, workspace)) return;

const leaf = newLeaf
? workspace.getLeaf("split", "vertical")
: workspace.getLeaf(false);

await openInLeaf(urlInfo, leaf);
}

export async function openInLeaf(info: UrlMediaInfo, leaf: WorkspaceLeaf) {
const state: MediaEmbedViewState | MediaWebpageViewState = {
source: info.original,
};
await leaf.setViewState(
{
type: info.viewType,
state,
active: true,
},
{ subpath: info.hash },
);
await this.leafOpener.openMedia(urlInfo, newLeaf);
}
10 changes: 3 additions & 7 deletions apps/app/src/media-note/link-click/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import { parseLinktext } from "obsidian";
import { MEDIA_FILE_VIEW_TYPE } from "@/media-view/file-view";
import type MxPlugin from "@/mx-main";
import { checkMediaType } from "@/patch/utils";
import { openInOpenedPlayer } from "./opened";

export function onInternalLinkClick(
export async function onInternalLinkClick(
this: MxPlugin,
linktext: string,
sourcePath: string,
newLeaf: boolean,
fallback: () => void,
) {
const { workspace, metadataCache } = this.app;
const { metadataCache } = this.app;
const { path: linkpath, subpath } = parseLinktext(linktext);
const linkFile = metadataCache.getFirstLinkpathDest(linkpath, sourcePath);
let mediaType;
Expand All @@ -25,8 +24,5 @@ export function onInternalLinkClick(
type: mediaType,
viewType: MEDIA_FILE_VIEW_TYPE[mediaType],
};
if (!newLeaf && openInOpenedPlayer(mediaInfo, workspace)) {
return;
}
fallback();
await this.leafOpener.openMedia(mediaInfo, newLeaf);
}
46 changes: 0 additions & 46 deletions apps/app/src/media-note/link-click/opened.ts

This file was deleted.

Loading

0 comments on commit adab478

Please sign in to comment.