From 8b90aa0775e3b605bc1a0e3d28d4b3e03d82f600 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 18 Jun 2021 18:19:04 +0200 Subject: [PATCH 1/9] Add a snackbar message to allow the user to trigger service-worker refresh Signed-off-by: Thomas Citharel --- js/src/App.vue | 55 +++++++++++++++++++++++++++++++-- js/src/i18n/en_US.json | 8 ++++- js/src/i18n/fr_FR.json | 8 ++++- js/src/registerServiceWorker.ts | 19 ++++++------ js/src/service-worker.ts | 14 +++++++++ 5 files changed, 91 insertions(+), 13 deletions(-) diff --git a/js/src/App.vue b/js/src/App.vue index 218a48cdc..f0ef0bfe6 100644 --- a/js/src/App.vue +++ b/js/src/App.vue @@ -117,11 +117,38 @@ export default class App extends Vue { window.addEventListener("offline", () => { this.online = false; this.showOfflineNetworkWarning(); - console.log("offline"); + console.debug("offline"); }); window.addEventListener("online", () => { this.online = true; - console.log("online"); + console.debug("online"); + }); + document.addEventListener("refreshApp", (event: Event) => { + this.$buefy.snackbar.open({ + queue: false, + indefinite: true, + type: "is-primary", + actionText: this.$t("Update app") as string, + cancelText: this.$t("Ignore") as string, + message: this.$t("A new version is available.") as string, + onAction: async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const detail = event.detail; + const registration = detail as ServiceWorkerRegistration; + try { + await this.refreshApp(registration); + window.location.reload(); + } catch (err) { + console.error(err); + this.$notifier.error( + this.$t( + "An error has occured while refreshing the page." + ) as string + ); + } + }, + }); }); this.interval = setInterval(async () => { @@ -138,6 +165,30 @@ export default class App extends Vue { }, 60000); } + private async refreshApp( + registration: ServiceWorkerRegistration + ): Promise { + const worker = registration.waiting; + if (!worker) { + return Promise.resolve(); + } + console.debug("Doing worker.skipWaiting()."); + return new Promise((resolve, reject) => { + const channel = new MessageChannel(); + + channel.port1.onmessage = (event) => { + console.debug("Done worker.skipWaiting()."); + if (event.data.error) { + reject(event.data); + } else { + resolve(event.data); + } + }; + console.debug("calling skip waiting"); + worker?.postMessage({ type: "skip-waiting" }, [channel.port2]); + }); + } + showOfflineNetworkWarning(): void { this.$notifier.error(this.$t("You are offline") as string); } diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index 3cf0514d1..787d95548 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -1047,5 +1047,11 @@ "Share this group": "Share this group", "This group is accessible only through it's link. Be careful where you post this link.": "This group is accessible only through it's link. Be careful where you post this link.", "{count} members": "No members|One member|{count} members", - "Share": "Share" + "Share": "Share", + "Update app": "Update app", + "Ignore": "Ignore", + "A new version is available.": "A new version is available.", + "An error has occured while refreshing the page.": "An error has occured while refreshing the page.", + "Join group {group}": "Join group {group}", + "Public preview": "Public preview" } diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index 2215a30c7..f5b8257f4 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -1138,5 +1138,11 @@ "Share this group": "Partager ce groupe", "This group is accessible only through it's link. Be careful where you post this link.": "Ce groupe est accessible uniquement à travers son lien. Faites attention où vous le diffusez.", "{count} members": "Aucun membre|Un⋅e membre|{count} membres", - "Share": "Partager" + "Share": "Partager", + "Update app": "Mettre à jour", + "Ignore": "Ignorer", + "A new version is available.": "Une nouvelle version est disponible.", + "An error has occured while refreshing the page.": "Une erreur est survenue lors du rafraîchissement de la page.", + "Join group {group}": "Rejoindre le groupe {group}", + "Public preview": "Aperçu public" } diff --git a/js/src/registerServiceWorker.ts b/js/src/registerServiceWorker.ts index 141c1daa4..ee0de1d9b 100644 --- a/js/src/registerServiceWorker.ts +++ b/js/src/registerServiceWorker.ts @@ -5,25 +5,27 @@ import { register } from "register-service-worker"; if ("serviceWorker" in navigator && isProduction()) { register(`${process.env.BASE_URL}service-worker.js`, { ready() { - console.log( + console.debug( "App is being served from cache by a service worker.\n" + "For more details, visit https://goo.gl/AFskqB" ); }, registered() { - console.log("Service worker has been registered."); + console.debug("Service worker has been registered."); }, cached() { - console.log("Content has been cached for offline use."); + console.debug("Content has been cached for offline use."); }, updatefound() { - console.log("New content is downloading."); + console.debug("New content is downloading."); }, - updated() { - console.log("New content is available; please refresh."); + updated(registration: ServiceWorkerRegistration) { + const event = new CustomEvent("refreshApp", { detail: registration }); + document.dispatchEvent(event); + console.debug("New content is available; please refresh."); }, offline() { - console.log( + console.debug( "No internet connection found. App is running in offline mode." ); }, @@ -34,6 +36,5 @@ if ("serviceWorker" in navigator && isProduction()) { } function isProduction(): boolean { - return true; - // return process.env.NODE_ENV === "production"; + return process.env.NODE_ENV === "production"; } diff --git a/js/src/service-worker.ts b/js/src/service-worker.ts index 7ff855658..e87c5cce7 100644 --- a/js/src/service-worker.ts +++ b/js/src/service-worker.ts @@ -124,3 +124,17 @@ self.addEventListener("notificationclick", function (event: NotificationEvent) { })() ); }); + +self.addEventListener("message", (event: ExtendableMessageEvent) => { + const replyPort = event.ports[0]; + const message = event.data; + if (replyPort && message && message.type === "skip-waiting") { + console.log("doing skip waiting"); + event.waitUntil( + self.skipWaiting().then( + () => replyPort.postMessage({ error: null }), + (error) => replyPort.postMessage({ error }) + ) + ); + } +}); From 637c7055c716a1eecd9908767cce1fb0d696190c Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 18 Jun 2021 18:41:09 +0200 Subject: [PATCH 2/9] Add fonts to service-worker workbox cache Signed-off-by: Thomas Citharel --- js/src/service-worker.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/service-worker.ts b/js/src/service-worker.ts index e87c5cce7..ee8ae6179 100644 --- a/js/src/service-worker.ts +++ b/js/src/service-worker.ts @@ -42,10 +42,11 @@ registerRoute( // Cache CSS, JS, and Web Worker requests with a Stale While Revalidate strategy registerRoute( - // Check to see if the request's destination is style for stylesheets, script for JavaScript, or worker for web worker + // Check to see if the request's destination is style for stylesheets, script for JavaScript, font, or worker for web worker ({ request }) => request.destination === "style" || request.destination === "script" || + request.destination === "font" || request.destination === "worker", // Use a Stale While Revalidate caching strategy new StaleWhileRevalidate({ From aed3f74be14e8c22a7514dfe2106f5bfcb5da2fd Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 18 Jun 2021 18:41:34 +0200 Subject: [PATCH 3/9] Don't show notification if the client is already focused Signed-off-by: Thomas Citharel --- js/src/service-worker.ts | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/js/src/service-worker.ts b/js/src/service-worker.ts index ee8ae6179..a3378a824 100644 --- a/js/src/service-worker.ts +++ b/js/src/service-worker.ts @@ -83,6 +83,24 @@ registerRoute( }) ); +async function isClientFocused(): Promise { + const windowClients = await self.clients.matchAll({ + type: "window", + includeUncontrolled: true, + }); + + let clientIsFocused = false; + for (let i = 0; i < windowClients.length; i++) { + const windowClient = windowClients[i] as WindowClient; + if (windowClient.focused) { + clientIsFocused = true; + break; + } + } + + return clientIsFocused; +} + self.addEventListener("push", async (event: PushEvent) => { if (!event.data) return; const payload = event.data.json(); @@ -99,7 +117,15 @@ self.addEventListener("push", async (event: PushEvent) => { }, }; - event.waitUntil(self.registration.showNotification(payload.title, options)); + event.waitUntil( + (async () => { + if (await isClientFocused()) { + // No need to show a notification, client already focused + return; + } + self.registration.showNotification(payload.title, options); + })() + ); }); self.addEventListener("notificationclick", function (event: NotificationEvent) { From 4e1d49693f0a98ed6788d409caaf0caab443144f Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 18 Jun 2021 18:41:53 +0200 Subject: [PATCH 4/9] Include uncontrolled clients into clients to focus Signed-off-by: Thomas Citharel --- js/src/service-worker.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/service-worker.ts b/js/src/service-worker.ts index a3378a824..af230659d 100644 --- a/js/src/service-worker.ts +++ b/js/src/service-worker.ts @@ -138,6 +138,7 @@ self.addEventListener("notificationclick", function (event: NotificationEvent) { (async () => { const clientList = await self.clients.matchAll({ type: "window", + includeUncontrolled: true, }); for (let i = 0; i < clientList.length; i++) { const client = clientList[i] as WindowClient; From dbd1e6fe2cad04a4625486b07c7b242a7f52fac2 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 18 Jun 2021 18:42:54 +0200 Subject: [PATCH 5/9] Don't show 401 graphQL errors Signed-off-by: Thomas Citharel --- js/src/vue-apollo.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/js/src/vue-apollo.ts b/js/src/vue-apollo.ts index b1dd2e8de..6ae0ee0a6 100644 --- a/js/src/vue-apollo.ts +++ b/js/src/vue-apollo.ts @@ -26,6 +26,7 @@ import { typePolicies, refreshAccessToken, } from "./apollo/utils"; +import { GraphQLError } from "graphql"; // Install the vue plugin Vue.use(VueApollo); @@ -120,10 +121,14 @@ const errorLink = onError( } if (graphQLErrors) { - graphQLErrors.map(({ message, locations, path }) => - console.log( - `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` - ) + graphQLErrors.map( + (graphQLError: GraphQLError & { status_code?: number }) => { + if (graphQLError?.status_code !== 401) { + console.log( + `[GraphQL error]: Message: ${graphQLError.message}, Location: ${graphQLError.locations}, Path: ${graphQLError.path}` + ); + } + } ); } From 62e73e2e6c6da93ae77fa5a7eb62911da20ba176 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 18 Jun 2021 19:25:47 +0200 Subject: [PATCH 6/9] Add width/height to avatar mini image Signed-off-by: Thomas Citharel --- js/src/components/Event/EventListCard.vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/js/src/components/Event/EventListCard.vue b/js/src/components/Event/EventListCard.vue index 7d3dc515e..71f40dbc6 100644 --- a/js/src/components/Event/EventListCard.vue +++ b/js/src/components/Event/EventListCard.vue @@ -2,7 +2,13 @@
- +
{{ displayNameAndUsername(participation.actor) }}
From d570f4438457cf652633f91cccd9f75f106d9dee Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 18 Jun 2021 19:28:59 +0200 Subject: [PATCH 7/9] Remove mad margin on .title Signed-off-by: Thomas Citharel --- js/src/components/Event/RecentEventCardWrapper.vue | 2 +- js/src/variables.scss | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/js/src/components/Event/RecentEventCardWrapper.vue b/js/src/components/Event/RecentEventCardWrapper.vue index bee20a990..a2d2c6e1d 100644 --- a/js/src/components/Event/RecentEventCardWrapper.vue +++ b/js/src/components/Event/RecentEventCardWrapper.vue @@ -30,6 +30,6 @@ export default class RecentEventCardWrapper extends Vue { diff --git a/js/src/variables.scss b/js/src/variables.scss index 18f916fc2..02942a8c1 100644 --- a/js/src/variables.scss +++ b/js/src/variables.scss @@ -128,10 +128,6 @@ $subtitle-size: 32px; $subtitle-sub-size: 30px; $subtitle-sup-size: 15px; -.title { - margin: 30px auto 45px; -} - .subtitle { background: $secondary; display: inline; From 762f917ff784ef7b7f67088736f0acaf580d6dcb Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 18 Jun 2021 19:36:50 +0200 Subject: [PATCH 8/9] Homepage improvements Signed-off-by: Thomas Citharel --- js/src/i18n/en_US.json | 4 +-- js/src/i18n/fr_FR.json | 4 +-- js/src/views/Home.vue | 68 +++++++++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index 787d95548..b370a2fa0 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -857,7 +857,6 @@ "Your city or region and the radius will only be used to suggest you events nearby. The event radius will consider the administrative center of the area.": "Your city or region and the radius will only be used to suggest you events nearby. The event radius will consider the administrative center of the area.", "Your upcoming events": "Your upcoming events", "Last published events": "Last published events", - "On {instance}": "On {instance}", "Events nearby": "Events nearby", "Within {number} kilometers of {place}": "|Within one kilometer of {place}|Within {number} kilometers of {place}", "@{username}": "@{username}", @@ -1053,5 +1052,6 @@ "A new version is available.": "A new version is available.", "An error has occured while refreshing the page.": "An error has occured while refreshing the page.", "Join group {group}": "Join group {group}", - "Public preview": "Public preview" + "Public preview": "Public preview", + "On {instance} and other federated instances": "On {instance} and other federated instances" } diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index f5b8257f4..f9f94422c 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -561,7 +561,6 @@ "On {date} ending at {endTime}": "Le {date}, se terminant à {endTime}", "On {date} from {startTime} to {endTime}": "Le {date} de {startTime} à {endTime}", "On {date} starting at {startTime}": "Le {date} à partir de {startTime}", - "On {instance}": "Sur {instance}", "Ongoing tasks": "Tâches en cours", "Only accessible through link": "Accessible uniquement par le lien", "Only accessible through link (private)": "Uniquement accessible par lien (privé)", @@ -1144,5 +1143,6 @@ "A new version is available.": "Une nouvelle version est disponible.", "An error has occured while refreshing the page.": "Une erreur est survenue lors du rafraîchissement de la page.", "Join group {group}": "Rejoindre le groupe {group}", - "Public preview": "Aperçu public" + "Public preview": "Aperçu public", + "On {instance} and other federated instances": "Sur {instance} et d'autres instances fédérées" } diff --git a/js/src/views/Home.vue b/js/src/views/Home.vue index 38337dd88..47a7aaf6d 100644 --- a/js/src/views/Home.vue +++ b/js/src/views/Home.vue @@ -52,11 +52,13 @@ v-if="config && (!currentUser.id || !currentActor.id)" >
-

+

{{ $t("Last published events") }}

- {{ $t("On {instance}", { instance: config.name }) }} + + {{ config.name }} +

@@ -176,7 +178,7 @@ class="container section" v-if="config && loggedUser && loggedUser.settings" > -
+
{{ $t("Welcome back {username}!", { username: currentActor.displayName(), @@ -189,34 +191,33 @@ }}
-
-

{{ $t("Your upcoming events") }}

+
+

{{ $t("Your upcoming events") }}

- - - {{ + {{ $tc("You have one event today.", row[1].length, { count: row[1].length, }) - }} - {{ + }} + {{ $tc("You have one event tomorrow.", row[1].length, { count: row[1].length, }) - }} - + }} + {{ $tc("You have one event in {days} days.", row[1].length, { count: row[1].length, days: calculateDiffDays(row[0]), }) }} - - + +

+
-

{{ $t("Last week") }}

+

{{ $t("Last week") }}

+
-

+

{{ $t("Events nearby") }}

@@ -289,11 +298,13 @@ " />

-

+

{{ $t("Last published events") }}

- {{ $t("On {instance}", { instance: config.name }) }} + + {{ config.name }} +

@@ -629,20 +640,16 @@ main > div > .container { .date-component-container { display: flex; align-items: center; - margin: 1.5rem auto; + margin: 0.5rem auto 1rem; h3.subtitle { margin-left: 7px; } } -section.container { - margin: auto auto 3rem; -} - span.view-all { display: block; - margin-top: 2rem; + margin-top: 1rem; text-align: right; a { @@ -688,8 +695,8 @@ section.hero { } #recent_events { - padding: 1rem 0; - min-height: calc(100vh - 400px); + padding: 0; + min-height: 20vh; z-index: 10; .title { @@ -697,7 +704,7 @@ section.hero { } .columns { - margin: 0rem auto 3rem; + margin: 0 auto; } } @@ -761,4 +768,11 @@ section.hero { .clickable { cursor: pointer; } + +.title { + font-size: 27px; + &:not(:last-child) { + margin-bottom: 0.5rem; + } +} From 4aaf92803a39ee42a327c80fb9b6b20ce0a10b13 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 18 Jun 2021 20:18:00 +0200 Subject: [PATCH 9/9] Upgrade deps - Remove `@types/vuedraggable` (already in vuedraggable) - Upgrade Tiptap way of using StarterKit Signed-off-by: Thomas Citharel --- js/package.json | 1 - js/src/components/Editor.vue | 4 +- js/yarn.lock | 117 ++++++++++++++++------------------- 3 files changed, 57 insertions(+), 65 deletions(-) diff --git a/js/package.json b/js/package.json index cafffcbce..b9f97cf80 100644 --- a/js/package.json +++ b/js/package.json @@ -70,7 +70,6 @@ "@types/prosemirror-model": "^1.7.2", "@types/prosemirror-state": "^1.2.4", "@types/prosemirror-view": "^1.11.4", - "@types/vuedraggable": "^2.23.0", "@typescript-eslint/eslint-plugin": "^4.18.0", "@typescript-eslint/parser": "^4.18.0", "@vue/cli-plugin-babel": "~5.0.0-beta.2", diff --git a/js/src/components/Editor.vue b/js/src/components/Editor.vue index f83b9adc7..f0b5a0eac 100644 --- a/js/src/components/Editor.vue +++ b/js/src/components/Editor.vue @@ -179,7 +179,7 @@