diff --git a/.env.development b/.env.development index 91a5e54b..c6742b09 100644 --- a/.env.development +++ b/.env.development @@ -6,6 +6,7 @@ VITE_APP_SERVER=http://localhost:3000 # This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production). VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 -VITE_DEFAULT_IMAGE_API_SERVER=http://localhost:3000 +# Using shared server by default to ease setup, which works for shared test users. +VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000 -VITE_PASSKEYS_ENABLED=true \ No newline at end of file +VITE_PASSKEYS_ENABLED=true diff --git a/.env.production b/.env.production index 01c23cb1..d7669d93 100644 --- a/.env.production +++ b/.env.production @@ -6,5 +6,6 @@ VITE_APP_SERVER=https://timesafari.app # This is the claim ID for actions in the BVC project. VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01GXYPFF7FA03NXKPYY142PY4H VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch + VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch diff --git a/.env.staging b/.env.staging index efddb249..6f8c1fc1 100644 --- a/.env.staging +++ b/.env.staging @@ -6,6 +6,7 @@ VITE_APP_SERVER=https://test.timesafari.app # This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production). VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch + VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch VITE_PASSKEYS_ENABLED=true diff --git a/BUILDING.md b/BUILDING.md index 2917f379..ca85fb1f 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -246,32 +246,7 @@ npm run lint-fix ## Environment Configuration -### Development -Create a `.env.development` file: -```bash -TIME_SAFARI_APP_TITLE="TimeSafari_Dev" -VITE_APP_SERVER=http://localhost:3000 -VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F -VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 -VITE_DEFAULT_IMAGE_API_SERVER=http://localhost:3000 -VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000 -VITE_PASSKEYS_ENABLED=true -``` - -### Test/Staging -Create a `.env.staging` file: -```bash -TIME_SAFARI_APP_TITLE="TimeSafari_Test" -VITE_APP_SERVER=https://test.timesafari.app -VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F -VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch -VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app -VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch -VITE_PASSKEYS_ENABLED=true -``` - -### Production -The `.env.production` file will be used automatically for production builds. +See `.env.*` files for configuration. ## Notes diff --git a/playwright.config-local.ts b/playwright.config-local.ts index f27eac27..deecbfff 100644 --- a/playwright.config-local.ts +++ b/playwright.config-local.ts @@ -119,7 +119,7 @@ export default defineConfig({ */ webServer: { command: - "VITE_APP_SERVER=http://localhost:8081 VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 VITE_PASSKEYS_ENABLED=true npm run dev -- --port=8081", + "VITE_APP_SERVER=http://localhost:8081 VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000 VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_PASSKEYS_ENABLED=true npm run dev -- --port=8081", url: "http://localhost:8081", reuseExistingServer: !process.env.CI, }, diff --git a/src/App.vue b/src/App.vue index 809124f5..00ce8c2b 100644 --- a/src/App.vue +++ b/src/App.vue @@ -333,46 +333,46 @@ export default class App extends Vue { stopAsking = false; - created() { - console.log( - "Component created: Reactivity set up.", - window.location.pathname, - ); - } + // created() { + // console.log( + // "Component created: Reactivity set up.", + // window.location.pathname, + // ); + // } - truncateLongWords(sentence: string) { - return sentence - .split(" ") - .map((word) => (word.length > 30 ? word.slice(0, 30) + "..." : word)) - .join(" "); - } + // beforeCreate() { + // console.log("Component beforeCreate: Instance initialized."); + // } - beforeCreate() { - console.log("Component beforeCreate: Instance initialized."); - } + // beforeMount() { + // console.log("Component beforeMount: Template is about to be rendered."); + // } - beforeMount() { - console.log("Component beforeMount: Template is about to be rendered."); - } + // mounted() { + // console.log("Component mounted: Template is now rendered."); + // } - mounted() { - console.log("Component mounted: Template is now rendered."); - } + // beforeUpdate() { + // console.log("Component beforeUpdate: DOM is about to be updated."); + // } - beforeUpdate() { - console.log("Component beforeUpdate: DOM is about to be updated."); - } + // updated() { + // console.log("Component updated: DOM has been updated."); + // } - updated() { - console.log("Component updated: DOM has been updated."); - } + // beforeUnmount() { + // console.log("Component beforeUnmount: Cleaning up before removal."); + // } - beforeUnmount() { - console.log("Component beforeUnmount: Cleaning up before removal."); - } + // unmounted() { + // console.log("Component unmounted: Component removed from the DOM."); + // } - unmounted() { - console.log("Component unmounted: Component removed from the DOM."); + truncateLongWords(sentence: string) { + return sentence + .split(" ") + .map((word) => (word.length > 30 ? word.slice(0, 30) + "..." : word)) + .join(" "); } async turnOffNotifications( diff --git a/src/components/PhotoDialog.vue b/src/components/PhotoDialog.vue index 3ced63cc..eb519cfd 100644 --- a/src/components/PhotoDialog.vue +++ b/src/components/PhotoDialog.vue @@ -369,6 +369,14 @@ export default class PhotoDialog extends Vue { formData.append("image", this.blob, this.fileName || "snapshot.png"); formData.append("claimType", this.claimType); try { + if ( + window.location.hostname === "localhost" && + !DEFAULT_IMAGE_API_SERVER.includes("localhost") + ) { + console.log( + "Using shared image API server, so only users on that server can play with images.", + ); + } const response = await axios.post( DEFAULT_IMAGE_API_SERVER + "/image", formData, diff --git a/src/main.ts b/src/main.ts index b0785d87..1e6af960 100644 --- a/src/main.ts +++ b/src/main.ts @@ -197,7 +197,7 @@ function setupGlobalErrorHandler(app: VueApp) { ); }; } -console.log("Bootstrapping Vue app..."); +// console.log("Bootstrapping Vue app..."); const app = createApp(App) .component("fa", FontAwesomeIcon) .component("camera", Camera) @@ -209,4 +209,4 @@ const app = createApp(App) setupGlobalErrorHandler(app); app.mount("#app"); -console.log("Vue app mounted."); +// console.log("Vue app mounted."); diff --git a/src/router/index.ts b/src/router/index.ts index 0c63eecd..3d077e69 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -23,8 +23,6 @@ const enterOrStart = async ( const accountsDB = await accountsDBPromise; const num_accounts = await accountsDB.accounts.count(); - console.log("Number of accounts: ", num_accounts); - if (num_accounts > 0) { next(); } else { @@ -299,8 +297,6 @@ const router = createRouter({ routes, }); -console.log("Initial URL:", initialPath); - // Replace initial URL to start at `/` if necessary router.replace(initialPath || "/"); @@ -319,12 +315,10 @@ const errorHandler = ( router.onError(errorHandler); // Assign the error handler to the router instance -router.beforeEach((to, from, next) => { - console.log("Navigating to view:", to.name); - console.log("From view:", from.name); - next(); -}); - -console.log("Initial URL:", window.location.pathname); +// router.beforeEach((to, from, next) => { +// console.log("Navigating to view:", to.name); +// console.log("From view:", from.name); +// next(); +// }); export default router; diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 4a316498..c503b55c 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -365,35 +365,41 @@
Checking…
-
+
{{ limitsMessage }}
-
+

You have done - {{ endorserLimits.doneClaimsThisWeek }} claims out of - {{ endorserLimits.maxClaimsPerWeek }} for this week. Your - claims counter resets at + {{ endorserLimits?.doneClaimsThisWeek || "?" }} claims out of + {{ endorserLimits?.maxClaimsPerWeek || "?" }} for this week. + Your claims counter resets at {{ - readableDate(endorserLimits.nextWeekBeginDateTime) + readableDate(endorserLimits?.nextWeekBeginDateTime) }}

You have done - {{ endorserLimits.doneRegistrationsThisMonth }} registrations - out of {{ endorserLimits.maxRegistrationsPerMonth }} for this - month. - (You cannot register anyone else on your first day.) + {{ + endorserLimits?.doneRegistrationsThisMonth || "?" + }} + registrations + out of + {{ endorserLimits?.maxRegistrationsPerMonth || "?" }} for this + this month. + (You cannot register anyone on your first day.) Your registration counter resets at - {{ readableDate(endorserLimits.nextMonthBeginDateTime) }} + {{ readableDate(endorserLimits?.nextMonthBeginDateTime) }}

-

+

You have uploaded - {{ imageLimits?.doneImagesThisWeek }} images out of - {{ imageLimits?.maxImagesPerWeek }} for this week. Your image - counter resets at + {{ imageLimits?.doneImagesThisWeek || "?" }} images out of + {{ imageLimits?.maxImagesPerWeek || "?" }} for this week. Your + image counter resets at {{ readableDate(imageLimits?.nextWeekBeginDateTime) }} @@ -1215,7 +1221,7 @@ export default class AccountViewView extends Vue { } readableDate(timeStr: string) { - return timeStr.substring(0, timeStr.indexOf("T")); + return timeStr ? timeStr.substring(0, timeStr.indexOf("T")) : "?"; } /** @@ -1230,11 +1236,11 @@ export default class AccountViewView extends Vue { this.publicHex = identity.keys[0].publicKeyHex; this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64"); this.derivationPath = identity.keys[0].meta?.derivationPath as string; - await this.checkLimitsFor(this.activeDid); + await this.checkLimits(); } else if (account?.publicKeyHex) { this.publicHex = account.publicKeyHex as string; this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64"); - await this.checkLimitsFor(this.activeDid); + await this.checkLimits(); } } @@ -1598,11 +1604,13 @@ export default class AccountViewView extends Vue { } /** + * Use "checkLimits" instead. + * * Asynchronously checks rate limits for the given identity. * * Updates component state variables `limits`, `limitsMessage`, and `loadingLimits`. */ - public async checkLimitsFor(did: string) { + private async checkLimitsFor(did: string) { this.loadingLimits = true; this.limitsMessage = ""; @@ -1632,9 +1640,15 @@ export default class AccountViewView extends Vue { ); } } - const imageResp = await fetchImageRateLimits(this.axios, did); - if (imageResp.status === 200) { - this.imageLimits = imageResp.data; + try { + const imageResp = await fetchImageRateLimits(this.axios, did); + if (imageResp.status === 200) { + this.imageLimits = imageResp.data; + } else { + this.limitsMessage = "You don't have access to upload images."; + } + } catch { + this.limitsMessage = "You cannot upload images."; } } } catch (error) { @@ -1739,6 +1753,14 @@ export default class AccountViewView extends Vue { try { const headers = await getHeaders(this.activeDid); this.passkeyExpirationDescription = tokenExpiryTimeDescription(); + if ( + window.location.hostname === "localhost" && + !DEFAULT_IMAGE_API_SERVER.includes("localhost") + ) { + console.log( + "Using shared image API server, so only users on that server can play with images.", + ); + } const response = await this.axios.delete( DEFAULT_IMAGE_API_SERVER + "/image/" + diff --git a/src/views/DiscoverView.vue b/src/views/DiscoverView.vue index 09ef7e3b..6dd9214e 100644 --- a/src/views/DiscoverView.vue +++ b/src/views/DiscoverView.vue @@ -302,7 +302,10 @@ import InfiniteScroll from "../components/InfiniteScroll.vue"; import ProjectIcon from "../components/ProjectIcon.vue"; import OnboardingDialog from "../components/OnboardingDialog.vue"; import TopMessage from "../components/TopMessage.vue"; -import { NotificationIface, DEFAULT_PARTNER_API_SERVER } from "../constants/app"; +import { + NotificationIface, + DEFAULT_PARTNER_API_SERVER, +} from "../constants/app"; import { db, logConsoleAndDb, diff --git a/src/views/GiftedDetailsView.vue b/src/views/GiftedDetailsView.vue index 89e2ed3c..c26693a4 100644 --- a/src/views/GiftedDetailsView.vue +++ b/src/views/GiftedDetailsView.vue @@ -547,6 +547,14 @@ export default class GiftedDetails extends Vue { } try { const headers = await getHeaders(this.activeDid); + if ( + window.location.hostname === "localhost" && + !DEFAULT_IMAGE_API_SERVER.includes("localhost") + ) { + console.log( + "Using shared image API server, so only users on that server can play with images.", + ); + } const response = await this.axios.delete( DEFAULT_IMAGE_API_SERVER + "/image/" + diff --git a/src/views/NewEditProjectView.vue b/src/views/NewEditProjectView.vue index 71bcce54..8725fdc3 100644 --- a/src/views/NewEditProjectView.vue +++ b/src/views/NewEditProjectView.vue @@ -357,6 +357,14 @@ export default class NewEditProjectView extends Vue { } try { const headers = (await getHeaders(this.activeDid)) as AxiosRequestHeaders; + if ( + window.location.hostname === "localhost" && + !DEFAULT_IMAGE_API_SERVER.includes("localhost") + ) { + console.log( + "Using shared image API server, so only users on that server can play with images.", + ); + } const response = await this.axios.delete( DEFAULT_IMAGE_API_SERVER + "/image/" + diff --git a/src/views/SharedPhotoView.vue b/src/views/SharedPhotoView.vue index f70b6730..83f4df1e 100644 --- a/src/views/SharedPhotoView.vue +++ b/src/views/SharedPhotoView.vue @@ -183,6 +183,14 @@ export default class SharedPhotoView extends Vue { ); formData.append("claimType", imageType); + if ( + window.location.hostname === "localhost" && + !DEFAULT_IMAGE_API_SERVER.includes("localhost") + ) { + console.log( + "Using shared image API server, so only users on that server can play with images.", + ); + } const response = await axios.post( DEFAULT_IMAGE_API_SERVER + "/image", formData, diff --git a/test-playwright/35-record-gift-from-image-share.spec.ts b/test-playwright/35-record-gift-from-image-share.spec.ts index 135f9ff7..bdece669 100644 --- a/test-playwright/35-record-gift-from-image-share.spec.ts +++ b/test-playwright/35-record-gift-from-image-share.spec.ts @@ -50,6 +50,14 @@ import path from 'path'; import { test, expect } from '@playwright/test'; import { importUser } from './testUtils'; +/** + * Note: by default, this test uses the test image API server. + * + * If you want to use your own image API server, you can set the + * VITE_DEFAULT_IMAGE_API_SERVER environment variable to your server's URL + * in the playwright.config-local.ts file. + * + */ test('Record item given from image-share', async ({ page }) => { let randomString = Math.random().toString(36).substring(2, 8); diff --git a/test-playwright/40-add-contact.spec.ts b/test-playwright/40-add-contact.spec.ts index 72cb2cbe..7655ea12 100644 --- a/test-playwright/40-add-contact.spec.ts +++ b/test-playwright/40-add-contact.spec.ts @@ -300,7 +300,7 @@ test('Copy contact to clipboard, then import ', async ({ page, context }, testIn return; } - console.log("Running test that copies contact details to clipboard."); + // console.log("Running test that copies contact details to clipboard."); await page.getByTestId('copySelectedContactsButtonTop').click(); const clipboardText = await page.evaluate(async () => { return navigator.clipboard.readText(); diff --git a/vite.config.mjs b/vite.config.mjs index 3ceaa7ca..1005799a 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -33,12 +33,6 @@ export default defineConfig(({ mode }) => { fs: { strict: false }, - proxy: process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test' ? { - '/api': { - target: process.env.VITE_DEFAULT_ENDORSER_API_SERVER || 'http://localhost:3000', - changeOrigin: true, - }, - } : undefined, }, build: { outDir: isElectron ? "dist-electron" : "dist",