mirror of
https://github.com/aleksejs1/obsidian-contact-sync-plugin.git
synced 2026-07-22 05:48:16 +00:00
Add type for address
This commit is contained in:
parent
434bbbc9c8
commit
c58c5eb1fb
5 changed files with 182 additions and 59 deletions
|
|
@ -66,6 +66,54 @@ describe('AddressAdapter', () => {
|
|||
};
|
||||
expect(adapter.extract(contact)).toEqual([]);
|
||||
});
|
||||
|
||||
it('supports useContactTypes correctly', () => {
|
||||
const contact: GoogleContact = {
|
||||
resourceName: 'p4',
|
||||
addresses: [
|
||||
createAddress({ type: 'Home', formattedValue: 'Home 1' }),
|
||||
createAddress({ type: 'Home', formattedValue: 'Home 2' }),
|
||||
createAddress({ type: 'Work', formattedValue: 'Work 1' }),
|
||||
createAddress({ formattedValue: 'Other 1' }),
|
||||
],
|
||||
};
|
||||
const results = adapter.extract(contact, { useContactTypes: true });
|
||||
expect(results).toEqual([
|
||||
{ value: 'Home 1', type: 'home', index: 0 },
|
||||
{ value: 'Home 2', type: 'home', index: 1 },
|
||||
{ value: 'Work 1', type: 'work', index: 0 },
|
||||
{ value: 'Other 1', type: 'other', index: 0 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Array Strategy', () => {
|
||||
const contextWithArray = { namingStrategy: NamingStrategy.Array };
|
||||
|
||||
it('combines addresses into a single array extraction', () => {
|
||||
const results = adapter.extract(mockContact, contextWithArray);
|
||||
expect(results).toEqual([
|
||||
{
|
||||
value: [
|
||||
'123 Main St\nSpringfield, USA 12345',
|
||||
'456 Office Rd\nWorktown, UK AB1 2CD',
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns single string value if only one address', () => {
|
||||
const singleContact: GoogleContact = {
|
||||
resourceName: 'p2',
|
||||
addresses: [mockContact.addresses![0]!],
|
||||
};
|
||||
const results = adapter.extract(singleContact, contextWithArray);
|
||||
expect(results).toEqual([
|
||||
{
|
||||
value: '123 Main St\nSpringfield, USA 12345',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VCF Strategy', () => {
|
||||
|
|
@ -130,5 +178,30 @@ describe('AddressAdapter', () => {
|
|||
[]
|
||||
);
|
||||
});
|
||||
|
||||
it('supports useContactTypes for VCF strategy', () => {
|
||||
const typeContact: GoogleContact = {
|
||||
resourceName: 'p6',
|
||||
addresses: [
|
||||
createAddress({
|
||||
city: 'City 1',
|
||||
type: 'Home',
|
||||
}),
|
||||
createAddress({
|
||||
city: 'City 2',
|
||||
type: 'Home',
|
||||
}),
|
||||
],
|
||||
};
|
||||
const results = adapter.extract(typeContact, {
|
||||
namingStrategy: NamingStrategy.VCF,
|
||||
useContactTypes: true,
|
||||
});
|
||||
|
||||
expect(results).toEqual([
|
||||
{ value: 'City 1', suffix: 'CITY', index: 0, type: 'home' },
|
||||
{ value: 'City 2', suffix: 'CITY', index: 1, type: 'home' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,58 +14,108 @@ export class AddressAdapter implements FieldAdapter {
|
|||
|
||||
const isVcfStrategy = context?.namingStrategy === NamingStrategy.VCF;
|
||||
|
||||
const typeCounts: Record<string, number> = {};
|
||||
const processType = (typeStr?: string) => {
|
||||
let normalizedType = (typeStr ?? 'other').toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
if (!normalizedType) {
|
||||
normalizedType = 'other';
|
||||
}
|
||||
const currentCount = typeCounts[normalizedType] ?? 0;
|
||||
typeCounts[normalizedType] = currentCount + 1;
|
||||
return { type: normalizedType, index: currentCount };
|
||||
};
|
||||
|
||||
if (isVcfStrategy) {
|
||||
// For VCF strategy, return subfields
|
||||
const results: ExtractionResult[] = [];
|
||||
addresses.forEach((addr, addrIndex) => {
|
||||
// Add each subfield as a separate result
|
||||
// All subfields of the same address get the same index
|
||||
if (addr.streetAddress) {
|
||||
results.push({
|
||||
value: addr.streetAddress,
|
||||
suffix: 'STREET',
|
||||
index: addrIndex,
|
||||
});
|
||||
}
|
||||
if (addr.city) {
|
||||
results.push({ value: addr.city, suffix: 'CITY', index: addrIndex });
|
||||
}
|
||||
if (addr.country) {
|
||||
results.push({
|
||||
value: addr.country,
|
||||
suffix: 'COUNTRY',
|
||||
index: addrIndex,
|
||||
});
|
||||
}
|
||||
if (addr.postalCode) {
|
||||
results.push({
|
||||
value: addr.postalCode,
|
||||
suffix: 'POSTALCODE',
|
||||
index: addrIndex,
|
||||
});
|
||||
}
|
||||
if (addr.extendedAddress) {
|
||||
results.push({
|
||||
value: addr.extendedAddress,
|
||||
suffix: 'EXTENDED',
|
||||
index: addrIndex,
|
||||
});
|
||||
}
|
||||
if (addr.formattedType) {
|
||||
results.push({
|
||||
value: addr.formattedType,
|
||||
suffix: 'TYPE',
|
||||
index: addrIndex,
|
||||
});
|
||||
const typeInfo = context.useContactTypes
|
||||
? processType(addr.type)
|
||||
: undefined;
|
||||
const derivedIndex = typeInfo ? typeInfo.index : addrIndex;
|
||||
const derivedType = typeInfo ? typeInfo.type : undefined;
|
||||
|
||||
const addResult = (subfield: string, suffix: string) => {
|
||||
const result: ExtractionResult = {
|
||||
value: subfield,
|
||||
suffix,
|
||||
index: derivedIndex,
|
||||
};
|
||||
if (derivedType !== undefined) {
|
||||
result.type = derivedType;
|
||||
}
|
||||
results.push(result);
|
||||
};
|
||||
|
||||
const fields = [
|
||||
{ value: addr.streetAddress, suffix: 'STREET' },
|
||||
{ value: addr.city, suffix: 'CITY' },
|
||||
{ value: addr.country, suffix: 'COUNTRY' },
|
||||
{ value: addr.postalCode, suffix: 'POSTALCODE' },
|
||||
{ value: addr.extendedAddress, suffix: 'EXTENDED' },
|
||||
{ value: addr.formattedType, suffix: 'TYPE' },
|
||||
];
|
||||
|
||||
for (const field of fields) {
|
||||
if (field.value) {
|
||||
addResult(field.value, field.suffix);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
} else {
|
||||
// For Default strategy, return formattedValue as before
|
||||
return addresses
|
||||
.filter((item) => item.formattedValue)
|
||||
.map((item) => ({ value: item.formattedValue }));
|
||||
}
|
||||
|
||||
const validAddresses = addresses.filter((item) => item.formattedValue);
|
||||
if (validAddresses.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (context?.namingStrategy === NamingStrategy.Array) {
|
||||
return this.extractForArrayStrategy(validAddresses);
|
||||
}
|
||||
|
||||
if (context?.useContactTypes === true) {
|
||||
return this.extractWithSemanticTypes(validAddresses);
|
||||
}
|
||||
|
||||
return validAddresses.map((item) => ({ value: item.formattedValue }));
|
||||
}
|
||||
|
||||
private extractForArrayStrategy(
|
||||
validAddresses: { formattedValue: string; type?: string }[]
|
||||
): ExtractionResult[] {
|
||||
const first = validAddresses[0]?.formattedValue;
|
||||
const allValues = validAddresses.map((item) => item.formattedValue);
|
||||
return [
|
||||
{
|
||||
value: allValues.length === 1 && first ? first : allValues,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
private extractWithSemanticTypes(
|
||||
validAddresses: { formattedValue: string; type?: string }[]
|
||||
): ExtractionResult[] {
|
||||
const typeCounts: Record<string, number> = {};
|
||||
const results: ExtractionResult[] = [];
|
||||
|
||||
for (const item of validAddresses) {
|
||||
let normalizedType = (item.type ?? 'other').toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
if (!normalizedType) {
|
||||
normalizedType = 'other';
|
||||
}
|
||||
|
||||
const currentCount = typeCounts[normalizedType] ?? 0;
|
||||
typeCounts[normalizedType] = currentCount + 1;
|
||||
|
||||
results.push({
|
||||
value: item.formattedValue,
|
||||
type: normalizedType,
|
||||
index: currentCount,
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,10 +96,10 @@ export const ru = {
|
|||
'Skip nameless contacts': 'Пропускать контакты без имени',
|
||||
'If enabled, contacts without a name (where the file would be named by ID) will be skipped during sync':
|
||||
'Если включено, контакты без имени (у которых файл называется по ID) будут пропускаться при синхронизации',
|
||||
'Use contact types for emails and phones':
|
||||
'Использовать типы для e-mail и телефонов',
|
||||
'If enabled, emails and phones will be formatted with their specific types (e.g. email_work, phone_mobile) instead of generic indexes. This is ignored in the Array naming strategy.':
|
||||
'Если включено, электронные адреса и телефоны будут отформатированы с учетом их типов (например, email_work, phone_mobile) вместо индексов. Игнорируется в стратегии "Массив".',
|
||||
'Use contact types for emails, phones and addresses':
|
||||
'Использовать типы для e-mail, телефонов и адресов',
|
||||
'If enabled, emails, phones and addresses will be formatted with their specific types (e.g. email_work, phone_mobile, address_home) instead of generic indexes. This is ignored in the Array naming strategy.':
|
||||
'Если включено, электронные адреса, телефоны и адреса будут отформатированы с учетом их типов (например, email_work, phone_mobile, address_home) вместо индексов. Игнорируется в стратегии "Массив".',
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -200,10 +200,10 @@ export const lv = {
|
|||
'Skip nameless contacts': 'Izlaist kontaktus bez vārda',
|
||||
'If enabled, contacts without a name (where the file would be named by ID) will be skipped during sync':
|
||||
'Ja iespējots, kontakti bez vārda (kuru fails tiktu nosaukts pēc ID) tiks izlaisti sinhronizācijas laikā',
|
||||
'Use contact types for emails and phones':
|
||||
'Izmantot e-pasta un tālruņa tipus',
|
||||
'If enabled, emails and phones will be formatted with their specific types (e.g. email_work, phone_mobile) instead of generic indexes. This is ignored in the Array naming strategy.':
|
||||
'Ja iespējots, e-pasti un tālruņi tiks formatēti ar to specifiskajiem tipiem (piem., email_work, phone_mobile), nevis vispārīgiem rādītājiem. Tas tiek ignorēts Array nosaukšanas stratēģijā.',
|
||||
'Use contact types for emails, phones and addresses':
|
||||
'Izmantot e-pasta, tālruņa un adrešu tipus',
|
||||
'If enabled, emails, phones and addresses will be formatted with their specific types (e.g. email_work, phone_mobile, address_home) instead of generic indexes. This is ignored in the Array naming strategy.':
|
||||
'Ja iespējots, e-pasti, tālruņi un adreses tiks formatēti ar to specifiskajiem tipiem (piem., email_work, phone_mobile, address_home), nevis vispārīgiem rādītājiem. Tas tiek ignorēts Array nosaukšanas stratēģijā.',
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -302,8 +302,8 @@ export const en = {
|
|||
'Skip nameless contacts': 'Skip nameless contacts',
|
||||
'If enabled, contacts without a name (where the file would be named by ID) will be skipped during sync':
|
||||
'If enabled, contacts without a name (where the file would be named by ID) will be skipped during sync',
|
||||
'Use contact types for emails and phones':
|
||||
'Use contact types for emails and phones',
|
||||
'If enabled, emails and phones will be formatted with their specific types (e.g. email_work, phone_mobile) instead of generic indexes. This is ignored in the Array naming strategy.':
|
||||
'If enabled, emails and phones will be formatted with their specific types (e.g. email_work, phone_mobile) instead of generic indexes. This is ignored in the Array naming strategy.',
|
||||
'Use contact types for emails, phones and addresses':
|
||||
'Use contact types for emails, phones and addresses',
|
||||
'If enabled, emails, phones and addresses will be formatted with their specific types (e.g. email_work, phone_mobile, address_home) instead of generic indexes. This is ignored in the Array naming strategy.':
|
||||
'If enabled, emails, phones and addresses will be formatted with their specific types (e.g. email_work, phone_mobile, address_home) instead of generic indexes. This is ignored in the Array naming strategy.',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -119,10 +119,10 @@ export class ContactSyncSettingTab extends PluginSettingTab {
|
|||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t('Use contact types for emails and phones'))
|
||||
.setName(t('Use contact types for emails, phones and addresses'))
|
||||
.setDesc(
|
||||
t(
|
||||
'If enabled, emails and phones will be formatted with their specific types (e.g. email_work, phone_mobile) instead of generic indexes. This is ignored in the Array naming strategy.'
|
||||
'If enabled, emails, phones and addresses will be formatted with their specific types (e.g. email_work, phone_mobile, address_home) instead of generic indexes. This is ignored in the Array naming strategy.'
|
||||
)
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export interface ContactSyncSettings {
|
|||
/** Whether to skip contacts that have no name and would fall back to using their ID */
|
||||
skipNamelessContacts: boolean;
|
||||
|
||||
/** Whether to use contact types for emails and phones instead of index */
|
||||
/** Whether to use contact types for emails, phones and addresses instead of index */
|
||||
useContactTypes: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue