Skip to content

Commit 2cef477

Browse files
committed
✨ Basic push notification feature
Subscribe to individual launches, locations, rockets & launch service providers. User will recieve notification reminder when launch is < T-10 minutes
1 parent 361530d commit 2cef477

19 files changed

+450
-32
lines changed

.env.development

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
VUE_APP_API_URL=
2+
VUE_APP_API_VAPID_PUBLIC_KEY=
23
BASE_URL=

.env.production

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
VUE_APP_API_URL=
2+
VUE_APP_API_VAPID_PUBLIC_KEY=
23
BASE_URL=

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ cp .env.production .env.production.local
3333
### Example environment
3434
```env
3535
VUE_APP_API_URL=https://api.example.app
36+
VUE_APP_API_VAPID_PUBLIC_KEY=BIMSlo01XNTmTFT0rcrMluhMl0Vbls-t6DAXtbRBiFW_z_n9kF9KlxsEiyujlAEZhDDEJsxMlMxT86_OXxKNQeU
3637
BASE_URL=https://example.app
3738
````
3839

package-lock.json

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
"scripts": {
88
"serve": "vue-cli-service serve",
99
"build": "vue-cli-service build",
10+
"build:watch": "vue-cli-service build --watch --mode production",
1011
"build:report": "vue-cli-service build --report",
1112
"lint": "vue-cli-service lint"
1213
},
1314
"dependencies": {
1415
"@fortawesome/fontawesome-svg-core": "^1.2.15",
16+
"@fortawesome/free-regular-svg-icons": "^5.13.1",
1517
"@fortawesome/free-solid-svg-icons": "^5.7.2",
1618
"@fortawesome/vue-fontawesome": "^0.1.5",
1719
"axios": "^0.19.0",

src/App.vue

+4-2
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@
1414
</template>
1515

1616
<script>
17+
import { mapState } from 'vuex'
18+
import notificationService from './services/notificationService'
1719
import TheNavbar from './components/TheNavbar.vue'
1820
import TheFooter from './components/TheFooter.vue'
1921
import TheMenu from './components/TheMenu.vue'
2022
import TheSearchScreen from './components/TheSearchScreen.vue'
2123
import TheBackground from './components/TheBackground.vue'
22-
import { mapState } from 'vuex'
2324
2425
export default {
2526
name: 'App',
@@ -60,8 +61,9 @@ export default {
6061
this.applyTheme()
6162
},
6263
},
63-
created () {
64+
async created () {
6465
this.applyTheme()
66+
notificationService.setupNotificationWatcher()
6567
},
6668
}
6769
</script>

src/api/notification.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Api from './index'
2+
3+
export default {
4+
async subscribe (id, subscription) {
5+
const req = await Api().post(`/notification/subscribe/${id}`, subscription)
6+
return req.status === 200 ? req.data : undefined
7+
},
8+
async unsubscribe (id, subscription) {
9+
const req = await Api().get(`/notification/unsubscribe/${id}`, subscription)
10+
return req.status === 200 ? req.data : undefined
11+
},
12+
async subscribeTopic (id, topic, subscription) {
13+
const req = await Api().post(`/notification/subscribe/${topic}/${id}`, subscription)
14+
return req.status === 200 ? req.data : undefined
15+
},
16+
async unsubscribeTopic (id, topic, subscription) {
17+
const req = await Api().post(`/notification/unsubscribe/${topic}/${id}`, subscription)
18+
return req.status === 200 ? req.data : undefined
19+
},
20+
}

src/components/LaunchCard.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@
3838
</template>
3939

4040
<script>
41-
import Tag from './Tag.vue'
42-
import Ticker from './Ticker.vue'
41+
import { mapActions } from 'vuex'
4342
import theme from '../mixins/themes.js'
4443
import launchMixin from '../mixins/launch.js'
45-
import { mapActions } from 'vuex'
44+
import Tag from './Tag'
45+
import Ticker from './Ticker'
4646
4747
export default {
4848
components: { Tag, Ticker },

src/components/LaunchList.vue

+34-22
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
<template>
22
<div class="launch-list">
3-
<h1 class="launch-list__header" v-if="title">
3+
<div class="launch-list__header" v-if="title">
44
<font-awesome-icon v-if="icon" :icon="icon"/>
5-
<span>{{ filter }}</span>
6-
<span>{{ search }}</span>
7-
</h1>
5+
<h1>
6+
<span>{{ filter }}</span>
7+
<span>{{ search }}</span>
8+
</h1>
9+
<subscribe-button
10+
v-if="state && filter"
11+
:id="filter" :topic="state"
12+
/>
13+
</div>
814
<launch-card-featured v-if="featured" :launch="featured" :forced="true" />
915
<div class="launch-list__content">
1016
<h2 class="launch-list__heading">
@@ -20,7 +26,7 @@
2026
:launch="launch"
2127
/>
2228
</section>
23-
<base-button v-if="more.next" @click.native="fetchMoreNext" text="Load more" :loading="loading.next" />
29+
<base-button class="launch-list__more-button" v-if="more.next" @click.native="fetchMoreNext" text="Load more" :loading="loading.next" />
2430
<h3 v-else class="launch-list__heading --medium">phew... that was the last one!</h3>
2531
</template>
2632
<h3 v-else class="launch-list__heading --tall">No upcoming Launches</h3>
@@ -38,7 +44,7 @@
3844
:ticker="false"
3945
/>
4046
</section>
41-
<base-button v-if="more.past" @click.native="fetchMorePast" text="Load more" :loading="loading.past" />
47+
<base-button class="launch-list__more-button" v-if="more.past" @click.native="fetchMorePast" text="Load more" :loading="loading.past" />
4248
<h3 v-else class="launch-list__heading --medium">phew... that was the last one!</h3>
4349
</template>
4450
<h3 v-else class="launch-list__heading --tall">No past launches</h3>
@@ -48,12 +54,13 @@
4854

4955
<script>
5056
import { mapState, mapActions } from 'vuex'
51-
import LaunchCard from './LaunchCard.vue'
52-
import LaunchCardFeatured from './LaunchCardFeatured.vue'
53-
import BaseButton from './BaseButton.vue'
57+
import LaunchCard from './LaunchCard'
58+
import LaunchCardFeatured from './LaunchCardFeatured'
59+
import BaseButton from './BaseButton'
60+
import SubscribeButton from './SubscribeButton'
5461
5562
export default {
56-
components: { LaunchCard, LaunchCardFeatured, BaseButton },
63+
components: { LaunchCard, LaunchCardFeatured, BaseButton, SubscribeButton },
5764
name: 'LaunchList',
5865
props: {
5966
state: {
@@ -124,6 +131,13 @@ export default {
124131
}),
125132
},
126133
methods: {
134+
...mapActions({
135+
getMoreNext: 'launches/getMoreNextLaunches',
136+
getMorePast: 'launches/getMorePastLaunches',
137+
unsetState: 'launches/unsetState',
138+
getNext: 'launches/getNextLaunches',
139+
getPast: 'launches/getPastLaunches',
140+
}),
127141
fetchMoreNext: async function () {
128142
this.loading.next = true
129143
const fetched = await this.getMoreNext({
@@ -144,13 +158,6 @@ export default {
144158
if (fetched === 0) this.more.past = false
145159
this.loading.past = false
146160
},
147-
...mapActions({
148-
getMoreNext: 'launches/getMoreNextLaunches',
149-
getMorePast: 'launches/getMorePastLaunches',
150-
unsetState: 'launches/unsetState',
151-
getNext: 'launches/getNextLaunches',
152-
getPast: 'launches/getPastLaunches',
153-
}),
154161
},
155162
created: async function () {
156163
const { state, getQuery, filter } = this
@@ -178,23 +185,28 @@ export default {
178185
}
179186
180187
&__header {
181-
font-size: calc(.8em + 5vw);
182188
display: flex;
183189
flex-flow: column;
184190
justify-content: center;
185191
align-items: center;
186192
text-align: center;
187193
min-height: calc(35vh - 77px);
188194
margin: 0 10px;
195+
font-size: 1.6em;
196+
padding: 1em 0;
189197
190198
@media only screen and (min-width: 640px) {
191199
min-height: calc(40vh - 77px);
192-
font-size: 4em;
200+
font-size: 2em;
193201
}
194202
195203
svg {
196-
margin-bottom: .4em;
197-
font-size: 1.4em;
204+
font-size: 4em;
205+
}
206+
207+
.base-button {
208+
font-size: .6em;
209+
margin: 0;
198210
}
199211
}
200212
@@ -231,7 +243,7 @@ export default {
231243
}
232244
}
233245
234-
.base-button {
246+
&__more-button {
235247
margin: 60px 0;
236248
font-weight: 500;
237249
align-self: center;

src/components/LaunchOverview.vue

+20-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
class="launch-overview"
44
:class="[{ '--placeholder': launch.placeholder }]">
55
<template v-if="launch.id">
6+
<div class="launch-overview__subscribe">
7+
<subscribe-button class="--right" :id="launch.id"/>
8+
</div>
69
<h2 class="launch-overview__title">
710
<span class="launch-overview__title--primary">{{ launch.rocket.name }}</span>
811
<span class="launch-overview__title--secondary">{{ launch.mission.short }}</span>
@@ -75,6 +78,7 @@
7578
import { mapState } from 'vuex'
7679
import Ticker from './Ticker.vue'
7780
import BaseButton from './BaseButton'
81+
import SubscribeButton from './SubscribeButton'
7882
import LaunchOverviewRow from './LaunchOverviewRow'
7983
import LaunchOverviewItem from './LaunchOverviewItem'
8084
import launchMixin from '../mixins/launch.js'
@@ -86,6 +90,7 @@ export default {
8690
LaunchOverviewItem,
8791
Ticker,
8892
BaseButton,
93+
SubscribeButton,
8994
},
9095
mixins: [ launchMixin ],
9196
computed: {
@@ -121,11 +126,11 @@ export default {
121126
align-items: center;
122127
flex-flow: column;
123128
padding: 1.4em;
129+
padding-top: 0;
124130
min-width: 0;
125131
text-align: center;
126132
font-size: 13px;
127133
animation: fade-in .25s;
128-
margin-top: 2em;
129134
width: 100%;
130135
131136
> * {
@@ -150,6 +155,19 @@ export default {
150155
}
151156
}
152157
158+
&__subscribe {
159+
display: flex;
160+
justify-content: flex-end;
161+
width: 100%;
162+
font-size: .6em;
163+
margin-top: 0;
164+
165+
@media only screen and (min-width: 640px) {
166+
margin-top: $margin;
167+
font-size: .8em;
168+
}
169+
}
170+
153171
&__title {
154172
font-weight: 500;
155173
font-size: 1.8em;
@@ -179,7 +197,7 @@ export default {
179197
}
180198
&__desc {
181199
font-size: 1.2em;
182-
line-height: 1.5em;
200+
line-height: 2em;
183201
}
184202
&__watch {
185203
font-size: 1em;

0 commit comments

Comments
 (0)