Browse Source

Merge branch 'master' into move-id-switch

move-id-switch
Trent Larson 1 year ago
parent
commit
8d15b7bfb8
  1. 8
      project.task.yaml
  2. 2
      src/main.ts
  3. 2
      src/views/HomeView.vue
  4. 28
      src/views/ProjectViewView.vue
  5. 77
      web-push.md

8
project.task.yaml

@ -2,16 +2,12 @@
tasks: tasks:
- test alerts on all pages -- or refactor to new "notify" (since AlertMessage refactoring may require a change, et. ContactQRScanShowView) - test alerts on all pages -- or refactor to new "notify" (since AlertMessage refactoring may require a change, et. ContactQRScanShowView)
- .2 bug - on contacts view, click on "to" & "from" and nothing happens - .2 bug - on contacts view, click on "to" & "from" and nothing happens
- 01 add a location for a project via map pin :
- add with a "location" field containing this: { "geo":{ "@type":"GeoCoordinates", "latitude":40.883944, "longitude":-111.884787 } }
- 40 notifications : - 40 notifications :
- push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data assignee:matthew - push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data assignee:matthew
- 01 add a location for a project via map pin - 01 add my bounding box(es) of interest for searches on Nearby part of Discovery page
- 04 search by a bounding box for local projects (see API by clicking on "Nearby") - .5 search by a bounding box(s) of interest for local projects (see API by clicking on "Nearby")
- 01 Replace Gifted/Give in ContactsView with GiftedDialog assignee:matthew - 01 Replace Gifted/Give in ContactsView with GiftedDialog assignee:matthew
- 02 Fix images on projectview - allow choice of image from a pallete of images or a url image (discovery page display also)
- SEE: https://github.com/dmester/jdenticon assignee:jose
- 08 Scan QR code to import into contacts assignee:matthew - 08 Scan QR code to import into contacts assignee:matthew
- SEE: https://github.com/gruhn/vue-qrcode-reader - SEE: https://github.com/gruhn/vue-qrcode-reader

2
src/main.ts

@ -35,6 +35,7 @@ import {
faGift, faGift,
faHand, faHand,
faHouseChimney, faHouseChimney,
faLocationDot,
faLongArrowAltLeft, faLongArrowAltLeft,
faLongArrowAltRight, faLongArrowAltRight,
faMagnifyingGlass, faMagnifyingGlass,
@ -80,6 +81,7 @@ library.add(
faGift, faGift,
faHand, faHand,
faHouseChimney, faHouseChimney,
faLocationDot,
faLongArrowAltLeft, faLongArrowAltLeft,
faLongArrowAltRight, faLongArrowAltRight,
faMagnifyingGlass, faMagnifyingGlass,

2
src/views/HomeView.vue

@ -111,7 +111,7 @@
<div class="mb-8"> <div class="mb-8">
<h2 class="text-xl font-bold">Quick Action</h2> <h2 class="text-xl font-bold">Quick Action</h2>
<p class="mb-4">Show appreciation to a contact:</p> <p class="mb-4">Record a gift from a contact:</p>
<ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5"> <ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5">
<li @click="openDialog()"> <li @click="openDialog()">

28
src/views/ProjectViewView.vue

@ -39,6 +39,16 @@
<fa icon="calendar" class="fa-fw text-slate-400"></fa> <fa icon="calendar" class="fa-fw text-slate-400"></fa>
{{ timeSince }} {{ timeSince }}
</div> </div>
<div v-if="latitude || longitude">
<fa icon="location-dot" class="fa-fw text-slate-400"></fa>
<a
:href="getOpenStreetMapUrl()"
target="_blank"
class="underline"
>
Map View
</a>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -231,6 +241,8 @@ export default class ProjectViewView extends Vue {
expanded = false; expanded = false;
givesToThis: Array<GiveServerRecord> = []; givesToThis: Array<GiveServerRecord> = [];
givesByThis: Array<GiveServerRecord> = []; givesByThis: Array<GiveServerRecord> = [];
latitude = 0;
longitude = 0;
name = ""; name = "";
issuer = ""; issuer = "";
projectId = localStorage.getItem("projectId") || ""; // handle ID projectId = localStorage.getItem("projectId") || ""; // handle ID
@ -326,6 +338,8 @@ export default class ProjectViewView extends Vue {
this.name = resp.data.claim?.name || "(no name)"; this.name = resp.data.claim?.name || "(no name)";
this.description = resp.data.claim?.description || "(no description)"; this.description = resp.data.claim?.description || "(no description)";
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.longitude = resp.data.claim?.location?.geo?.longitude || 0;
} 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(
@ -441,6 +455,20 @@ export default class ProjectViewView extends Vue {
this.$refs.customDialog.open(contact); this.$refs.customDialog.open(contact);
} }
getOpenStreetMapUrl() {
// Google URL is https://maps.google.com/?q=LAT,LONG
return (
"https://www.openstreetmap.org/?mlat=" +
this.latitude +
"&mlon=" +
this.longitude +
"#map=15/" +
this.latitude +
"/" +
this.longitude
);
}
handleDialogResult(result) { handleDialogResult(result) {
if (result.action === "confirm") { if (result.action === "confirm") {
return new Promise((resolve) => { return new Promise((resolve) => {

77
web-push.md

@ -12,7 +12,7 @@ controlling the web browser's source code. Here named PROVIDER. An example of a
PROVIDER is FCM (Firebase Cloud Messaging) which is owned by Google. PROVIDER is FCM (Firebase Cloud Messaging) which is owned by Google.
3) The Web Application that a user is visiting from their web browser. Let's 3) The Web Application that a user is visiting from their web browser. Let's
call this the SERVICE (short for Web Push application service) call this the SERVICE (short for Web Push application service)
[4) A Custom Web Push Intermediary Service, either third party or self-hosted. 4) A Custom Web Push Intermediary Service, either third party or self-hosted.
Called INTERMEDIARY here. FCM also may fit in this category if the SERVICE Called INTERMEDIARY here. FCM also may fit in this category if the SERVICE
has an API key from FCM.] has an API key from FCM.]
@ -105,7 +105,7 @@ Here's a version which can be used for testing locally. Note there can be
caching issues in your browser! Incognito is highly recommended. caching issues in your browser! Incognito is highly recommended.
sw-dev.ts sw-dev.ts
```
self.addEventListener('push', function(event: PushEvent) { self.addEventListener('push', function(event: PushEvent) {
console.log('Received a push message', event); console.log('Received a push message', event);
@ -122,10 +122,11 @@ self.addEventListener('push', function(event: PushEvent) {
}) })
); );
}); });
```
vue.config.js vue.config.js
```
module.exports = { module.exports = {
pwa: { pwa: {
workboxOptions: { workboxOptions: {
@ -133,6 +134,7 @@ module.exports = {
} }
} }
} }
```
Once we have the service worker registered and the ServiceWorkerRegistration is Once we have the service worker registered and the ServiceWorkerRegistration is
returned, we then have access to a `pushManager` property object. This property returned, we then have access to a `pushManager` property object. This property
@ -184,6 +186,7 @@ PROVIDER which creates and stores a special URL for that BROWSER.
Here's a bit of code describing the above process: Here's a bit of code describing the above process:
```
// b64 is the VAPID // b64 is the VAPID
b64 = 'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U'; b64 = 'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U';
const applicationServerKey = urlBase64ToUint8Array(b64); const applicationServerKey = urlBase64ToUint8Array(b64);
@ -199,6 +202,7 @@ registration.pushManager.subscribe(options)
.catch(function(error) { .catch(function(error) {
console.error('Push subscription failed:', error); console.error('Push subscription failed:', error);
}); });
```
In this example, the `applicationServerKey` variable contains the VAPID public In this example, the `applicationServerKey` variable contains the VAPID public
key, which is converted to a `Uint8Array` using a function such as this: key, which is converted to a `Uint8Array` using a function such as this:
@ -221,6 +225,16 @@ export function toUint8Array(base64String: string, atobFn: typeof atob): Uint8Ar
The options object is of type `PushSubscriptionOptions`, which includes the The options object is of type `PushSubscriptionOptions`, which includes the
`userVisibleOnly` and `applicationServerKey` (ie VAPID public key) properties. `userVisibleOnly` and `applicationServerKey` (ie VAPID public key) properties.
options: An object that contains the options used for creating the
subscription. This object itself has the following sub-properties:
applicationServerKey: A public key your push service uses for application
server identification. This is normally a Uint8Array.
userVisibleOnly: A boolean value indicating that the push messages that
are sent should be made visible to the user through a notification.
This is often set to true.
The subscribe() method returns a `Promise` that resolves to a `PushSubscription` The subscribe() method returns a `Promise` that resolves to a `PushSubscription`
object containing details of the subscription, such as the endpoint URL and the object containing details of the subscription, such as the endpoint URL and the
public key. The returned data would have a form like this: public key. The returned data would have a form like this:
@ -242,16 +256,6 @@ public key. The returned data would have a form like this:
representing the subscription's expiration time in milliseconds since representing the subscription's expiration time in milliseconds since
01 January, 1970 UTC. This can be null if the subscription never expires. 01 January, 1970 UTC. This can be null if the subscription never expires.
options: An object that contains the options used for creating the
subscription. This object itself has the following sub-properties:
applicationServerKey: A public key your push service uses for application
server identification. This is normally a Uint8Array.
userVisibleOnly: A boolean value indicating that the push messages that
are sent should be made visible to the user through a notification.
This is often set to true.
The BROWSER will, internally, then use that URL to check for incoming messages The BROWSER will, internally, then use that URL to check for incoming messages
by way of the service worker we described earlier. The BROWSER also sends this by way of the service worker we described earlier. The BROWSER also sends this
URL back to SERVICE which will use that URL to send messages to the BROWSER via URL back to SERVICE which will use that URL to send messages to the BROWSER via
@ -266,6 +270,7 @@ via the PROVIDER so that they reach the BROWSER service worker.
Just to remind us that in our service worker our code for receiving messages Just to remind us that in our service worker our code for receiving messages
will look something like this: will look something like this:
```
self.addEventListener('push', function(event: PushEvent) { self.addEventListener('push', function(event: PushEvent) {
console.log('Received a push message', event); console.log('Received a push message', event);
@ -282,7 +287,7 @@ self.addEventListener('push', function(event: PushEvent) {
}) })
); );
}); });
```
Now to address the issue of receiving notification messages on mobile devices. Now to address the issue of receiving notification messages on mobile devices.
@ -309,3 +314,47 @@ OneSignal) can perform in the role of such proxies.
#4 -The INTERMEDIARY- doesn't appear to be anything we should be spending our #4 -The INTERMEDIARY- doesn't appear to be anything we should be spending our
time on. time on.
A BROWSER may also remove a subscription. In order to remove a subscription,
the registration record must be retrieved from the serviceWorker using
`navigator.serviceWorker.ready`. Within the `ready` property is the
`pushManager` which has a `getSubscription` method. Once you have the
subscription object, you may call the `unsubscribe` method. `unsubscribe` is
asynchronnous and returns a boolean true if it is successful in removing the
subscription and false if not.
```
async function unsubscribeFromPush() {
// Check if the browser supports service workers
if ("serviceWorker" in navigator) {
// Get the registration object for the service worker
const registration = await navigator.serviceWorker.ready;
// Get the existing subscription
const subscription = await registration.pushManager.getSubscription();
if (subscription) {
// Unsubscribe
const successful = await subscription.unsubscribe();
if (successful) {
console.log("Successfully unsubscribed from push notifications.");
// You can also inform your server to remove this subscription
} else {
console.log("Failed to unsubscribe from push notifications.");
}
} else {
console.log("No subscription was found.");
}
} else {
console.log("Service workers are not supported by this browser.");
}
}
// Unsubscribe from push notifications
unsubscribeFromPush().catch((err) => {
console.error("An error occurred while unsubscribing from push notifications", err);
});
```
NOTE: We could offer an option within the app to "mute" these notifications. This wouldn't turn off the notifications at the browser level, but you could make it so that your Service Worker doesn't display them even if it receives them.

Loading…
Cancel
Save