Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 002f240720 | |||
| ffe8d90161 | |||
| 6d6816d1a8 |
@@ -61,9 +61,11 @@ Install dependencies:
|
|||||||
|
|
||||||
* `npx prettier --write ./sw_scripts/`
|
* `npx prettier --write ./sw_scripts/`
|
||||||
|
|
||||||
* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`.
|
* Update the ClickUp tasks & CHANGELOG.md & the version in package.json.
|
||||||
|
|
||||||
* Run a build to make sure package-lock version is updated, linting works, etc: `npm install && npm run build`
|
* Run install & build to make sure package-lock version is updated, linting works, etc: `npm install && npm run build:web`
|
||||||
|
|
||||||
|
* If also deploying to mobile, update the new version in the ios & android filed.
|
||||||
|
|
||||||
* Commit everything (since the commit hash is used the app).
|
* Commit everything (since the commit hash is used the app).
|
||||||
|
|
||||||
@@ -362,7 +364,7 @@ Prerequisites: macOS with Xcode installed
|
|||||||
4. Bump the version to match Android & package.json:
|
4. Bump the version to match Android & package.json:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd ios/App && xcrun agvtool new-version 36 && perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.3;/g" App.xcodeproj/project.pbxproj && cd -
|
cd ios/App && xcrun agvtool new-version 37 && perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.4;/g" App.xcodeproj/project.pbxproj && cd -
|
||||||
# Unfortunately this edits Info.plist directly.
|
# Unfortunately this edits Info.plist directly.
|
||||||
#xcrun agvtool new-marketing-version 0.4.5
|
#xcrun agvtool new-marketing-version 0.4.5
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|
||||||
|
## [1.0.4] - 2025.07.20
|
||||||
|
### Fixed
|
||||||
|
- Deep link for invite-one-accept
|
||||||
|
|
||||||
|
|
||||||
## [1.0.3] - 2025.07.12
|
## [1.0.3] - 2025.07.12
|
||||||
### Changed
|
### Changed
|
||||||
- Photo is pinned to profile mode
|
- Photo is pinned to profile mode
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ android {
|
|||||||
applicationId "app.timesafari.app"
|
applicationId "app.timesafari.app"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 36
|
versionCode 37
|
||||||
versionName "1.0.3"
|
versionName "1.0.4"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|||||||
@@ -403,7 +403,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 36;
|
CURRENT_PROJECT_VERSION = 37;
|
||||||
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
||||||
ENABLE_APP_SANDBOX = NO;
|
ENABLE_APP_SANDBOX = NO;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
@@ -413,7 +413,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.3;
|
MARKETING_VERSION = 1.0.4;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -430,7 +430,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 36;
|
CURRENT_PROJECT_VERSION = 37;
|
||||||
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
||||||
ENABLE_APP_SANDBOX = NO;
|
ENABLE_APP_SANDBOX = NO;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
@@ -440,7 +440,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.3;
|
MARKETING_VERSION = 1.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "timesafari",
|
"name": "timesafari",
|
||||||
"version": "1.0.4-beta",
|
"version": "1.0.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "timesafari",
|
"name": "timesafari",
|
||||||
"version": "1.0.4-beta",
|
"version": "1.0.4",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor-community/sqlite": "6.0.2",
|
"@capacitor-community/sqlite": "6.0.2",
|
||||||
"@capacitor-mlkit/barcode-scanning": "^6.0.0",
|
"@capacitor-mlkit/barcode-scanning": "^6.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "timesafari",
|
"name": "timesafari",
|
||||||
"version": "1.0.4-beta",
|
"version": "1.0.4",
|
||||||
"description": "Time Safari Application",
|
"description": "Time Safari Application",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Time Safari Team"
|
"name": "Time Safari Team"
|
||||||
|
|||||||
@@ -82,7 +82,9 @@ export const baseUrlSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add a union type of all valid route paths
|
// Add a union type of all valid route paths
|
||||||
export const VALID_DEEP_LINK_ROUTES = Object.keys(deepLinkSchemas) as readonly (keyof typeof deepLinkSchemas)[];
|
export const VALID_DEEP_LINK_ROUTES = Object.keys(
|
||||||
|
deepLinkSchemas,
|
||||||
|
) as readonly (keyof typeof deepLinkSchemas)[];
|
||||||
|
|
||||||
export type DeepLinkParams = {
|
export type DeepLinkParams = {
|
||||||
[K in keyof typeof deepLinkSchemas]: z.infer<(typeof deepLinkSchemas)[K]>;
|
[K in keyof typeof deepLinkSchemas]: z.infer<(typeof deepLinkSchemas)[K]>;
|
||||||
@@ -94,4 +96,6 @@ export interface DeepLinkError extends Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use the type to ensure route validation
|
// Use the type to ensure route validation
|
||||||
export const routeSchema = z.enum(VALID_DEEP_LINK_ROUTES as [string, ...string[]]);
|
export const routeSchema = z.enum(
|
||||||
|
VALID_DEEP_LINK_ROUTES as [string, ...string[]],
|
||||||
|
);
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ const handleDeepLink = async (data: { url: string }) => {
|
|||||||
await deepLinkHandler.handleDeepLink(data.url);
|
await deepLinkHandler.handleDeepLink(data.url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("[DeepLink] Error handling deep link: ", error);
|
logger.error("[DeepLink] Error handling deep link: ", error);
|
||||||
let message: string = error instanceof Error ? error.message : safeStringify(error);
|
let message: string =
|
||||||
|
error instanceof Error ? error.message : safeStringify(error);
|
||||||
if (data.url) {
|
if (data.url) {
|
||||||
message += `\nURL: ${data.url}`;
|
message += `\nURL: ${data.url}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,10 @@ import { logConsoleAndDb } from "../db/databaseUtil";
|
|||||||
import type { DeepLinkError } from "../interfaces/deepLinks";
|
import type { DeepLinkError } from "../interfaces/deepLinks";
|
||||||
|
|
||||||
// Helper function to extract the first key from a Zod object schema
|
// Helper function to extract the first key from a Zod object schema
|
||||||
function getFirstKeyFromZodObject(schema: z.ZodObject<any>): string | undefined {
|
function getFirstKeyFromZodObject(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
schema: z.ZodObject<any>,
|
||||||
|
): string | undefined {
|
||||||
const shape = schema.shape;
|
const shape = schema.shape;
|
||||||
const keys = Object.keys(shape);
|
const keys = Object.keys(shape);
|
||||||
return keys.length > 0 ? keys[0] : undefined;
|
return keys.length > 0 ? keys[0] : undefined;
|
||||||
@@ -64,21 +67,25 @@ function getFirstKeyFromZodObject(schema: z.ZodObject<any>): string | undefined
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps deep link routes to their corresponding Vue router names and optional parameter keys.
|
* Maps deep link routes to their corresponding Vue router names and optional parameter keys.
|
||||||
*
|
*
|
||||||
* It's an object where keys are the deep link routes and values are objects with 'name' and 'paramKey'.
|
* It's an object where keys are the deep link routes and values are objects with 'name' and 'paramKey'.
|
||||||
*
|
*
|
||||||
* The paramKey is used to extract the parameter from the route path,
|
* The paramKey is used to extract the parameter from the route path,
|
||||||
* because "router.replace" expects the right parameter name for the route.
|
* because "router.replace" expects the right parameter name for the route.
|
||||||
*/
|
*/
|
||||||
export const ROUTE_MAP: Record<string, { name: string; paramKey?: string }> =
|
export const ROUTE_MAP: Record<string, { name: string; paramKey?: string }> =
|
||||||
Object.entries(deepLinkSchemas).reduce((acc, [routeName, schema]) => {
|
Object.entries(deepLinkSchemas).reduce(
|
||||||
const paramKey = getFirstKeyFromZodObject(schema as z.ZodObject<any>);
|
(acc, [routeName, schema]) => {
|
||||||
acc[routeName] = {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
name: routeName,
|
const paramKey = getFirstKeyFromZodObject(schema as z.ZodObject<any>);
|
||||||
paramKey
|
acc[routeName] = {
|
||||||
|
name: routeName,
|
||||||
|
paramKey,
|
||||||
};
|
};
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, { name: string; paramKey?: string }>);
|
},
|
||||||
|
{} as Record<string, { name: string; paramKey?: string }>,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles processing and routing of deep links in the application.
|
* Handles processing and routing of deep links in the application.
|
||||||
@@ -200,7 +207,10 @@ export class DeepLinkHandler {
|
|||||||
validatedQuery = await schema.parseAsync(query);
|
validatedQuery = await schema.parseAsync(query);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// For parameter validation errors, provide specific error feedback
|
// For parameter validation errors, provide specific error feedback
|
||||||
logConsoleAndDb(`[DeepLink] Invalid parameters for route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with params: ${JSON.stringify(params)} ... and query: ${JSON.stringify(query)}`, true);
|
logConsoleAndDb(
|
||||||
|
`[DeepLink] Invalid parameters for route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with params: ${JSON.stringify(params)} ... and query: ${JSON.stringify(query)}`,
|
||||||
|
true,
|
||||||
|
);
|
||||||
await this.router.replace({
|
await this.router.replace({
|
||||||
name: "deep-link-error",
|
name: "deep-link-error",
|
||||||
params,
|
params,
|
||||||
@@ -223,7 +233,10 @@ export class DeepLinkHandler {
|
|||||||
query: validatedQuery,
|
query: validatedQuery,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logConsoleAndDb(`[DeepLink] Error routing to route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with validated params: ${JSON.stringify(validatedParams)} ... and validated query: ${JSON.stringify(validatedQuery)}`, true);
|
logConsoleAndDb(
|
||||||
|
`[DeepLink] Error routing to route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with validated params: ${JSON.stringify(validatedParams)} ... and validated query: ${JSON.stringify(validatedQuery)}`,
|
||||||
|
true,
|
||||||
|
);
|
||||||
// For parameter validation errors, provide specific error feedback
|
// For parameter validation errors, provide specific error feedback
|
||||||
await this.router.replace({
|
await this.router.replace({
|
||||||
name: "deep-link-error",
|
name: "deep-link-error",
|
||||||
@@ -231,12 +244,11 @@ export class DeepLinkHandler {
|
|||||||
query: {
|
query: {
|
||||||
originalPath: path,
|
originalPath: path,
|
||||||
errorCode: "ROUTING_ERROR",
|
errorCode: "ROUTING_ERROR",
|
||||||
errorMessage: `Error routing to ${routeName}: ${(JSON.stringify(error))}`,
|
errorMessage: `Error routing to ${routeName}: ${JSON.stringify(error)}`,
|
||||||
...validatedQuery,
|
...validatedQuery,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -31,7 +31,11 @@
|
|||||||
<h2>Supported Deep Links</h2>
|
<h2>Supported Deep Links</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(routeItem, index) in validRoutes" :key="index">
|
<li v-for="(routeItem, index) in validRoutes" :key="index">
|
||||||
<code>timesafari://{{ routeItem }}/:{{ deepLinkSchemaKeys[routeItem] }}</code>
|
<code
|
||||||
|
>timesafari://{{ routeItem }}/:{{
|
||||||
|
deepLinkSchemaKeys[routeItem]
|
||||||
|
}}</code
|
||||||
|
>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -41,7 +45,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted } from "vue";
|
import { computed, onMounted } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { VALID_DEEP_LINK_ROUTES, deepLinkSchemas } from "../interfaces/deepLinks";
|
import {
|
||||||
|
VALID_DEEP_LINK_ROUTES,
|
||||||
|
deepLinkSchemas,
|
||||||
|
} from "../interfaces/deepLinks";
|
||||||
import { logConsoleAndDb } from "../db/databaseUtil";
|
import { logConsoleAndDb } from "../db/databaseUtil";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
|
|
||||||
@@ -52,7 +59,7 @@ const deepLinkSchemaKeys = Object.fromEntries(
|
|||||||
Object.entries(deepLinkSchemas).map(([route, schema]) => {
|
Object.entries(deepLinkSchemas).map(([route, schema]) => {
|
||||||
const param = Object.keys(schema.shape)[0];
|
const param = Object.keys(schema.shape)[0];
|
||||||
return [route, param];
|
return [route, param];
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Extract error information from query params
|
// Extract error information from query params
|
||||||
|
|||||||
@@ -128,7 +128,10 @@ export default class InviteOneAcceptView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract JWT from route path
|
// Extract JWT from route path
|
||||||
const jwt = (this.$route.params.jwt as string) || this.$route.query.jwt as string || "";
|
const jwt =
|
||||||
|
(this.$route.params.jwt as string) ||
|
||||||
|
(this.$route.query.jwt as string) ||
|
||||||
|
"";
|
||||||
await this.processInvite(jwt, false);
|
await this.processInvite(jwt, false);
|
||||||
|
|
||||||
this.checkingInvite = false;
|
this.checkingInvite = false;
|
||||||
|
|||||||
@@ -153,9 +153,7 @@ export default class QuickActionBvcBeginView extends Vue {
|
|||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text:
|
text: timeResult?.error || "There was an error sending the time.",
|
||||||
timeResult?.error ||
|
|
||||||
"There was an error sending the time.",
|
|
||||||
},
|
},
|
||||||
5000,
|
5000,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user