mirror of
https://github.com/broekema41/obsidian-vcf-contacts.git
synced 2026-07-22 05:42:58 +00:00
started cleaning code for a maintainable release
This commit is contained in:
parent
c869420040
commit
78b258b6da
9 changed files with 46 additions and 35 deletions
|
|
@ -1,5 +0,0 @@
|
|||
import { App } from 'obsidian';
|
||||
import * as React from "react";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
export const AppContext = React.createContext<App>(undefined!);
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import { App } from "obsidian";
|
||||
import * as React from "react";
|
||||
import { AppContext } from "./context";
|
||||
|
||||
export const useApp = (): App => {
|
||||
return React.useContext(AppContext);
|
||||
};
|
||||
19
src/context/sharedAppContext.ts
Normal file
19
src/context/sharedAppContext.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { App } from 'obsidian'
|
||||
|
||||
let _app: App | undefined
|
||||
|
||||
export function setApp(app: App) {
|
||||
_app = app
|
||||
}
|
||||
|
||||
// Safe accessor, runtime-enforced, TS knows it's not undefined
|
||||
export function getApp(): App {
|
||||
if (!_app) {
|
||||
throw new Error('App context has not been set.')
|
||||
}
|
||||
return _app
|
||||
}
|
||||
|
||||
export function clearApp() {
|
||||
_app = undefined;
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import {App, MetadataCache, TFile} from "obsidian";
|
||||
import {TFile} from "obsidian";
|
||||
import * as yaml from 'js-yaml';
|
||||
import { Contact } from "./contact";
|
||||
import { getApp } from "src/context/sharedAppContext";
|
||||
|
||||
export async function parseContactFiles(files: TFile[], metadataCache: MetadataCache) {
|
||||
export async function parseContactFiles(files: TFile[]) {
|
||||
const { metadataCache } = getApp();
|
||||
const contactsData: Contact[] = [];
|
||||
for (const file of files) {
|
||||
const frontMatter = metadataCache.getFileCache(file)?.frontmatter
|
||||
|
|
@ -16,8 +18,8 @@ export async function parseContactFiles(files: TFile[], metadataCache: MetadataC
|
|||
return contactsData;
|
||||
}
|
||||
|
||||
export async function updateFrontMatterValue(app: App, file: TFile, key: string, value: string) {
|
||||
|
||||
export async function updateFrontMatterValue(file: TFile, key: string, value: string) {
|
||||
const app = getApp();
|
||||
const content = await app.vault.read(file);
|
||||
|
||||
const match = content.match(/^---\n([\s\S]*?)\n---\n?/);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {MetadataCache, Notice, TFile} from "obsidian";
|
||||
import {Notice, TFile} from "obsidian";
|
||||
import {parseKey} from "./vcardKey";
|
||||
import {VCardStructuredFields} from "./vcardDefinitions";
|
||||
import {getApp} from "src/context/sharedAppContext";
|
||||
|
||||
function filterNonNull<T>(array: (T | null | undefined)[]): T[] {
|
||||
return array.filter((item): item is T => item !== null && item !== undefined);
|
||||
|
|
@ -37,8 +38,9 @@ function renderSingleKey([key, value]:[string, string]):string {
|
|||
return `${keyObj.key}${type}:${value}`;
|
||||
}
|
||||
|
||||
function generateVCard(metadataCache: MetadataCache, file: TFile): string {
|
||||
function generateVCard(file: TFile): string {
|
||||
try {
|
||||
const { metadataCache } = getApp();
|
||||
const frontMatter = metadataCache.getFileCache(file)?.frontmatter;
|
||||
if (!frontMatter) return "";
|
||||
|
||||
|
|
@ -71,9 +73,9 @@ function generateVCard(metadataCache: MetadataCache, file: TFile): string {
|
|||
}
|
||||
}
|
||||
|
||||
export async function vcardToString(metadataCache: MetadataCache, contactFiles: TFile[]): Promise<string> {
|
||||
export async function vcardToString(contactFiles: TFile[]): Promise<string> {
|
||||
return contactFiles
|
||||
.map(file => generateVCard(metadataCache, file))
|
||||
.map(file => generateVCard(file))
|
||||
.filter(vcard => vcard !== "") // Remove empty results
|
||||
.join("\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from "react";
|
||||
import {useApp} from "src/context/hooks";
|
||||
import {getApp} from "src/context/sharedAppContext";
|
||||
import {fileId, openFile} from "src/file/file";
|
||||
import {Contact} from "src/parse/contact";
|
||||
import {setIcon, TFile} from "obsidian";
|
||||
|
|
@ -15,7 +15,7 @@ type ContactProps = {
|
|||
|
||||
|
||||
export const ContactView = (props: ContactProps) => {
|
||||
const {workspace} = useApp();
|
||||
const {workspace} = getApp();
|
||||
const contact = props.contact;
|
||||
const buttons = React.useRef<(HTMLElement | null)[]>([]);
|
||||
React.useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {normalizePath, Notice, TAbstractFile, TFile, TFolder} from "obsidian";
|
||||
import * as React from "react";
|
||||
import { useApp } from "src/context/hooks";
|
||||
import { getApp } from "src/context/sharedAppContext";
|
||||
import {createContactFile, createFileName, findContactFiles, openFilePicker, saveVcardFilePicker} from "src/file/file";
|
||||
import ContactsPlugin from "src/main";
|
||||
import { Contact } from "src/parse/contact";
|
||||
|
|
@ -14,13 +14,13 @@ import myScrollTo from "src/util/myScrollTo";
|
|||
import { vcardToString } from "src/parse/vcard/vcardToString";
|
||||
import { processAvatar } from "src/util/avatarActions";
|
||||
|
||||
|
||||
type RootProps = {
|
||||
plugin: ContactsPlugin;
|
||||
};
|
||||
|
||||
export const SidebarRootView = (props: RootProps) => {
|
||||
const app = useApp();
|
||||
const { vault, metadataCache } = app;
|
||||
const { vault } = getApp();
|
||||
const [contacts, setContacts] = React.useState<Contact[]>([]);
|
||||
const [sort, setSort] = React.useState<Sort>(Sort.NAME);
|
||||
const folder = props.plugin.settings.contactsFolder;
|
||||
|
|
@ -39,7 +39,7 @@ export const SidebarRootView = (props: RootProps) => {
|
|||
return;
|
||||
}
|
||||
|
||||
parseContactFiles(findContactFiles(contactsFolder), metadataCache).then((contactsData) =>{
|
||||
parseContactFiles(findContactFiles(contactsFolder)).then((contactsData) =>{
|
||||
setContacts(contactsData);
|
||||
});
|
||||
};
|
||||
|
|
@ -101,7 +101,7 @@ export const SidebarRootView = (props: RootProps) => {
|
|||
}}
|
||||
exportAllVCF={async() => {
|
||||
const allContactFiles = contacts.map((contact)=> contact.file)
|
||||
const vcards = await vcardToString(metadataCache, allContactFiles);
|
||||
const vcards = await vcardToString(allContactFiles);
|
||||
saveVcardFilePicker(vcards)
|
||||
}}
|
||||
onCreateContact={async () => {
|
||||
|
|
@ -117,7 +117,7 @@ export const SidebarRootView = (props: RootProps) => {
|
|||
processAvatar={(contact :Contact) => {
|
||||
(async () => {
|
||||
try {
|
||||
await processAvatar(app, contact);
|
||||
await processAvatar(contact);
|
||||
setTimeout(() => { parseContacts() }, 50);
|
||||
} catch (err) {
|
||||
new Notice(err.message);
|
||||
|
|
@ -126,7 +126,7 @@ export const SidebarRootView = (props: RootProps) => {
|
|||
}}
|
||||
exportVCF={(contactFile: TFile) => {
|
||||
(async () => {
|
||||
const vcards = await vcardToString(metadataCache, [contactFile])
|
||||
const vcards = await vcardToString([contactFile])
|
||||
saveVcardFilePicker(vcards, contactFile)
|
||||
})();
|
||||
}} />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ItemView, WorkspaceLeaf } from "obsidian";
|
||||
import * as React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { AppContext } from "src/context/context";
|
||||
import {setApp, clearApp} from "src/context/sharedAppContext";
|
||||
import ContactsPlugin from "src/main";
|
||||
import { CONTACTS_VIEW_CONFIG } from "src/util/constants";
|
||||
import { SidebarRootView } from "./components/SidebarRootView";
|
||||
|
|
@ -26,15 +26,15 @@ export class ContactsView extends ItemView {
|
|||
return CONTACTS_VIEW_CONFIG.icon;
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
async onOpen(){
|
||||
setApp(this.app);
|
||||
this.root.render(
|
||||
<AppContext.Provider value={this.app}>
|
||||
<SidebarRootView plugin={this.plugin} />
|
||||
</AppContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
clearApp();
|
||||
this.root.unmount();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ function isHttpUrl(str: string): boolean {
|
|||
}
|
||||
}
|
||||
|
||||
export const processAvatar = async (app: App, contact: Contact) => {
|
||||
export const processAvatar = async (contact: Contact) => {
|
||||
try {
|
||||
let rawImg :HTMLImageElement;
|
||||
if (isHttpUrl(contact.data['PHOTO'])) {
|
||||
|
|
@ -64,7 +64,7 @@ export const processAvatar = async (app: App, contact: Contact) => {
|
|||
}
|
||||
}
|
||||
|
||||
await updateFrontMatterValue(app, contact.file, 'PHOTO', base64EncodeImage(resizeAndCropImage(rawImg, 120)));
|
||||
await updateFrontMatterValue(contact.file, 'PHOTO', base64EncodeImage(resizeAndCropImage(rawImg, 120)));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw new Error(
|
||||
|
|
|
|||
Loading…
Reference in a new issue