fixes for all review items to get a comunity registration listing

This commit is contained in:
Roland Broekema 2025-04-17 14:59:38 +02:00
parent 350b39373a
commit 7879cce3d0
13 changed files with 53 additions and 45 deletions

View file

@ -1,6 +1,7 @@
MIT License
Copyright (c) 2022 Vadim Beskrovnov
Copyright (c) 2025 Roland Broekema
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -19,3 +20,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Originally created by Vadim Beskrovnov (c) 2022.
Forked and completely rewritten by Roland Broekema (c) 2025.

View file

@ -210,3 +210,7 @@ For many of us, using Obsidian is like brewing a perfect cup of coffee—it kick
✔ **Add a dash of joy to your everyday workflow—because organization brings productivity and happy routines!**
Wishing you an inspiring journey with your enhanced contact experience. ** Start using the plugin today and share your thoughts in the [community comments](https://github.com/broekema41/obsidian-vcf-contacts/discussions)!**
🙏 Acknowledgements
This plugin was originally inspired by and started as a fork of Vadim Beskrovnovs Contacts plugin. You probably wouldnt recognize it from the current codebase anymore—but still, huge thanks to Vadim for laying a solid foundation to explore, experiment with, and be inspired by.

View file

@ -1,10 +1,11 @@
{
"id": "vcf-contacts",
"name": "VCF Contacts",
"version": "2.0.2",
"minAppVersion": "0.15.0",
"description": "Effortlessly manage, organize, and interact with your contacts inside Obsidian. Import, export, and structure vCard (VCF) files seamlessly while keeping all contact details accessible in your knowledge base. Includes click-to-call, right-click copy, structured metadata, and more!",
"version": "2.0.3",
"minAppVersion": "1.1.0",
"description": "Effortlessly manage, organize, and interact with your contacts. Import, export, and structure vCard (VCF) files seamlessly while keeping all contact details accessible in your knowledge base. Includes click-to-call, right-click copy, structured metadata, and more!",
"author": "Roland Broekema",
"authorUrl": "https://github.com/broekema41/",
"isDesktopOnly": true
"isDesktopOnly": true,
"forkedFrom": "https://github.com/vbeskrovnov/obsidian-contacts/"
}

View file

@ -1,6 +1,6 @@
{
"name": "vcf-contacts",
"version": "2.0.2",
"version": "2.0.3",
"description": "Effortlessly manage, organize, and interact with your contacts inside Obsidian. Import, export, and structure vCard (VCF) files seamlessly while keeping all contact details accessible in your knowledge base. Includes click-to-call, right-click copy, structured metadata, and more!",
"main": "main.js",
"scripts": {
@ -16,7 +16,6 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/node": "^16.11.6",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
@ -31,7 +30,6 @@
"typescript": "4.7.4"
},
"dependencies": {
"js-yaml": "^4.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}

View file

@ -1,5 +1,4 @@
import * as yaml from 'js-yaml';
import { TFile } from "obsidian";
import { parseYaml, stringifyYaml, TFile } from "obsidian";
import { getApp } from "src/context/sharedAppContext";
export type Contact = {
@ -32,13 +31,13 @@ export async function updateFrontMatterValue(file: TFile, key: string, value: st
let body = content;
if (match) {
yamlObj = yaml.load(match[1]) || {};
yamlObj = parseYaml(match[1]) || {};
body = content.slice(match[0].length);
}
yamlObj[key] = value;
const newFrontMatter = '---\n' + yaml.dump(yamlObj, { lineWidth: -1 }) + '---\n';
const newFrontMatter = '---\n' + stringifyYaml(yamlObj) + '---\n';
const newContent = newFrontMatter + body;
await app.vault.modify(file, newContent);

View file

@ -1,4 +1,4 @@
import * as yaml from "js-yaml";
import { stringifyYaml } from "obsidian";
export function mdRender(record: Record<string, any>, hashtags: string): string {
const { NOTE, ...recordWithoutNote } = record;
@ -9,7 +9,7 @@ export function mdRender(record: Record<string, any>, hashtags: string): string
const tempTags= recordWithoutNote.CATEGORIES.split(',')
additionalTags = `#${tempTags.join(' #')}`
}
const yamlString = yaml.dump(recordWithoutNote, { lineWidth: -1 });
const yamlString = stringifyYaml(recordWithoutNote);
return `---
${yamlString}

View file

@ -3,24 +3,26 @@ import { ContactNameModal } from "src/ui/modals/contactNameModal";
import { convertToLatestVCFPhotoFormat } from "src/util/avatarActions";
function unfoldVCardLines(vCardData: string): string[] {
const lines = vCardData.split(/\r\n?/g);
const unfoldedLines: string[] = [];
let currentLine = "";
// Normalize line endings to \n first (handles \r, \r\n)
const normalized = vCardData.replace(/\r\n?/g, '\n');
for (const line of lines) {
if (/^\s/.test(line)) {
// Continuation of the previous line (line folding)
currentLine += line.trimStart();
} else {
if (currentLine) unfoldedLines.push(currentLine);
currentLine = line;
}
}
if (currentLine) unfoldedLines.push(currentLine);
const lines = normalized.split('\n');
const unfoldedLines: string[] = [];
let currentLine = "";
return unfoldedLines;
for (const line of lines) {
if (/^[ \t]/.test(line)) {
// Line is a continuation (folded)
currentLine += line.slice(1); // remove the space or tab
} else {
if (currentLine) unfoldedLines.push(currentLine);
currentLine = line;
}
}
if (currentLine) unfoldedLines.push(currentLine);
return unfoldedLines;
}
/**
* Extracts the base key from a vCard field name.
* - If the key contains `[`, extract everything before it.
@ -241,6 +243,7 @@ export async function createEmptyVcard() {
export async function parseVcard(vCardData: string) {
const unfoldedLines = unfoldVCardLines(vCardData);
const vCardObject: Record<string, any> = {};
for (const line of unfoldedLines) {
const parsedLine = parseVCardLine(line);
const indexedParsedLine = indexIfKeysExist(vCardObject, parsedLine)

View file

@ -30,8 +30,6 @@ export class ContactsSettingTab extends PluginSettingTab {
containerEl.empty();
containerEl.createEl('h2', { text: 'Settings for "Contacts" plugin.' });
new Setting(containerEl)
.setName('Contacts folder location')
.setDesc('Files in this folder and all subfolders will be available as contacts')

View file

@ -175,7 +175,7 @@ export const ContactView = (props: ContactProps) => {
className={
"clickable-icon nav-action-button "
}
aria-label="Process Avatar"
aria-label="Process avatar"
ref={(element) => (buttons.current[0] = element)}
onClick={() => props.processAvatar(contact)}
>
@ -185,7 +185,7 @@ export const ContactView = (props: ContactProps) => {
className={
"clickable-icon nav-action-button "
}
aria-label="export VCF"
aria-label="Export vcf"
ref={(element) => (buttons.current[1] = element)}
onClick={() => props.exportVCF(contact.file)}
>

View file

@ -19,7 +19,7 @@ export const CopyableItem: React.FC<CopyableItemProps> = ({ value, children }) =
event.preventDefault();
const menu = new Menu();
menu.addItem((item) =>
item.setTitle("Copy to Clipboard").setIcon("clipboard").onClick(handleCopy)
item.setTitle("Copy to clipboard").setIcon("clipboard").onClick(handleCopy)
);
menu.showAtPosition({ x: event.pageX, y: event.pageY });
};

View file

@ -26,7 +26,7 @@ export const HeaderView = (props: HeaderProps) => {
id="create-btn"
className="clickable-icon nav-action-button"
data-icon="contact"
aria-label="Create New Contact"
aria-label="Create new contact"
ref={(element) => (buttons.current[1] = element)}
onClick={props.onCreateContact}
/>
@ -37,7 +37,7 @@ export const HeaderView = (props: HeaderProps) => {
className={
"clickable-icon nav-action-button "
}
aria-label="import VCF"
aria-label="Import vcf"
ref={(element) => (buttons.current[2] = element)}
onClick={ props.importVCF }
/>
@ -47,7 +47,7 @@ export const HeaderView = (props: HeaderProps) => {
className={
"clickable-icon nav-action-button "
}
aria-label="export VCF"
aria-label="Export vcf"
ref={(element) => (buttons.current[3] = element)}
onClick={ props.exportAllVCF }
/>
@ -60,7 +60,7 @@ export const HeaderView = (props: HeaderProps) => {
"clickable-icon nav-action-button " +
(props.sort === Sort.NAME && "is-active")
}
aria-label="Sort By Name"
aria-label="Sort by name"
ref={(element) => (buttons.current[4] = element)}
onClick={() => props.onSortChange(Sort.NAME)}
/>
@ -71,7 +71,7 @@ export const HeaderView = (props: HeaderProps) => {
"clickable-icon nav-action-button " +
(props.sort === Sort.BIRTHDAY && "is-active")
}
aria-label="Sort By Birthday"
aria-label="Sort by birthday"
ref={(element) => (buttons.current[6] = element)}
onClick={() => props.onSortChange(Sort.BIRTHDAY)}
/>
@ -82,7 +82,7 @@ export const HeaderView = (props: HeaderProps) => {
"clickable-icon nav-action-button " +
(props.sort === Sort.ORG && "is-active")
}
aria-label="Sort By Organization"
aria-label="Sort by organization"
ref={(element) => (buttons.current[7] = element)}
onClick={() => props.onSortChange(Sort.ORG)}
/>

View file

@ -1,11 +1,11 @@
import { MarkdownView, WorkspaceLeaf } from "obsidian";
import { fileId } from "src/file/file";
let debounceTimer: NodeJS.Timeout;
let debounceTimer: number
const scrollToLeaf = (leaf: WorkspaceLeaf):void => {
clearTimeout(debounceTimer); // Reset debounce timer
debounceTimer = setTimeout(() => {
debounceTimer = window.setTimeout(() => {
if (!(leaf?.view instanceof MarkdownView)) return;
const contactElement = document.getElementById(fileId(leaf.view.file));
@ -44,7 +44,7 @@ const scrollToTop = ():void => {
const clearDebounceTimer = ():void => {
clearTimeout(debounceTimer);
window.clearTimeout(debounceTimer);
}
export default {

View file

@ -1,5 +1,6 @@
{
"2.0.0": "0.15.0",
"2.0.1": "0.15.0",
"2.0.2": "0.15.0"
"2.0.0": "1.1.0",
"2.0.1": "1.1.0",
"2.0.2": "1.1.0",
"2.0.3": "1.1.0"
}