Browse Source

fix: resolve cross-platform contactMethods JSON parsing inconsistencies

- Add platform-agnostic parseJsonField utility for contactMethods handling
- Update contact export functions (contactsToExportJson, contactToCsvLine)
- Fix contact storage in QR scan views (ContactQRScanShowView, ContactQRScanFullView)
- Ensure consistent JSON string storage across web SQLite and Capacitor SQLite
- Prevents "[object Object] is not valid JSON" errors when switching platforms
- Maintains compatibility between auto-parsing web SQLite and raw string Capacitor SQLite

Fixes: contactMethods parsing errors in export and QR scan functionality
Related: searchBoxes field had similar issue (already fixed)
pull/138/head
Matthew Raymer 4 days ago
parent
commit
5df560154f
  1. 6
      src/libs/util.ts
  2. 13
      src/views/ContactEditView.vue
  3. 4
      src/views/ContactImportView.vue
  4. 3
      src/views/ContactQRScanFullView.vue
  5. 3
      src/views/ContactQRScanShowView.vue

6
src/libs/util.ts

@ -44,7 +44,7 @@ import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { sha256 } from "ethereum-cryptography/sha256"; import { sha256 } from "ethereum-cryptography/sha256";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
import { insertDidSpecificSettings } from "../db/databaseUtil"; import { insertDidSpecificSettings, parseJsonField } from "../db/databaseUtil";
export interface GiverReceiverInputInfo { export interface GiverReceiverInputInfo {
did?: string; did?: string;
@ -865,7 +865,7 @@ export const contactToCsvLine = (contact: Contact): string => {
// Handle contactMethods array by stringifying it // Handle contactMethods array by stringifying it
const contactMethodsStr = contact.contactMethods const contactMethodsStr = contact.contactMethods
? escapeField(JSON.stringify(contact.contactMethods)) ? escapeField(JSON.stringify(parseJsonField(contact.contactMethods, [])))
: ""; : "";
const fields = [ const fields = [
@ -910,7 +910,7 @@ export const contactsToExportJson = (contacts: Contact[]): DatabaseExport => {
did: contact.did, did: contact.did,
name: contact.name || null, name: contact.name || null,
contactMethods: contact.contactMethods contactMethods: contact.contactMethods
? JSON.stringify(contact.contactMethods) ? JSON.stringify(parseJsonField(contact.contactMethods, []))
: null, : null,
nextPubKeyHashB64: contact.nextPubKeyHashB64 || null, nextPubKeyHashB64: contact.nextPubKeyHashB64 || null,
notes: contact.notes || null, notes: contact.notes || null,

13
src/views/ContactEditView.vue

@ -138,11 +138,14 @@ import { RouteLocationNormalizedLoaded, Router } from "vue-router";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
import TopMessage from "../components/TopMessage.vue"; import TopMessage from "../components/TopMessage.vue";
import { AppString, NotificationIface, USE_DEXIE_DB } from "../constants/app"; import { NotificationIface, USE_DEXIE_DB } from "../constants/app";
import * as databaseUtil from "../db/databaseUtil";
import { parseJsonField } from "../db/databaseUtil";
import { db } from "../db/index"; import { db } from "../db/index";
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
import { Contact, ContactMethod } from "../db/tables/contacts"; import { Contact, ContactMethod } from "../db/tables/contacts";
import * as databaseUtil from "../db/databaseUtil"; import { AppString } from "../constants/app";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { logger } from "../utils/logger";
/** /**
* Contact Edit View Component * Contact Edit View Component
@ -230,9 +233,7 @@ export default class ContactEditView extends Vue {
let contact: Contact | undefined = databaseUtil.mapQueryResultToValues( let contact: Contact | undefined = databaseUtil.mapQueryResultToValues(
dbContact, dbContact,
)[0] as unknown as Contact; )[0] as unknown as Contact;
contact.contactMethods = JSON.parse( contact.contactMethods = parseJsonField(contact?.contactMethods, []);
(contact?.contactMethods as unknown as string) || "[]",
);
if (USE_DEXIE_DB) { if (USE_DEXIE_DB) {
await db.open(); await db.open();
contact = await db.contacts.get(contactDid || ""); contact = await db.contacts.get(contactDid || "");

4
src/views/ContactImportView.vue

@ -213,6 +213,7 @@ import {
} from "../db/index"; } from "../db/index";
import { Contact, ContactMethod } from "../db/tables/contacts"; import { Contact, ContactMethod } from "../db/tables/contacts";
import * as databaseUtil from "../db/databaseUtil"; import * as databaseUtil from "../db/databaseUtil";
import { parseJsonField } from "../db/databaseUtil";
import * as libsUtil from "../libs/util"; import * as libsUtil from "../libs/util";
import { import {
capitalizeAndInsertSpacesBeforeCaps, capitalizeAndInsertSpacesBeforeCaps,
@ -222,6 +223,7 @@ import {
import { getContactJwtFromJwtUrl } from "../libs/crypto"; import { getContactJwtFromJwtUrl } from "../libs/crypto";
import { decodeEndorserJwt } from "../libs/crypto/vc"; import { decodeEndorserJwt } from "../libs/crypto/vc";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { logger } from "../utils/logger";
/** /**
* Interface for contact data as stored in the database * Interface for contact data as stored in the database
@ -289,7 +291,7 @@ function dbRecordToContact(record: ContactDbRecord): Contact {
profileImageUrl: safeString(record.profileImageUrl), profileImageUrl: safeString(record.profileImageUrl),
publicKeyBase64: safeString(record.publicKeyBase64), publicKeyBase64: safeString(record.publicKeyBase64),
nextPubKeyHashB64: safeString(record.nextPubKeyHashB64), nextPubKeyHashB64: safeString(record.nextPubKeyHashB64),
contactMethods: JSON.parse(record.contactMethods || "[]"), contactMethods: parseJsonField(record.contactMethods, []),
}; };
} }

3
src/views/ContactQRScanFullView.vue

@ -124,6 +124,7 @@ import UserNameDialog from "../components/UserNameDialog.vue";
import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer"; import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer";
import { retrieveAccountMetadata } from "../libs/util"; import { retrieveAccountMetadata } from "../libs/util";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { parseJsonField } from "../db/databaseUtil";
interface QRScanResult { interface QRScanResult {
rawValue?: string; rawValue?: string;
@ -474,7 +475,7 @@ export default class ContactQRScan extends Vue {
// Add new contact // Add new contact
// @ts-expect-error because we're just using the value to store to the DB // @ts-expect-error because we're just using the value to store to the DB
contact.contactMethods = JSON.stringify(contact.contactMethods); contact.contactMethods = JSON.stringify(parseJsonField(contact.contactMethods, []));
const { sql, params } = databaseUtil.generateInsertStatement( const { sql, params } = databaseUtil.generateInsertStatement(
contact as unknown as Record<string, unknown>, contact as unknown as Record<string, unknown>,
"contacts", "contacts",

3
src/views/ContactQRScanShowView.vue

@ -171,6 +171,7 @@ import { db, retrieveSettingsForActiveAccount } from "../db/index";
import { Contact } from "../db/tables/contacts"; import { Contact } from "../db/tables/contacts";
import { MASTER_SETTINGS_KEY } from "../db/tables/settings"; import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
import * as databaseUtil from "../db/databaseUtil"; import * as databaseUtil from "../db/databaseUtil";
import { parseJsonField } from "../db/databaseUtil";
import { getContactJwtFromJwtUrl } from "../libs/crypto"; import { getContactJwtFromJwtUrl } from "../libs/crypto";
import { import {
generateEndorserJwtUrlForAccount, generateEndorserJwtUrlForAccount,
@ -778,7 +779,7 @@ export default class ContactQRScanShow extends Vue {
// Add new contact // Add new contact
// @ts-expect-error because we're just using the value to store to the DB // @ts-expect-error because we're just using the value to store to the DB
contact.contactMethods = JSON.stringify(contact.contactMethods); contact.contactMethods = JSON.stringify(parseJsonField(contact.contactMethods, []));
const { sql, params } = databaseUtil.generateInsertStatement( const { sql, params } = databaseUtil.generateInsertStatement(
contact as unknown as Record<string, unknown>, contact as unknown as Record<string, unknown>,
"contacts", "contacts",

Loading…
Cancel
Save