diff --git a/CHANGELOG.md b/CHANGELOG.md index c983e4bc..6b0c377b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,44 @@ 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). +== [0.4.2] - 2025.02.17 +### Merged +- Master to split_process_build +- fixed path issues +- all tests passing +- capacitor build to Android working + +## [0.4.1] - 2025.02.16 +### Fixed +- nostr build issue +- Linting + + +## [0.4.0] - 2025.02.14 +### Changed +- Images in the home feed now take up the full width of the card. +- Clicking the image previously, would open the image in a new tab. Now, clicking the image opens the image in a lightbox view. + +### Added +- Clicking an image also now displays an in-app lightbox view of the image. +- The lightbox view includes a download button for the image in mobile view. + + +## [0.3.57] - 2025.02.11 +### Added +- Automatic user creation in onboarding meetings + + +## [0.3.55] - 2025.02.07 +### Added +- End time for projects + + +## [0.3.54] - 2025.02.06 +### Added +- Group onboarding meetings + + ## [0.3.53] - 2025.01.30 ### Added - Hints for contacting the creator of a project diff --git a/README.md b/README.md index 211bb60d..0607f5c9 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,64 @@ npm install npm run dev ``` -See [BUILDING.md](BUILDING.md) for detailed build and setup instructions. +See the test locations for "IMAGE_API_SERVER" or "PARTNER_API_SERVER" below, or use http://localhost:3000 for local endorser.ch + +### Build the test & production app +``` +npm run serve +``` + +### Lint and fix files +``` +npm run lint +``` + +### Run all UI tests + +Look below for the "test-all" instructions. + + +### Compile and minify for test & production + +* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations. + +* `npx prettier --write ./sw_scripts/` + +* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`. + +* Commit everything (since the commit hash is used the app). + +* Put the commit hash in the changelog (which will help you remember to bump the version later). + +* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 0.3.55 && git push origin 0.3.55`. + +* For test, build the app (because test server is not yet set up to build): + +``` +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 npm run build +``` + + ... and transfer to the test server: `rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari` + + (Let's replace that with a .env.development or .env.staging file.) + + (Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.) + +* For prod, get on the server and run the correct build: + + ... and log onto the server: + + * `pkgx +npm sh` + + * `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 0.3.55 && npm install && npm run build && cd -` + + (The plain `npm run build` uses the .env.production file.) + +* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/` + +* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production. + + diff --git a/android/build.gradle b/android/build.gradle index 7cc86c23..499aca35 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.8.0' + classpath 'com.android.tools.build:gradle:8.8.1' classpath 'com.google.gms:google-services:4.4.0' // NOTE: Do not place your application dependencies here; they belong diff --git a/package-lock.json b/package-lock.json index 5173ae73..5d005bc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "TimeSafari", - "version": "0.3.54-beta", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "TimeSafari", - "version": "0.3.54-beta", + "version": "0.4.1", "dependencies": { "@capacitor/android": "^6.2.0", "@capacitor/cli": "^6.2.0", @@ -54,7 +54,7 @@ "lru-cache": "^10.2.0", "luxon": "^3.4.4", "merkletreejs": "^0.3.11", - "nostr-tools": "^2.7.2", + "nostr-tools": "^2.10.4", "notiwind": "^2.0.2", "papaparse": "^5.4.1", "pina": "^0.20.2204228", diff --git a/package.json b/package.json index 20c9d48f..34a73adf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "TimeSafari", - "version": "0.3.54-beta", + "name": "timesafari", + "version": "0.4.2", "description": "TimeSafari Desktop Application", "author": { "name": "TimeSafari Team" @@ -77,7 +77,7 @@ "lru-cache": "^10.2.0", "luxon": "^3.4.4", "merkletreejs": "^0.3.11", - "nostr-tools": "^2.7.2", + "nostr-tools": "^2.10.4", "notiwind": "^2.0.2", "papaparse": "^5.4.1", "pina": "^0.20.2204228", diff --git a/src/components/ChoiceButtonDialog.vue b/src/components/ChoiceButtonDialog.vue index 2e8118aa..5a7ce958 100644 --- a/src/components/ChoiceButtonDialog.vue +++ b/src/components/ChoiceButtonDialog.vue @@ -66,7 +66,7 @@ + + diff --git a/src/components/MembersList.vue b/src/components/MembersList.vue index 20f2c1bb..ea169cd4 100644 --- a/src/components/MembersList.vue +++ b/src/components/MembersList.vue @@ -1,40 +1,41 @@ diff --git a/src/views/NewEditProjectView.vue b/src/views/NewEditProjectView.vue index 8725fdc3..328d606e 100644 --- a/src/views/NewEditProjectView.vue +++ b/src/views/NewEditProjectView.vue @@ -71,17 +71,17 @@ -
+
If you want to be contacted, be sure to include your contact information -- just remember that this information is public and saved in a public history.
-
+
{{ fullClaim.description?.length }}/5000 max. characters
@@ -89,28 +89,55 @@ v-model="fullClaim.url" placeholder="Website" autocapitalize="none" - class="block w-full rounded border border-slate-400 mb-4 px-3 py-2" + class="block w-full rounded border border-slate-400 mt-4 px-3 py-2" /> -
- - - {{ zoneName }} +
+
+ Starts At + + +
+ +
+ + {{ zoneName }} time zone + +
+ +
+
+ Ends at +
+ + +
@@ -202,18 +229,10 @@ import "leaflet/dist/leaflet.css"; import { AxiosError, AxiosRequestHeaders } from "axios"; import { DateTime } from "luxon"; -import { hexToBytes } from "@noble/hashes/utils"; +import { finalizeEvent, serializeEvent } from "nostr-tools"; // these core imports could also be included as "import type ..." -import { - EventTemplate, - UnsignedEvent, - VerifiedEvent, -} from "nostr-tools/lib/types/core"; -import { - accountFromExtendedKey, - extendedKeysFromSeedWords, -} from "nostr-tools/nip06"; -import { finalizeEvent, serializeEvent } from "nostr-tools/pure"; +import { EventTemplate, UnsignedEvent, VerifiedEvent } from "nostr-tools/core"; +import * as nip06 from "nostr-tools/nip06"; import { Component, Vue } from "vue-facing-decorator"; import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; import { RouteLocationNormalizedLoaded, Router } from "vue-router"; @@ -251,6 +270,8 @@ export default class NewEditProjectView extends Vue { activeDid = ""; agentDid = ""; apiServer = ""; + endDateInput?: string; + endTimeInput?: string; errorMessage = ""; fullClaim: PlanVerifiableCredential = { "@context": "https://schema.org", @@ -325,6 +346,13 @@ export default class NewEditProjectView extends Vue { this.startDateInput = localDateTime.toFormat("yyyy-MM-dd"); this.startTimeInput = localDateTime.toFormat("HH:mm"); } + if (this.fullClaim.endTime) { + const localDateTime = DateTime.fromISO( + this.fullClaim.endTime as string, + ).toLocal(); + this.endDateInput = localDateTime.toFormat("yyyy-MM-dd"); + this.endTimeInput = localDateTime.toFormat("HH:mm"); + } } } catch (error) { console.error("Got error retrieving that project", error); @@ -468,7 +496,7 @@ export default class NewEditProjectView extends Vue { group: "alert", type: "danger", title: "Date Error", - text: "The date was invalid so it was not set.", + text: "The start date was invalid so it was not set.", }, 5000, ); @@ -476,6 +504,28 @@ export default class NewEditProjectView extends Vue { } else { delete vcClaim.startTime; } + if (this.endDateInput) { + try { + const endTimeFull = this.endTimeInput || "23:59:59"; + const fullTimeString = this.endDateInput + " " + endTimeFull; + // throw an error on an invalid date or time string + vcClaim.endTime = new Date(fullTimeString).toISOString(); // ensure timezone is part of it + } catch { + // it's not a valid date so erase it and tell the user + delete vcClaim.endTime; + this.$notify( + { + group: "alert", + type: "danger", + title: "Date Error", + text: "The end date was invalid so it was not set.", + }, + 5000, + ); + } + } else { + delete vcClaim.endTime; + } const vcJwt = await createEndorserJwtVcFromClaim(this.activeDid, vcClaim); // Make the xhr request payload @@ -618,15 +668,15 @@ export default class NewEditProjectView extends Vue { // remove any trailing ' const finalDerNumNoApostrophe = finalDerNum?.replace(/'/g, ""); const accountNum = Number(finalDerNumNoApostrophe || 0); - const extPubPri = extendedKeysFromSeedWords( + const extPubPri = nip06.extendedKeysFromSeedWords( account?.mnemonic as string, "", accountNum, ); const publicExtendedKey: string = extPubPri?.publicExtendedKey; const privateExtendedKey = extPubPri?.privateExtendedKey; - const privateKey = accountFromExtendedKey(privateExtendedKey).privateKey; - const privateBytes = hexToBytes(privateKey); + const privateBytes: Uint8Array = + nip06.accountFromExtendedKey(privateExtendedKey).privateKey; // No real content is necessary, we just want something signed, // so we might as well use nostr libs for nostr functions. // Besides: someday we may create real content that we can relay. @@ -660,7 +710,8 @@ export default class NewEditProjectView extends Vue { const endorserPartnerUrl = partnerServer + "/api/partner/link"; const timeSafariUrl = window.location.origin + "/claim/" + jwtId; const content = this.fullClaim.name + " - see " + timeSafariUrl; - const publicKeyHex = accountFromExtendedKey(publicExtendedKey).publicKey; + const publicKeyHex = + nip06.accountFromExtendedKey(publicExtendedKey).publicKey; const unsignedPayload: UnsignedEvent = { // why doesn't "...signedPayload" work? kind: signedPayload.kind, diff --git a/src/views/OnboardMeetingListView.vue b/src/views/OnboardMeetingListView.vue index 36a97c94..c95ccfc0 100644 --- a/src/views/OnboardMeetingListView.vue +++ b/src/views/OnboardMeetingListView.vue @@ -126,6 +126,7 @@ export default class OnboardMeetingListView extends Vue { attendingMeeting: Meeting | null = null; firstName = ""; isLoading = false; + isRegistered = false; meetings: Meeting[] = []; password = ""; selectedMeeting: Meeting | null = null; @@ -136,6 +137,7 @@ export default class OnboardMeetingListView extends Vue { this.activeDid = settings.activeDid || ""; this.apiServer = settings.apiServer || ""; this.firstName = settings.firstName || ""; + this.isRegistered = !!settings.isRegistered; await this.fetchMeetings(); } @@ -232,6 +234,7 @@ export default class OnboardMeetingListView extends Vue { const memberData = { name: this.firstName, did: this.activeDid, + isRegistered: this.isRegistered, }; const memberDataString = JSON.stringify(memberData); const encryptedMemberData = await encryptMessage( diff --git a/src/views/OnboardMeetingMembersView.vue b/src/views/OnboardMeetingMembersView.vue index 9bc588a6..4156e429 100644 --- a/src/views/OnboardMeetingMembersView.vue +++ b/src/views/OnboardMeetingMembersView.vue @@ -8,8 +8,16 @@ Meeting Members + +
+ +
+ -
+
{{ errorMessage }}
@@ -19,13 +27,14 @@
- + + +