Skip to content

Commit

Permalink
#2233 - Custom PWA installation in Home.vue (#2242)
Browse files Browse the repository at this point in the history
* create PWA-related sbp selectors

* add PWA promo banner to Home.vue

* custom pwa install logic in Home.vue

* design updates for PR feedbacks

* fix the linter err

* add a comment
  • Loading branch information
SebinSong committed Jul 25, 2024
1 parent dd39cca commit 37d9f4a
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 5 deletions.
27 changes: 27 additions & 0 deletions frontend/controller/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ import sbp from '@sbp/sbp'
import { PUBSUB_INSTANCE } from '@controller/instance-keys.js'
import { REQUEST_TYPE, PUSH_SERVER_ACTION_TYPE, PUBSUB_RECONNECTION_SUCCEEDED, createMessage } from '~/shared/pubsub.js'
import { HOURS_MILLIS } from '~/frontend/model/contracts/shared/time.js'
import { PWA_INSTALLABLE } from '@utils/events.js'

const pwa = {
deferredInstallPrompt: null,
installed: false
}

// How to provide your own in-app PWA install experience:
// https://web.dev/articles/customize-install

// PWA related event handlers
// NOTE: Apparently, 'beforeinstallprompt' is not fired either when a PWA from the browser is already installed.
window.addEventListener('beforeinstallprompt', e => {
// e.preventDefault() - uncomment this if we want to prevent the browser from displaying its own install UI.

pwa.deferredInstallPrompt = e
sbp('okTurtles.events/emit', PWA_INSTALLABLE)
})

sbp('sbp/selectors/register', {
'service-workers/setup': async function () {
Expand Down Expand Up @@ -198,6 +216,15 @@ sbp('sbp/selectors/register', {
console.error(`[sw] Failed to update the service-worker! - ${err.message}`)
}
}
},
'service-worker/check-pwa-installability': function () {
return Boolean(pwa.deferredInstallPrompt)
},
'service-worker/trigger-install-prompt': async function () {
if (!pwa.deferredInstallPrompt) { return false }

const result = await pwa.deferredInstallPrompt.prompt()
return result.outcome
}
})

Expand Down
2 changes: 2 additions & 0 deletions frontend/utils/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,5 @@ export const CHATROOM_USER_STOP_TYPING = 'chatroom-user-stop-typing'
export const NAMESPACE_REGISTRATION = 'namespace-registration'

export const KV_QUEUE = 'kv-queue'

export const PWA_INSTALLABLE = 'pwa-installable'
14 changes: 12 additions & 2 deletions frontend/views/components/banners/BannerSimple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default ({
default: 'info',
validator: function (value) {
// The value must match one of these strings
return ['success', 'info', 'warning', 'danger'].indexOf(value) !== -1
return ['success', 'info', 'warning', 'danger', 'general'].indexOf(value) !== -1
}
}
},
Expand All @@ -29,7 +29,8 @@ export default ({
warning: 'icon-exclamation-triangle c-icon',
danger: 'icon-times-circle c-icon',
info: 'icon-info-circle c-icon',
success: 'icon-check-circle c-icon'
success: 'icon-check-circle c-icon',
general: 'icon-info-circle c-icon'
}[this.severity]
}
}
Expand Down Expand Up @@ -61,6 +62,15 @@ export default ({
}
}
&.is-general {
background-color: $general_1;
color: $text_1;
.c-icon {
color: $text_1;
}
}
&.is-info {
background-color: $primary_2;
color: $primary_0;
Expand Down
10 changes: 10 additions & 0 deletions frontend/views/pages/DesignSystem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,16 @@ page(
| with a 
a.link(href='/') link.

tr
td
pre banner-simple(severity='general')
td
banner-simple(severity='general')
| This is a 
strong short message
| with a 
a.link(href='/') link.

h4.is-title-4 With title
table
thead
Expand Down
53 changes: 50 additions & 3 deletions frontend/views/pages/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@ main.c-splash(data-test='homeLogo' v-if='!currentGroupId')
@click='openModal("GroupJoinModal")'
data-test='joinGroup'
) Join a Group

banner-simple.hide-hoverable-device.hide-tablet.c-pwa-promo(
v-if='!isLoggedIn && ephemeral.showPwaPromo'
severity='general'
)
template(#header='')
i18n Install this web app on your device for better usability.

button-submit.is-success.is-small.c-install-btn(
type='button'
@click='onInstallClick'
) {{ L('Install') }}

//- TODO: conditionally show this depending on environment variable
//- footer.c-footer(v-if='!isLoggedIn')
//- banner-simple.c-demo-warning(severity='warning')
Expand All @@ -61,8 +74,9 @@ main.c-splash(data-test='homeLogo' v-if='!currentGroupId')
<script>
import sbp from '@sbp/sbp'
import { mapGetters, mapState } from 'vuex'
import { ACCEPTED_GROUP, OPEN_MODAL } from '@utils/events.js'
import { ACCEPTED_GROUP, OPEN_MODAL, PWA_INSTALLABLE } from '@utils/events.js'
import BannerSimple from '@components/banners/BannerSimple.vue'
import ButtonSubmit from '@components/ButtonSubmit.vue'
import SvgCreateGroup from '@svgs/create-group.svg'
import SvgJoinGroup from '@svgs/join-group.svg'
import { ignoreWhenNavigationCancelled } from '~/frontend/views/utils/misc.js'
Expand All @@ -72,7 +86,8 @@ export default ({
components: {
SvgJoinGroup,
SvgCreateGroup,
BannerSimple
BannerSimple,
ButtonSubmit
},
computed: {
...mapGetters([
Expand All @@ -93,12 +108,14 @@ export default ({
if (contractID !== this.currentGroupId) return
// For first time joins, force redirect to /pending-approval
this.ephemeral.ourProfileActive = false
}
},
showPwaPromo: false
}
}
},
beforeMount () {
sbp('okTurtles.events/on', ACCEPTED_GROUP, this.ephemeral.listener)
this.checkPwaInstallability()
},
mounted () {
this.ephemeral.ourProfileActive = this.ourProfileActive
Expand All @@ -125,6 +142,15 @@ export default ({
// (Related GH issue: https://github.com/okTurtles/group-income/issues/1830)
const path = this.$route.query.next ?? (this.ephemeral.ourProfileActive ? '/dashboard' : '/pending-approval')
this.$router.push({ path }).catch(e => ignoreWhenNavigationCancelled(e, path))
},
checkPwaInstallability () {
this.ephemeral.showPwaPromo = sbp('service-worker/check-pwa-installability')
sbp('okTurtles.events/once', PWA_INSTALLABLE, () => {
this.ephemeral.showPwaPromo = true
})
},
async onInstallClick () {
await sbp('service-worker/trigger-install-prompt')
}
},
watch: {
Expand Down Expand Up @@ -242,4 +268,25 @@ export default ({
.c-demo-warning {
text-align: left;
}
.c-pwa-promo {
position: fixed;
bottom: 1.875rem;
left: 50%;
transform: translateX(-50%);
z-index: 10; // should be less than modal, tooltip, prompt etc.
text-align: left;
width: calc(100vw - 3rem);
max-width: 31.25rem;
::v-deep .c-body {
margin-top: 1rem;
text-align: right;
}
}
.c-install-btn {
padding-left: 1.75rem;
padding-right: 1.75rem;
}
</style>

0 comments on commit 37d9f4a

Please sign in to comment.