forked from trent_larson/crowd-funder-for-time-pwa
add URL for plans
This commit is contained in:
@@ -11,10 +11,10 @@ tasks:
|
|||||||
|
|
||||||
- .5 Add infinite scroll to gifts on the home page
|
- .5 Add infinite scroll to gifts on the home page
|
||||||
|
|
||||||
- Discuss whether the remaining tasks are worthwhile before MVP release.
|
|
||||||
|
|
||||||
- .5 If notifications are not enabled, add message to front page with link/button to enable
|
- .5 If notifications are not enabled, add message to front page with link/button to enable
|
||||||
|
|
||||||
|
- Discuss whether the remaining tasks are worthwhile before MVP release.
|
||||||
|
|
||||||
- .5 make a VC details page, or link to endorser.ch (including confirmations)
|
- .5 make a VC details page, or link to endorser.ch (including confirmations)
|
||||||
- .3 Add URL for project
|
- .3 Add URL for project
|
||||||
- .5 Add start date to project
|
- .5 Add start date to project
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ export interface OfferServerRecord {
|
|||||||
validThrough: string;
|
validThrough: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that previous VCs may have additional fields.
|
||||||
|
// https://endorser.ch/doc/html/transactions.html#id4
|
||||||
export interface GiveVerifiableCredential {
|
export interface GiveVerifiableCredential {
|
||||||
"@context"?: string; // optional when embedded, eg. in an Agree
|
"@context"?: string; // optional when embedded, eg. in an Agree
|
||||||
"@type": "GiveAction";
|
"@type": "GiveAction";
|
||||||
@@ -80,6 +82,8 @@ export interface GiveVerifiableCredential {
|
|||||||
recipient?: { identifier: string };
|
recipient?: { identifier: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that previous VCs may have additional fields.
|
||||||
|
// https://endorser.ch/doc/html/transactions.html#id8
|
||||||
export interface OfferVerifiableCredential {
|
export interface OfferVerifiableCredential {
|
||||||
"@context"?: string; // optional when embedded, eg. in an Agree
|
"@context"?: string; // optional when embedded, eg. in an Agree
|
||||||
"@type": "Offer";
|
"@type": "Offer";
|
||||||
@@ -93,6 +97,8 @@ export interface OfferVerifiableCredential {
|
|||||||
validThrough?: string;
|
validThrough?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that previous VCs may have additional fields.
|
||||||
|
// https://endorser.ch/doc/html/transactions.html#id7
|
||||||
export interface PlanVerifiableCredential {
|
export interface PlanVerifiableCredential {
|
||||||
"@context": "https://schema.org";
|
"@context": "https://schema.org";
|
||||||
"@type": "PlanAction";
|
"@type": "PlanAction";
|
||||||
|
|||||||
3
src/libs/util.ts
Normal file
3
src/libs/util.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const isGlobalUri = (uri: string) => {
|
||||||
|
return uri && uri.match(new RegExp(/^[A-Za-z][A-Za-z0-9+.-]+:/));
|
||||||
|
};
|
||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
faFloppyDisk,
|
faFloppyDisk,
|
||||||
faFolderOpen,
|
faFolderOpen,
|
||||||
faGift,
|
faGift,
|
||||||
|
faGlobe,
|
||||||
faHand,
|
faHand,
|
||||||
faHouseChimney,
|
faHouseChimney,
|
||||||
faLocationDot,
|
faLocationDot,
|
||||||
@@ -86,6 +87,7 @@ library.add(
|
|||||||
faFloppyDisk,
|
faFloppyDisk,
|
||||||
faFolderOpen,
|
faFolderOpen,
|
||||||
faGift,
|
faGift,
|
||||||
|
faGlobe,
|
||||||
faHand,
|
faHand,
|
||||||
faHouseChimney,
|
faHouseChimney,
|
||||||
faLocationDot,
|
faLocationDot,
|
||||||
|
|||||||
@@ -40,6 +40,12 @@
|
|||||||
{{ fullClaim.description.length }}/5000 max. characters
|
{{ fullClaim.description.length }}/5000 max. characters
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
v-model="fullClaim.url"
|
||||||
|
placeholder="Website"
|
||||||
|
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="flex items-center mb-4">
|
<div class="flex items-center mb-4">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -142,7 +148,7 @@ export default class NewEditProjectView extends Vue {
|
|||||||
"@type": "PlanAction",
|
"@type": "PlanAction",
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
};
|
}; // this default is only to avoid errors before plan is loaded
|
||||||
includeLocation = false;
|
includeLocation = false;
|
||||||
latitude = 0;
|
latitude = 0;
|
||||||
longitude = 0;
|
longitude = 0;
|
||||||
|
|||||||
@@ -45,8 +45,13 @@
|
|||||||
:href="getOpenStreetMapUrl()"
|
:href="getOpenStreetMapUrl()"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="underline"
|
class="underline"
|
||||||
>
|
>Map View
|
||||||
Map View
|
</a>
|
||||||
|
</div>
|
||||||
|
<div v-if="url">
|
||||||
|
<fa icon="globe" class="fa-fw text-slate-400"></fa>
|
||||||
|
<a :href="addScheme(url)" target="_blank" class="underline"
|
||||||
|
>{{ domainForWebsite(this.url) }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,6 +277,7 @@ import { accountsDB, db } from "@/db/index";
|
|||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
|
||||||
import { accessToken } from "@/libs/crypto";
|
import { accessToken } from "@/libs/crypto";
|
||||||
|
import { isGlobalUri } from "@/libs/util";
|
||||||
import {
|
import {
|
||||||
didInfo,
|
didInfo,
|
||||||
GiverInputInfo,
|
GiverInputInfo,
|
||||||
@@ -314,6 +320,7 @@ export default class ProjectViewView extends Vue {
|
|||||||
timeSince = "";
|
timeSince = "";
|
||||||
truncatedDesc = "";
|
truncatedDesc = "";
|
||||||
truncateLength = 40;
|
truncateLength = 40;
|
||||||
|
url = "";
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
await db.open();
|
await db.open();
|
||||||
@@ -415,6 +422,7 @@ export default class ProjectViewView extends Vue {
|
|||||||
this.truncatedDesc = this.description.slice(0, this.truncateLength);
|
this.truncatedDesc = this.description.slice(0, this.truncateLength);
|
||||||
this.latitude = resp.data.claim?.location?.geo?.latitude || 0;
|
this.latitude = resp.data.claim?.location?.geo?.latitude || 0;
|
||||||
this.longitude = resp.data.claim?.location?.geo?.longitude || 0;
|
this.longitude = resp.data.claim?.location?.geo?.longitude || 0;
|
||||||
|
this.url = resp.data.claim?.url || "";
|
||||||
} else if (resp.status === 404) {
|
} else if (resp.status === 404) {
|
||||||
// actually, axios throws an error so we never get here
|
// actually, axios throws an error so we never get here
|
||||||
this.$notify(
|
this.$notify(
|
||||||
@@ -651,5 +659,34 @@ export default class ProjectViewView extends Vue {
|
|||||||
iconForUnitCode(unitCode: string) {
|
iconForUnitCode(unitCode: string) {
|
||||||
return this.UNIT_CODES[unitCode]?.faIcon || "question";
|
return this.UNIT_CODES[unitCode]?.faIcon || "question";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return an HTTPS URL if it's not a global URL
|
||||||
|
addScheme(url: string) {
|
||||||
|
if (!isGlobalUri(url)) {
|
||||||
|
return "https://" + url;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return just the domain for display, if possible
|
||||||
|
domainForWebsite(url: string) {
|
||||||
|
try {
|
||||||
|
const hostname = new URL(url).hostname;
|
||||||
|
console.log("hostname", hostname);
|
||||||
|
if (!hostname) {
|
||||||
|
// happens for non-http URLs
|
||||||
|
return url;
|
||||||
|
} else if (url.endsWith(hostname)) {
|
||||||
|
// it's just the domain
|
||||||
|
return hostname;
|
||||||
|
} else {
|
||||||
|
// there's more, but don't bother displaying the whole thing
|
||||||
|
return hostname + "...";
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
// must not be a valid URL
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user