Compare commits

...

22 Commits

Author SHA1 Message Date
Jose Olarte III
bb2a4ab76e URL scheme config for iOS
- Registers the timesafari:// URL scheme
- Sets the bundle URL name to app.timesafari
2025-06-16 16:09:08 +08:00
Matthew Raymer
048dded278 fix: resolve deep link route mismatch for project links
- Fix schema validation mismatch between "project-details" and "project"
- Update VALID_DEEP_LINK_ROUTES to include "project" instead of "project-details"
- Update deepLinkSchemas to use "project" route name
- Update documentation to reflect correct route name
- Resolves "Invalid route path: project" errors in deep link handling

The deep link timesafari://project/01JWH0YAB3MAGBD751VAJAXQ17 now works correctly
and routes to the ProjectViewView component as expected.

Fixes: Deep link validation errors for project routes
2025-06-16 05:48:13 +00:00
e240c2940a remove unused deep links and add another 2025-06-15 13:54:12 -06:00
54dca9e745 fix project deep-link (and reorder alphabetically) 2025-06-15 11:02:16 -06:00
9f0fed0a60 update ios check to work, and add links to app stores 2025-06-14 22:10:49 -06:00
0d152adbf2 remove the deep-link autoVerify because it caused a build failure 2025-06-14 22:06:12 -06:00
cead308800 incorporate one of the BUILDING steps directly into the file 2025-06-13 22:37:03 -06:00
676a301331 bump to build 30 version 0.5.4 2025-06-13 22:36:28 -06:00
d6db81cc36 fix some result types and refactor types themselves 2025-06-13 21:58:57 -06:00
Matthew Raymer
f2ddcd2541 feat: add conditional rendering for claim certificate link and update gitignore
- Add v-if directive to show claim certificate link only when veriClaim.id exists
- Update .gitignore to exclude android app resource directory
- Prevents broken links when claim data is not fully loaded
- Improves build process by ignoring generated Android resources

This change ensures the certificate link is only displayed when there's
valid claim data available, preventing navigation errors and improving
user experience. The gitignore update helps keep the repository clean
by excluding Android-specific generated files.
2025-06-14 03:31:12 +00:00
fb81f7b96e fix problems with :href links causing the app to reload for DB errors on mobile 2025-06-13 20:39:12 -06:00
a23416ead1 fix optional message at top to not overflow 2025-06-12 20:10:31 -06:00
530c7c1a13 fix problem with user-profile page, and bump to build 29 & version 0.5.3 2025-06-12 19:16:02 -06:00
f255ea389b bump to build 26 and version 0.5.1 2025-06-11 00:46:46 -06:00
0d343b9877 Merge pull request 'fix creation of did-specific settings (with a rename)' (#138) from fix-did-specifics into master
Reviewed-on: #138
2025-06-11 02:14:41 -04:00
df06100c32 remove more debugging 2025-06-10 23:49:14 -06:00
Matthew Raymer
ac5ddfc6f2 style: fix line length in ContactsView ternary operator
- Break long CSS class strings into multiple concatenated lines
- Ensure all lines are under 100 characters for better readability
- Maintain same functionality and styling behavior
- Improve code maintainability and readability

Fixes: Long lines in conditional CSS class assignment
2025-06-11 05:45:58 +00:00
Matthew Raymer
89b3f30466 fix: debug and clean up GiftedPrompts contact retrieval logic
- Add comprehensive debug logging to identify contact list population issues
- Fix array indexing bug in contact mapping (someContactDbIndex -> 0)
- Clean up all console.log statements for production readiness
- Improve contact retrieval debugging for SQLite and Dexie databases
- Maintain core functionality while adding diagnostic capabilities

Debugging: Contact list population issues in GiftedPrompts component
Cleanup: Remove debug console.log statements
2025-06-11 05:40:05 +00:00
Matthew Raymer
3cb5cc096b refactor: use databaseUtil.updateDefaultSettings for feed filter settings
- Replace direct platform service calls with databaseUtil.updateDefaultSettings
- Remove manual SQL query construction in favor of centralized utility
- Improve code consistency and maintainability
- Add proper error handling through databaseUtil's built-in mechanisms
- Remove unused PlatformServiceFactory import
- Fix SQL syntax errors in clearAll and setAll methods (AND -> comma)
- Ensure both SQLite and Dexie databases are updated consistently

Improves: FeedFilters component architecture and error handling
Fixes: isNearby and filterFeedByVisible settings not being saved properly
2025-06-11 05:19:15 +00:00
Matthew Raymer
5df560154f 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)
2025-06-11 04:17:38 +00:00
Matthew Raymer
c1aa522e6c fix: resolve cross-platform SQLite JSON parsing inconsistencies
- Add platform-agnostic parseJsonField utility to handle different SQLite implementations
- Web SQLite (wa-sqlite/absurd-sql) auto-parses JSON strings to objects
- Capacitor SQLite returns raw strings requiring manual parsing
- Update searchBoxes parsing to use new utility for consistent behavior
- Fixes "[object Object] is not valid JSON" error when switching platforms
- Ensures compatibility between web and mobile SQLite implementations

Fixes: searchBoxes parsing errors in databaseUtil.ts
Related: contactMethods field has similar issue (needs same treatment)
2025-06-11 03:44:28 +00:00
a082469a01 fix creation of did-specific settings (with a rename) 2025-06-10 20:51:22 -06:00
41 changed files with 1093 additions and 717 deletions

1
.gitignore vendored
View File

@@ -55,3 +55,4 @@ build_logs/
icons
android/app/src/main/res/

View File

@@ -321,11 +321,11 @@ Prerequisites: macOS with Xcode installed
#### Each Release
0. First time (or if XCode dependencies change):
0. First time (or if dependencies change):
- `pkgx +rubygems.org sh`
- ... and you may have to fix these, especially with pkgx
- ... and you may have to fix these, especially with pkgx:
```bash
gem_path=$(which gem)
@@ -334,12 +334,9 @@ Prerequisites: macOS with Xcode installed
export GEM_PATH=$shortened_path
```
```bash
cd ios/App
pod install
```
1. Check the iOS flag isIOS in CapacitorPlatformService (currently hard-coded for iOS build).
1. Build the web assets:
2. Build the web assets:
```bash
rm -rf dist
@@ -347,8 +344,7 @@ Prerequisites: macOS with Xcode installed
npm run build:capacitor
```
2. Update iOS project with latest build:
3. Update iOS project with latest build:
```bash
npx cap sync ios
@@ -356,7 +352,7 @@ Prerequisites: macOS with Xcode installed
- If that fails with "Could not find..." then look at the "gem_path" instructions above.
3. Copy the assets:
4. Copy the assets:
```bash
# It makes no sense why capacitor-assets will not run without these but it actually changes the contents.
@@ -367,15 +363,14 @@ Prerequisites: macOS with Xcode installed
npx capacitor-assets generate --ios
```
4. Bump the version to match Android:
4. Bump the version to match Android & package.json:
```
cd ios/App
xcrun agvtool new-version 25
xcrun agvtool new-version 30
# Unfortunately this edits Info.plist directly.
#xcrun agvtool new-marketing-version 0.4.5
cat App.xcodeproj/project.pbxproj | sed "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 0.5.0;/g" > temp
mv temp App.xcodeproj/project.pbxproj
cat App.xcodeproj/project.pbxproj | sed "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 0.5.4;/g" > temp && mv temp App.xcodeproj/project.pbxproj
cd -
```
@@ -403,6 +398,8 @@ Prerequisites: macOS with Xcode installed
* You'll probably have to "Manage" something about encryption, disallowed in France.
* Then "Save" and "Add to Review" and "Resubmit to App Review".
8. Revert the iOS flag isIOS in CapacitorPlatformService.
### Android Build
Prerequisites: Android Studio with Java SDK installed
@@ -427,7 +424,7 @@ Prerequisites: Android Studio with Java SDK installed
npx capacitor-assets generate --android
```
4. Bump version to match iOS: android/app/build.gradle
4. Bump version to match iOS & package.json: android/app/build.gradle
5. Open the project in Android Studio:
@@ -478,7 +475,7 @@ At play.google.com/console:
- Note that if you add testers, you have to go to "Publishing Overview" and send those changes or your (closed) testers won't see it.
## First-time Android Configuration for deep links
## Android Configuration for deep links
You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file:
@@ -490,3 +487,5 @@ You must add the following intent filter to the `android/app/src/main/AndroidMan
<data android:scheme="timesafari" />
</intent-filter>
```
... though when we tried that most recently it failed to 'build' the APK with: http(s) scheme and host attribute are missing, but are required for Android App Links [AppLinkUrlError]

View File

@@ -31,8 +31,8 @@ android {
applicationId "app.timesafari.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 25
versionName "0.5.0"
versionCode 30
versionName "0.5.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

View File

@@ -403,7 +403,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 25;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_TEAM = GM3FS5JQPH;
ENABLE_APP_SANDBOX = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
@@ -413,7 +413,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.5.0;
MARKETING_VERSION = 0.5.4;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -430,7 +430,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 25;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_TEAM = GM3FS5JQPH;
ENABLE_APP_SANDBOX = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
@@ -440,7 +440,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.5.0;
MARKETING_VERSION = 0.5.4;
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";

View File

@@ -49,5 +49,16 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>app.timesafari</string>
<key>CFBundleURLSchemes</key>
<array>
<string>timesafari</string>
</array>
</dict>
</array>
</dict>
</plist>

1217
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "timesafari",
"version": "0.4.8",
"version": "0.5.4",
"description": "Time Safari Application",
"author": {
"name": "Time Safari Team"

View File

@@ -104,7 +104,6 @@ import { USE_DEXIE_DB } from "@/constants/app";
import * as databaseUtil from "../db/databaseUtil";
import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
import { db, retrieveSettingsForActiveAccount } from "../db/index";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
@Component({
components: {
@@ -143,19 +142,23 @@ export default class FeedFilters extends Vue {
async toggleHasVisibleDid() {
this.settingChanged = true;
this.hasVisibleDid = !this.hasVisibleDid;
await db.settings.update(MASTER_SETTINGS_KEY, {
await databaseUtil.updateDefaultSettings({
filterFeedByVisible: this.hasVisibleDid,
});
if (USE_DEXIE_DB) {
await db.settings.update(MASTER_SETTINGS_KEY, {
filterFeedByVisible: this.hasVisibleDid,
});
}
}
async toggleNearby() {
this.settingChanged = true;
this.isNearby = !this.isNearby;
const platformService = PlatformServiceFactory.getInstance();
await platformService.dbExec(
`UPDATE settings SET filterFeedByNearby = ? WHERE id = ?`,
[this.isNearby, MASTER_SETTINGS_KEY],
);
await databaseUtil.updateDefaultSettings({
filterFeedByNearby: this.isNearby,
});
if (USE_DEXIE_DB) {
await db.settings.update(MASTER_SETTINGS_KEY, {
@@ -169,11 +172,10 @@ export default class FeedFilters extends Vue {
this.settingChanged = true;
}
const platformService = PlatformServiceFactory.getInstance();
await platformService.dbExec(
`UPDATE settings SET filterFeedByNearby = ? AND filterFeedByVisible = ? WHERE id = ?`,
[false, false, MASTER_SETTINGS_KEY],
);
await databaseUtil.updateDefaultSettings({
filterFeedByNearby: false,
filterFeedByVisible: false,
});
if (USE_DEXIE_DB) {
await db.settings.update(MASTER_SETTINGS_KEY, {
@@ -191,11 +193,10 @@ export default class FeedFilters extends Vue {
this.settingChanged = true;
}
const platformService = PlatformServiceFactory.getInstance();
await platformService.dbExec(
`UPDATE settings SET filterFeedByNearby = ? AND filterFeedByVisible = ? WHERE id = ?`,
[true, true, MASTER_SETTINGS_KEY],
);
await databaseUtil.updateDefaultSettings({
filterFeedByNearby: true,
filterFeedByVisible: true,
});
if (USE_DEXIE_DB) {
await db.settings.update(MASTER_SETTINGS_KEY, {

View File

@@ -321,7 +321,7 @@ export default class GiftedDialog extends Vue {
);
if (!result.success) {
const errorMessage = this.getGiveCreationErrorMessage(result);
const errorMessage = result.error;
logger.error("Error with give creation result:", result);
this.$notify(
{
@@ -367,19 +367,6 @@ export default class GiftedDialog extends Vue {
// Helper functions for readability
/**
* @param result direct response eg. ErrorResult or SuccessResult (potentially with embedded "data")
* @returns best guess at an error message
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getGiveCreationErrorMessage(result: any) {
return (
result.error?.userMessage ||
result.error?.error ||
result.response?.data?.error?.message
);
}
explainData() {
this.$notify(
{

View File

@@ -227,6 +227,7 @@ export default class GivenPrompts extends Vue {
let someContactDbIndex = Math.floor(Math.random() * this.numContacts);
let count = 0;
// as long as the index has an entry, loop
while (
this.shownContactDbIndices[someContactDbIndex] != null &&
@@ -245,9 +246,8 @@ export default class GivenPrompts extends Vue {
[someContactDbIndex],
);
if (result) {
this.currentContact = databaseUtil.mapQueryResultToValues(result)[
someContactDbIndex
] as unknown as Contact;
const mappedContacts = databaseUtil.mapQueryResultToValues(result);
this.currentContact = mappedContacts[0] as unknown as Contact;
}
if (USE_DEXIE_DB) {
await db.open();

View File

@@ -48,12 +48,15 @@
<span>
{{ didInfo(visDid) }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(visDid)">
<a :href="`/did/${visDid}`" class="text-blue-500">
<router-link
:to="{ path: '/did/' + encodeURIComponent(visDid) }"
class="text-blue-500"
>
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
/>
</a>
</router-link>
</span>
</span>
</div>

View File

@@ -250,7 +250,7 @@ export default class OfferDialog extends Vue {
);
if (!result.success) {
const errorMessage = this.getOfferCreationErrorMessage(result);
const errorMessage = result.error;
logger.error("Error with offer creation result:", result);
this.$notify(
{
@@ -290,21 +290,6 @@ export default class OfferDialog extends Vue {
);
}
}
// Helper functions for readability
/**
* @param result direct response eg. ErrorResult or SuccessResult (potentially with embedded "data")
* @returns best guess at an error message
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getOfferCreationErrorMessage(result: any) {
return (
serverMessageForUser(result) ||
result.error?.userMessage ||
result.error?.error
);
}
}
</script>

View File

@@ -259,7 +259,7 @@ export default class OnboardingDialog extends Vue {
this.visible = true;
if (this.page === OnboardPage.Create) {
// we'll assume that they've been through all the other pages
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
finishedOnboarding: true,
});
if (USE_DEXIE_DB) {
@@ -273,7 +273,7 @@ export default class OnboardingDialog extends Vue {
async onClickClose(done?: boolean, goHome?: boolean) {
this.visible = false;
if (done) {
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
finishedOnboarding: true,
});
if (USE_DEXIE_DB) {

View File

@@ -38,14 +38,14 @@ export default class TopMessage extends Vue {
settings.apiServer !== AppString.PROD_ENDORSER_API_SERVER
) {
const didPrefix = settings.activeDid?.slice(11, 15);
this.message = "You're linked to a non-prod server, user " + didPrefix;
this.message = "You're not using prod, user " + didPrefix;
} else if (
settings.warnIfProdServer &&
settings.apiServer === AppString.PROD_ENDORSER_API_SERVER
) {
const didPrefix = settings.activeDid?.slice(11, 15);
this.message =
"You're linked to the production server, user " + didPrefix;
"You are using prod, user " + didPrefix;
}
} catch (err: unknown) {
this.$notify(

View File

@@ -37,7 +37,20 @@ export async function updateDefaultSettings(
}
}
export async function updateAccountSettings(
export async function insertDidSpecificSettings(
did: string,
settings: Partial<Settings> = {},
): Promise<boolean> {
const platform = PlatformServiceFactory.getInstance();
const { sql, params } = generateInsertStatement(
{ ...settings, accountDid: did }, // make sure accountDid is set to the given value
"settings",
);
const result = await platform.dbExec(sql, params);
return result.changes === 1;
}
export async function updateDidSpecificSettings(
accountDid: string,
settingsChanges: Settings,
): Promise<boolean> {
@@ -96,9 +109,6 @@ export async function retrieveSettingsForActiveAccount(): Promise<Settings> {
const defaultSettings = await retrieveSettingsForDefaultAccount();
// If no active DID, return defaults
if (!defaultSettings.activeDid) {
logConsoleAndDb(
"[databaseUtil] No active DID found, returning default settings",
);
return defaultSettings;
}
@@ -111,9 +121,7 @@ export async function retrieveSettingsForActiveAccount(): Promise<Settings> {
);
if (!result?.values?.length) {
logConsoleAndDb(
`[databaseUtil] No account-specific settings found for ${defaultSettings.activeDid}`,
);
// we created DID-specific settings when generated or imported, so this shouldn't happen
return defaultSettings;
}
@@ -122,6 +130,7 @@ export async function retrieveSettingsForActiveAccount(): Promise<Settings> {
result.columns,
result.values,
)[0] as Settings;
const overrideSettingsFiltered = Object.fromEntries(
Object.entries(overrideSettings).filter(([_, v]) => v !== null),
);
@@ -131,17 +140,7 @@ export async function retrieveSettingsForActiveAccount(): Promise<Settings> {
// Handle searchBoxes parsing
if (settings.searchBoxes) {
try {
// @ts-expect-error - the searchBoxes field is a string in the DB
settings.searchBoxes = JSON.parse(settings.searchBoxes);
} catch (error) {
logConsoleAndDb(
`[databaseUtil] Failed to parse searchBoxes for ${defaultSettings.activeDid}: ${error}`,
true,
);
// Reset to empty array on parse failure
settings.searchBoxes = [];
}
settings.searchBoxes = parseJsonField(settings.searchBoxes, []);
}
return settings;
@@ -241,6 +240,7 @@ export function generateInsertStatement(
const values = Object.values(model).filter((value) => value !== undefined);
const placeholders = values.map(() => "?").join(", ");
const insertSql = `INSERT INTO ${tableName} (${columns.join(", ")}) VALUES (${placeholders})`;
return {
sql: insertSql,
params: values,
@@ -312,3 +312,115 @@ export function mapColumnsToValues(
return obj;
});
}
/**
* Debug function to inspect raw settings data in the database
* This helps diagnose issues with data corruption or malformed JSON
* @param did Optional DID to inspect specific account settings
* @author Matthew Raymer
*/
export async function debugSettingsData(did?: string): Promise<void> {
try {
const platform = PlatformServiceFactory.getInstance();
// Get all settings records
const allSettings = await platform.dbQuery("SELECT * FROM settings");
logConsoleAndDb(
`[DEBUG] Total settings records: ${allSettings?.values?.length || 0}`,
false,
);
if (allSettings?.values?.length) {
allSettings.values.forEach((row, index) => {
const settings = mapColumnsToValues(allSettings.columns, [row])[0];
logConsoleAndDb(`[DEBUG] Settings record ${index + 1}:`, false);
logConsoleAndDb(`[DEBUG] - ID: ${settings.id}`, false);
logConsoleAndDb(`[DEBUG] - accountDid: ${settings.accountDid}`, false);
logConsoleAndDb(`[DEBUG] - activeDid: ${settings.activeDid}`, false);
if (settings.searchBoxes) {
logConsoleAndDb(
`[DEBUG] - searchBoxes type: ${typeof settings.searchBoxes}`,
false,
);
logConsoleAndDb(
`[DEBUG] - searchBoxes value: ${String(settings.searchBoxes)}`,
false,
);
// Try to parse it
try {
const parsed = JSON.parse(String(settings.searchBoxes));
logConsoleAndDb(
`[DEBUG] - searchBoxes parsed successfully: ${JSON.stringify(parsed)}`,
false,
);
} catch (parseError) {
logConsoleAndDb(
`[DEBUG] - searchBoxes parse error: ${parseError}`,
true,
);
}
}
logConsoleAndDb(
`[DEBUG] - Full record: ${JSON.stringify(settings, null, 2)}`,
false,
);
});
}
// If specific DID provided, also check accounts table
if (did) {
const account = await platform.dbQuery(
"SELECT * FROM accounts WHERE did = ?",
[did],
);
logConsoleAndDb(
`[DEBUG] Account for ${did}: ${JSON.stringify(account, null, 2)}`,
false,
);
}
} catch (error) {
logConsoleAndDb(`[DEBUG] Error inspecting settings data: ${error}`, true);
}
}
/**
* Platform-agnostic JSON parsing utility
* Handles different SQLite implementations:
* - Web SQLite (wa-sqlite/absurd-sql): Auto-parses JSON strings to objects
* - Capacitor SQLite: Returns raw strings that need manual parsing
*
* @param value The value to parse (could be string or already parsed object)
* @param defaultValue Default value if parsing fails
* @returns Parsed object or default value
* @author Matthew Raymer
*/
export function parseJsonField<T>(value: unknown, defaultValue: T): T {
try {
// If already an object (web SQLite auto-parsed), return as-is
if (typeof value === "object" && value !== null) {
return value as T;
}
// If it's a string (Capacitor SQLite or fallback), parse it
if (typeof value === "string") {
return JSON.parse(value) as T;
}
// If it's null/undefined, return default
if (value === null || value === undefined) {
return defaultValue;
}
return defaultValue;
} catch (error) {
logConsoleAndDb(
`[databaseUtil] Failed to parse JSON field: ${error}`,
true,
);
return defaultValue;
}
}

View File

@@ -1,6 +1,4 @@
import { AxiosResponse } from "axios";
import { GiverReceiverInputInfo } from "../libs/util";
import { ErrorResult, ResultWithType } from "./common";
export interface GiverOutputInfo {
action: string;
@@ -47,12 +45,3 @@ export interface ProviderInfo {
*/
linkConfirmed: boolean;
}
// Type for createAndSubmitClaim result
export type CreateAndSubmitClaimResult = SuccessResult | ErrorResult;
// Update SuccessResult to use ClaimResult
export interface SuccessResult extends ResultWithType {
type: "success";
response: AxiosResponse<ClaimResult>;
}

View File

@@ -15,10 +15,6 @@ export interface GenericCredWrapper<T extends GenericVerifiableCredential> {
publicUrls?: Record<string, string>;
}
export interface ResultWithType {
type: string;
}
export interface ErrorResponse {
error?: {
message?: string;
@@ -30,11 +26,6 @@ export interface InternalError {
userMessage?: string;
}
export interface ErrorResult extends ResultWithType {
type: "error";
error: InternalError;
}
export interface KeyMeta {
did: string;
publicKeyHex: string;

View File

@@ -30,7 +30,7 @@ import { z } from "zod";
// Add a union type of all valid route paths
export const VALID_DEEP_LINK_ROUTES = [
"user-profile",
"project-details",
"project",
"onboard-meeting-setup",
"invite-one-accept",
"contact-import",
@@ -61,7 +61,7 @@ export const deepLinkSchemas = {
"user-profile": z.object({
id: z.string(),
}),
"project-details": z.object({
"project": z.object({
id: z.string(),
}),
"onboard-meeting-setup": z.object({

View File

@@ -1,5 +1,6 @@
export type {
// From common.ts
CreateAndSubmitClaimResult,
GenericCredWrapper,
GenericVerifiableCredential,
KeyMeta,
@@ -18,11 +19,6 @@ export type {
RegisterActionClaim,
} from "./claims";
export type {
// From claims-result.ts
CreateAndSubmitClaimResult,
} from "./claims-result";
export type {
// From records.ts
PlanSummaryRecord,

View File

@@ -979,7 +979,7 @@ export const createAndSubmitConfirmation = async (
handleId: string | undefined,
apiServer: string,
axios: Axios,
) => {
): Promise<CreateAndSubmitClaimResult> => {
const goodClaim = removeSchemaContext(
removeVisibleToDids(
addLastClaimOrHandleAsIdIfMissing(claim, lastClaimId, handleId),

View File

@@ -44,6 +44,7 @@ import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { sha256 } from "ethereum-cryptography/sha256";
import { IIdentifier } from "@veramo/core";
import { insertDidSpecificSettings, parseJsonField } from "../db/databaseUtil";
export interface GiverReceiverInputInfo {
did?: string;
@@ -697,6 +698,7 @@ export async function saveNewIdentity(
];
await platformService.dbExec(sql, params);
await databaseUtil.updateDefaultSettings({ activeDid: identity.did });
await databaseUtil.insertDidSpecificSettings(identity.did);
if (USE_DEXIE_DB) {
// one of the few times we use accountsDBPromise directly; try to avoid more usage
@@ -710,6 +712,7 @@ export async function saveNewIdentity(
publicKeyHex: identity.keys[0].publicKeyHex,
});
await updateDefaultSettings({ activeDid: identity.did });
await insertDidSpecificSettings(identity.did);
}
} catch (error) {
logger.error("Failed to update default settings:", error);
@@ -732,7 +735,9 @@ export const generateSaveAndActivateIdentity = async (): Promise<string> => {
const newId = newIdentifier(address, publicHex, privateHex, derivationPath);
await saveNewIdentity(newId, mnemonic, derivationPath);
await databaseUtil.updateAccountSettings(newId.did, { isRegistered: false });
await databaseUtil.updateDidSpecificSettings(newId.did, {
isRegistered: false,
});
if (USE_DEXIE_DB) {
await updateAccountSettings(newId.did, { isRegistered: false });
}
@@ -774,7 +779,7 @@ export const registerSaveAndActivatePasskey = async (
): Promise<Account> => {
const account = await registerAndSavePasskey(keyName);
await databaseUtil.updateDefaultSettings({ activeDid: account.did });
await databaseUtil.updateAccountSettings(account.did, {
await databaseUtil.updateDidSpecificSettings(account.did, {
isRegistered: false,
});
if (USE_DEXIE_DB) {
@@ -862,7 +867,7 @@ export const contactToCsvLine = (contact: Contact): string => {
// Handle contactMethods array by stringifying it
const contactMethodsStr = contact.contactMethods
? escapeField(JSON.stringify(contact.contactMethods))
? escapeField(JSON.stringify(parseJsonField(contact.contactMethods, [])))
: "";
const fields = [
@@ -907,7 +912,7 @@ export const contactsToExportJson = (contacts: Contact[]): DatabaseExport => {
did: contact.did,
name: contact.name || null,
contactMethods: contact.contactMethods
? JSON.stringify(contact.contactMethods)
? JSON.stringify(parseJsonField(contact.contactMethods, []))
: null,
nextPubKeyHashB64: contact.nextPubKeyHashB64 || null,
notes: contact.notes || null,

View File

@@ -28,7 +28,7 @@
*
* Supported Routes:
* - user-profile: View user profile
* - project-details: View project details
* - project: View project details
* - onboard-meeting-setup: Setup onboarding meeting
* - invite-one-accept: Accept invitation
* - contact-import: Import contacts
@@ -81,18 +81,16 @@ export class DeepLinkHandler {
string,
{ name: string; paramKey?: string }
> = {
"user-profile": { name: "user-profile" },
"project-details": { name: "project-details" },
"onboard-meeting-setup": { name: "onboard-meeting-setup" },
"invite-one-accept": { name: "invite-one-accept" },
"contact-import": { name: "contact-import" },
"confirm-gift": { name: "confirm-gift" },
claim: { name: "claim" },
"claim-cert": { name: "claim-cert" },
"claim": { name: "claim" },
"claim-add-raw": { name: "claim-add-raw" },
"contact-edit": { name: "contact-edit", paramKey: "did" },
contacts: { name: "contacts" },
did: { name: "did", paramKey: "did" },
"claim-cert": { name: "claim-cert" },
"confirm-gift": { name: "confirm-gift" },
"did": { name: "did", paramKey: "did" },
"invite-one-accept": { name: "invite-one-accept" },
"onboard-meeting-members": { name: "onboard-meeting-members" },
"onboard-meeting-setup": { name: "onboard-meeting-setup" },
"project": { name: "project" },
"user-profile": { name: "user-profile" },
};
/**

View File

@@ -5,6 +5,7 @@ import {
CameraSource,
CameraDirection,
} from "@capacitor/camera";
import { Capacitor } from "@capacitor/core";
import { Share } from "@capacitor/share";
import {
SQLiteConnection,
@@ -247,7 +248,7 @@ export class CapacitorPlatformService implements PlatformService {
hasFileSystem: true,
hasCamera: true,
isMobile: true,
isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent),
isIOS: Capacitor.getPlatform() === "ios",
hasFileDownload: false,
needsFileHandlingInstructions: true,
isNativeApp: true,

View File

@@ -1814,7 +1814,7 @@ export default class AccountViewView extends Vue {
if (!this.isRegistered) {
// the user was not known to be registered, but now they are (because we got no error) so let's record it
try {
await databaseUtil.updateAccountSettings(did, {
await databaseUtil.updateDidSpecificSettings(did, {
isRegistered: true,
});
if (USE_DEXIE_DB) {
@@ -2018,7 +2018,7 @@ export default class AccountViewView extends Vue {
if ((error as any).response.status === 404) {
logger.error("The image was already deleted:", error);
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
profileImageUrl: undefined,
});
if (USE_DEXIE_DB) {

View File

@@ -198,7 +198,7 @@ export default class ClaimAddRawView extends Vue {
this.apiServer,
this.axios,
);
if (result.type === "success") {
if (result.success) {
this.$notify(
{
group: "alert",

View File

@@ -46,6 +46,7 @@
</h2>
<div class="flex justify-center w-full">
<router-link
v-if="veriClaim.id"
:to="'/claim-cert/' + encodeURIComponent(veriClaim.id)"
class="text-blue-500 mt-2"
title="Printable Certificate"
@@ -292,12 +293,17 @@
<div class="text-sm">
{{ didInfo(confirmerId) }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(confirmerId)">
<a :href="`/did/${confirmerId}`" class="text-blue-500">
<router-link
:to="{
path: '/did/' + encodeURIComponent(confirmerId),
}"
class="text-blue-500"
>
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
/>
</a>
</router-link>
</span>
</div>
</div>
@@ -329,12 +335,17 @@
<div class="text-sm">
{{ didInfo(confsVisibleTo) }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(confsVisibleTo)">
<a :href="`/did/${confsVisibleTo}`" class="text-blue-500">
<router-link
:to="{
path: '/did/' + encodeURIComponent(confsVisibleTo),
}"
class="text-blue-500"
>
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
/>
</a>
</router-link>
</span>
</div>
</div>
@@ -443,12 +454,17 @@
<span>
{{ didInfo(visDid) }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(visDid)">
<a :href="`/did/${visDid}`" class="text-blue-500">
<router-link
:to="{
path: '/did/' + encodeURIComponent(visDid),
}"
class="text-blue-500"
>
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
/>
</a>
</router-link>
</span>
<span v-if="veriClaim.publicUrls?.[visDid]"
>, found at&nbsp;<a
@@ -925,7 +941,7 @@ export default class ClaimView extends Vue {
this.apiServer,
this.axios,
);
if (result.type === "success") {
if (result.success) {
this.$notify(
{
group: "alert",

View File

@@ -407,14 +407,14 @@
</a>
</div>
<div class="mt-2 ml-2">
<a
<router-link
v-if="isRegistered"
class="text-blue-500 cursor-pointer"
:href="urlForNewGive"
:to="urlForNewGive"
>
<font-awesome icon="file-lines" />
Record a Give Similar to the Original
</a>
</router-link>
</div>
</div>
</div>
@@ -831,7 +831,7 @@ export default class ConfirmGiftView extends Vue {
this.apiServer,
this.axios,
);
if (result.type === "success") {
if (result.success) {
this.$notify(
{
group: "alert",

View File

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

View File

@@ -213,6 +213,7 @@ import {
} from "../db/index";
import { Contact, ContactMethod } from "../db/tables/contacts";
import * as databaseUtil from "../db/databaseUtil";
import { parseJsonField } from "../db/databaseUtil";
import * as libsUtil from "../libs/util";
import {
capitalizeAndInsertSpacesBeforeCaps,
@@ -289,7 +290,7 @@ function dbRecordToContact(record: ContactDbRecord): Contact {
profileImageUrl: safeString(record.profileImageUrl),
publicKeyBase64: safeString(record.publicKeyBase64),
nextPubKeyHashB64: safeString(record.nextPubKeyHashB64),
contactMethods: JSON.parse(record.contactMethods || "[]"),
contactMethods: parseJsonField(record.contactMethods, []),
};
}

View File

@@ -124,6 +124,7 @@ import UserNameDialog from "../components/UserNameDialog.vue";
import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer";
import { retrieveAccountMetadata } from "../libs/util";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { parseJsonField } from "../db/databaseUtil";
interface QRScanResult {
rawValue?: string;
@@ -474,7 +475,9 @@ export default class ContactQRScan extends Vue {
// Add new contact
// @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(
contact as unknown as Record<string, unknown>,
"contacts",

View File

@@ -171,6 +171,7 @@ import { db, retrieveSettingsForActiveAccount } from "../db/index";
import { Contact } from "../db/tables/contacts";
import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
import * as databaseUtil from "../db/databaseUtil";
import { parseJsonField } from "../db/databaseUtil";
import { getContactJwtFromJwtUrl } from "../libs/crypto";
import {
generateEndorserJwtUrlForAccount,
@@ -778,7 +779,9 @@ export default class ContactQRScanShow extends Vue {
// Add new contact
// @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(
contact as unknown as Record<string, unknown>,
"contacts",

View File

@@ -103,8 +103,12 @@
v-if="!showGiveNumbers"
:class="
contactsSelected.length > 0
? 'text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-3 px-3 py-1.5 rounded-md cursor-pointer'
: 'text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-slate-300 ml-3 px-3 py-1.5 rounded-md cursor-not-allowed'
? 'text-md bg-gradient-to-b from-blue-400 to-blue-700 ' +
'shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ' +
'ml-3 px-3 py-1.5 rounded-md cursor-pointer'
: 'text-md bg-gradient-to-b from-slate-400 to-slate-700 ' +
'shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-slate-300 ' +
'ml-3 px-3 py-1.5 rounded-md cursor-not-allowed'
"
data-testId="copySelectedContactsButtonTop"
@click="copySelectedContacts()"
@@ -112,9 +116,9 @@
Copy
</button>
<font-awesome
@click="showCopySelectionsInfo()"
icon="circle-info"
class="text-2xl text-blue-500 ml-2"
@click="showCopySelectionsInfo()"
/>
</div>
</div>
@@ -142,16 +146,13 @@
class="text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md"
@click="toggleShowContactAmounts()"
>
{{
showGiveNumbers ? "Hide Actions" : "See Actions"
}}
{{ showGiveNumbers ? "Hide Actions" : "See Actions" }}
</button>
</div>
</div>
<div v-if="showGiveNumbers" class="my-3">
<div class="w-full text-center text-sm italic text-slate-600">
Only the most recent hours are included. <br />To see more,
click
Only the most recent hours are included. <br />To see more, click
<span
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1 py-0.5 rounded"
>
@@ -223,9 +224,7 @@
/>
</router-link>
<span class="text-xs truncate">{{
contact.did
}}</span>
<span class="text-xs truncate">{{ contact.did }}</span>
</div>
<div class="text-sm">
{{ contact.notes }}
@@ -237,7 +236,7 @@
v-if="showGiveNumbers && contact.did != activeDid"
class="flex gap-1.5 items-end"
>
<div class='text-center'>
<div class="text-center">
<div class="text-xs leading-none mb-1">From/To</div>
<div class="flex items-center">
<button
@@ -317,8 +316,12 @@
v-if="!showGiveNumbers"
:class="
contactsSelected.length > 0
? 'text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-3 px-3 py-1.5 rounded-md cursor-pointer'
: 'text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-slate-300 ml-3 px-3 py-1.5 rounded-md cursor-not-allowed'
? 'text-md bg-gradient-to-b from-blue-400 to-blue-700 ' +
'shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ' +
'ml-3 px-3 py-1.5 rounded-md cursor-pointer'
: 'text-md bg-gradient-to-b from-slate-400 to-slate-700 ' +
'shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-slate-300 ' +
'ml-3 px-3 py-1.5 rounded-md cursor-not-allowed'
"
@click="copySelectedContacts()"
>
@@ -541,7 +544,7 @@ export default class ContactsView extends Vue {
if (response.status != 201) {
throw { error: { response: response } };
}
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
isRegistered: true,
});
if (USE_DEXIE_DB) {
@@ -997,8 +1000,6 @@ export default class ContactsView extends Vue {
newContact as unknown as Record<string, unknown>,
"contacts",
);
logger.error("sql", sql);
logger.error("params", params);
let contactPromise = platformService.dbExec(sql, params);
if (USE_DEXIE_DB) {
// @ts-expect-error since the result of this promise won't be used, and this will go away soon

View File

@@ -788,7 +788,7 @@ export default class DiscoverView extends Vue {
const route = {
path: this.isProjectsActive
? "/project/" + encodeURIComponent(id)
: "/userProfile/" + encodeURIComponent(id),
: "/user-profile/" + encodeURIComponent(id),
};
this.$router.push(route);
}

View File

@@ -826,7 +826,7 @@ export default class GiftedDetails extends Vue {
}
if (!result.success) {
const errorMessage = this.getGiveCreationErrorMessage(result);
const errorMessage = result.error;
logger.error("Error with give creation result:", result);
this.$notify(
{
@@ -899,19 +899,6 @@ export default class GiftedDetails extends Vue {
// Helper functions for readability
/**
* @param result direct response eg. ErrorResult or SuccessResult (potentially with embedded "data")
* @returns best guess at an error message
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getGiveCreationErrorMessage(result: any) {
return (
result.error?.userMessage ||
result.error?.error ||
result.response?.data?.error?.message
);
}
explainData() {
this.$notify(
{

View File

@@ -24,11 +24,11 @@
<!-- eslint-disable prettier/prettier max-len -->
<div>
<p>
This app focuses on gifts & gratitude, using them to build cool things together with your network.
This app focuses on raw gratitude, using it to build cool things together with your network.
</p>
<p class="ml-4">
If you'd like to see the page-by-page help,
If you'd like to see the page-by-page help again,
<span
class="text-blue-500 cursor-pointer"
@click="unsetFinishedOnboarding()"
@@ -555,9 +555,6 @@
initiative.
</p>
<h2 class="text-xl font-semibold">What app version is this?</h2>
<p>{{ package.version }} ({{ commitHash }})</p>
<h2 class="text-xl font-semibold">
I have other questions or feedback, like getting a new profile or removing my data or requesting an improvement.
</h2>
@@ -567,6 +564,28 @@
>info@TimeSafari.app</a
>
</p>
<h2 class="text-xl font-semibold">What app version is this?</h2>
<p>{{ package.version }} ({{ commitHash }})</p>
<div v-if="Capacitor.isNativePlatform()">
<h2 class="text-xl font-semibold">
Do I have the latest version?
</h2>
<p v-if="Capacitor.getPlatform() === 'ios'">
<a href="https://apps.apple.com/us/app/time-safari/id6742664907" target="_blank" class="text-blue-500">
Check the App Store.
</a>
</p>
<p v-else-if="Capacitor.getPlatform() === 'android'">
<a href="https://timesafari.app/app.apk" target="_blank" class="text-blue-500">
Download the latest APK to see.
</a>
</p>
<p v-else>
Sorry, your platform of '{{ Capacitor.getPlatform() }}' is not recognized.
</p>
</div>
</div>
<!-- eslint enable -->
</section>
@@ -603,6 +622,7 @@ export default class HelpView extends Vue {
showVerifiable = false;
APP_SERVER = APP_SERVER;
Capacitor = Capacitor;
// Ideally, we put no functionality in here, especially in the setup,
// because we never want this page to have a chance of throwing an error.
@@ -622,7 +642,7 @@ export default class HelpView extends Vue {
}
if (settings.activeDid) {
await databaseUtil.updateAccountSettings(settings.activeDid, {
await databaseUtil.updateDidSpecificSettings(settings.activeDid, {
finishedOnboarding: false,
});
if (USE_DEXIE_DB) {

View File

@@ -630,7 +630,7 @@ export default class HomeView extends Vue {
this.activeDid,
);
if (resp.status === 200) {
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
isRegistered: true,
...(await databaseUtil.retrieveSettingsForActiveAccount()),
});
@@ -785,7 +785,7 @@ export default class HomeView extends Vue {
if (USE_DEXIE_DB) {
settings = await retrieveSettingsForActiveAccount();
}
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
apiServer: this.apiServer,
isRegistered: true,
...settings,
@@ -1843,7 +1843,7 @@ export default class HomeView extends Vue {
this.axios,
);
if (result.type === "success") {
if (result.success) {
this.$notify(
{
group: "alert",

View File

@@ -79,7 +79,8 @@ import {
newIdentifier,
nextDerivationPath,
} from "../libs/crypto";
import { accountsDBPromise, db } from "../db/index";
import * as databaseUtil from "../db/databaseUtil";
import { db } from "../db/index";
import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
import {
retrieveAllAccountsMetadata,
@@ -164,23 +165,15 @@ export default class ImportAccountView extends Vue {
try {
await saveNewIdentity(newId, mne, newDerivPath);
if (USE_DEXIE_DB) {
const accountsDB = await accountsDBPromise;
await accountsDB.accounts.add({
dateCreated: new Date().toISOString(),
derivationPath: newDerivPath,
did: newId.did,
identity: JSON.stringify(newId),
mnemonic: mne,
publicKeyHex: newId.keys[0].publicKeyHex,
});
}
// record that as the active DID
const platformService = PlatformServiceFactory.getInstance();
await platformService.dbExec("UPDATE settings SET activeDid = ?", [
newId.did,
]);
await databaseUtil.updateDidSpecificSettings(newId.did, {
isRegistered: false,
});
if (USE_DEXIE_DB) {
await db.settings.update(MASTER_SETTINGS_KEY, {
activeDid: newId.did,

View File

@@ -257,7 +257,7 @@ export default class NewActivityView extends Vue {
async expandOffersToUserAndMarkRead() {
this.showOffersDetails = !this.showOffersDetails;
if (this.showOffersDetails) {
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId,
});
if (USE_DEXIE_DB) {
@@ -285,7 +285,7 @@ export default class NewActivityView extends Vue {
);
if (index !== -1 && index < this.newOffersToUser.length - 1) {
// Set to the next offer's jwtId
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
lastAckedOfferToUserJwtId: this.newOffersToUser[index + 1].jwtId,
});
if (USE_DEXIE_DB) {
@@ -295,7 +295,7 @@ export default class NewActivityView extends Vue {
}
} else {
// it's the last entry (or not found), so just keep it the same
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
lastAckedOfferToUserJwtId: this.lastAckedOfferToUserJwtId,
});
if (USE_DEXIE_DB) {
@@ -319,7 +319,7 @@ export default class NewActivityView extends Vue {
this.showOffersToUserProjectsDetails =
!this.showOffersToUserProjectsDetails;
if (this.showOffersToUserProjectsDetails) {
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
lastAckedOfferToUserProjectsJwtId:
this.newOffersToUserProjects[0].jwtId,
});
@@ -349,7 +349,7 @@ export default class NewActivityView extends Vue {
);
if (index !== -1 && index < this.newOffersToUserProjects.length - 1) {
// Set to the next offer's jwtId
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
lastAckedOfferToUserProjectsJwtId:
this.newOffersToUserProjects[index + 1].jwtId,
});
@@ -361,7 +361,7 @@ export default class NewActivityView extends Vue {
}
} else {
// it's the last entry (or not found), so just keep it the same
await databaseUtil.updateAccountSettings(this.activeDid, {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
lastAckedOfferToUserProjectsJwtId:
this.lastAckedOfferToUserProjectsJwtId,
});

View File

@@ -52,16 +52,24 @@
icon="user"
class="fa-fw text-slate-400"
></font-awesome>
{{ issuerInfoObject?.displayName }}
<span v-if="!serverUtil.isEmptyOrHiddenDid(issuer)">
<a :href="`/did/${issuer}`" class="text-blue-500">
<span class="truncate inline-block max-w-[calc(100%-2rem)]">
{{ issuerInfoObject?.displayName }}
</span>
<span class="inline-flex items-center">
<router-link
:to="{
path: '/did/' + encodeURIComponent(issuer),
}"
class="text-blue-500 ml-1"
title="See more about this person"
>
<font-awesome
icon="arrow-up-right-from-square"
class="fa-fw"
/>
</a>
</router-link>
</span>
<span v-else-if="serverUtil.isHiddenDid(issuer)">
<span v-if="serverUtil.isHiddenDid(issuer)" class="ml-1">
<font-awesome
icon="info-circle"
class="fa-fw text-blue-500 cursor-pointer"
@@ -1425,7 +1433,7 @@ export default class ProjectViewView extends Vue {
this.apiServer,
this.axios,
);
if (result.type === "success") {
if (result.success) {
this.$notify(
{
group: "alert",

View File

@@ -155,7 +155,7 @@ import { Contact } from "../db/tables/contacts";
import {
GenericCredWrapper,
GenericVerifiableCredential,
ErrorResult,
CreateAndSubmitClaimResult,
} from "../interfaces";
import {
BVC_MEETUPS_PROJECT_CLAIM_ID,
@@ -298,13 +298,13 @@ export default class QuickActionBvcBeginView extends Vue {
}
// in parallel, make a confirmation for each selected claim and send them all to the server
const confirmResults = await Promise.allSettled(
const confirmResults: PromiseSettledResult<CreateAndSubmitClaimResult>[] = await Promise.allSettled(
this.claimsToConfirmSelected.map(async (jwtId) => {
const record = this.claimsToConfirm.find(
(claim) => claim.id === jwtId,
);
if (!record) {
return { type: "error", error: "Record not found." };
return { success: false, error: "Record not found." };
}
return createAndSubmitConfirmation(
this.activeDid,
@@ -318,8 +318,8 @@ export default class QuickActionBvcBeginView extends Vue {
);
// check for any rejected confirmations
const confirmsSucceeded = confirmResults.filter(
(result) =>
result.status === "fulfilled" && result.value.type === "success",
// 'fulfilled' is the status in a successful PromiseFulfilledResult
(result) => result.status === "fulfilled" && result.value.success,
);
if (confirmsSucceeded.length < this.claimsToConfirmSelected.length) {
logger.error("Error sending confirmations:", confirmResults);
@@ -353,7 +353,7 @@ export default class QuickActionBvcBeginView extends Vue {
undefined,
BVC_MEETUPS_PROJECT_CLAIM_ID,
);
giveSucceeded = giveResult.type === "success";
giveSucceeded = giveResult.success;
if (!giveSucceeded) {
logger.error("Error sending give:", giveResult);
this.$notify(
@@ -362,7 +362,7 @@ export default class QuickActionBvcBeginView extends Vue {
type: "danger",
title: "Error",
text:
(giveResult as ErrorResult)?.error?.userMessage ||
(giveResult as CreateAndSubmitClaimResult)?.error ||
"There was an error sending that give.",
},
5000,

View File

@@ -215,7 +215,7 @@ export default class SearchAreaView extends Vue {
if (USE_DEXIE_DB) {
await db.open();
await db.settings.update(MASTER_SETTINGS_KEY, {
searchBoxes: [newSearchBox],
searchBoxes: searchBoxes as unknown, // Type assertion for Dexie compatibility
});
}
this.searchBox = newSearchBox;
@@ -269,7 +269,7 @@ export default class SearchAreaView extends Vue {
if (USE_DEXIE_DB) {
await db.open();
await db.settings.update(MASTER_SETTINGS_KEY, {
searchBoxes: [],
searchBoxes: "[]" as unknown as string, // Type assertion for Dexie compatibility
filterFeedByNearby: false,
});
}