VueJS-#15 PWA Vue Firebase

Persiapan

Buat Proyek vue-vuetify-pwa

vue create vue-vuetify-pwa
Pilih Manually
Pilih PWA

Install Vuetify Theme

vue add vuetify

Install Firebase into Vue Project

npm install firebase --save

Buat Project di FireBase

Sebelum memulai membuat Program, kita akan menyiapkan Proyek di Firebase, masuk ke Firebase Console buat Proyek baru, setelah selesai masuk ke Menu Project Overview => Setelan proyek

Setelan Proyek

Menambahkan Firebase ke Aplikasi Web

Parameter Pengaturan Firebase

Sekarang kita akan membuat setingan untuk firebase di Program Vue kita sesuaikan dengan gambar di atas. Buka main.js tambahkan baris berikut diantara import dan Vue.config.productionTip = false;

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "./registerServiceWorker";
import vuetify from './plugins/vuetify';
import firebase from 'firebase/app';
import '@firebase/messaging';

const firebaseConfig = {
    apiKey: "AIzaxxxxxxxxxxxxxxxxxxxxx_xxxxxM3tRDNPc",
    authDomain: "vue-firebase-xxxxx.firebaseapp.com",
    databaseURL: "https://vue-firebase-xxxxx.firebaseio.com",
    projectId: "vue-firebase-xxxxx",
    storageBucket: "vue-firebase-xxxxx.appspot.com",
    messagingSenderId: "8142xxxxx626",
    appId: "1:8142xxxxx626:web:a313b9d16f14abb0fdb383"
};
firebase.initializeApp(firebaseConfig);

const messaging = firebase.messaging();

messaging.usePublicVapidKey("BASZxxxxxxxxxxxxxxx2tlVbCLpQ-F1nlfv9J0nOLF6457jgltDDLt-xxxxxxxxxxxxxx-K7NaBcCJqHbuPwf-o"); // 1. Generate a new key pair

// Request Permission of Notifications
messaging.requestPermission().then(() => {
  console.log('Notification permission granted.');

  // Get Token
  messaging.getToken().then((token) => {
    console.log(token)
  })
}).catch((err) => {
  console.log('Unable to get permission to notify.', err);
});

messaging.onMessage((payload) => {
    console.log('Message received. ', payload);
    // ...
});

Vue.config.productionTip = false;

new Vue({
    firebase,
    router,
    store,
    vuetify,
    render: h => h(App)
}).$mount("#app");

npm install firebase --save

Menyiapkan Route

router.js

import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue";
import Login from "./views/Login.vue";
import Profile from "./views/Profile.vue";
import Todos from "@/views/Todos";
import store from "./store.js";

Vue.use(Router);

let router =  new Router({
    mode: "history",
    base: process.env.BASE_URL,
    routes: [
        {
            path: "/home",
            name: "Home",
            component: Home,
            meta: {
                auth: true
            }
        },
        {
            path: '/about',
            name: 'about',
            component: () => import('./views/About.vue'),
            meta: {
                auth: true
            }
        },
        {
            path: "/",
            name: "Login",
            component: Login,
            meta: {
                guest: true
            }
        },
        {
            path: "/profile",
            name: "Profile",
            component: Profile,
            meta: {
                //auth: true
            }
        },
        {
            path: '/todo',
            name: 'Todos',
            component: Todos,
            meta: {
                auth: true
            }
        }
    ]
});
router.beforeEach((to, from, next) => {
    if (to.matched.some(record => record.meta.auth)) {
        if (store.state.isLogin) {
            next();
        } else {
            next({
                path: "/",
            });
        }
    } else if (to.matched.some(record => record.meta.guest)) {
        if (store.state.isLogin) {
            next({
                path: "/profile",
            });
        } else {
            next();
        }
    } else {
        next();
    }
});
export default router;

Menyiapkan Store

Kita perlu menyiapkan store untuk menyimpan status login & data user yang nantinya akan ditampilkan pada component Profile.vue

import Vue from "vue";
import Vuex from "vuex";
import todo from './modules/todo';

Vue.use(Vuex);

export default new Vuex.Store({
    state: {
        isLogin: false,
        user:null,
    },
    mutations: {
        resetState (state) {
            state.isLogin=false;
            state.user=null;
        }
    },
    actions: {
        clearState ({ commit }) {
            commit('resetState');
        },
    },
    modules: {
        todo,
    }
});

Login component menggunakan FirebaseUI

FirebaseUI adalah library yang dibuat sebagai tambahan dari Firebase Authentication SDK yang menyediakan alur UI drop-in untuk digunakan dalam aplikasi Anda. Baca penjelasannya disini.

Sekarang kita akan menambahkan FirebaseUI di program Vue kita

npm install firebaseui --save

Karena kita akan login menggunakan gmail atau email, maka kita harus mengaktifkan options Authentication pada Firebase Console. Masuk ke Menu Authentication=>Pilih Tab Metode Login lalu aktifkan pilihan Email & Google

Metode Login

Setelah itu kita buat component Login.vue yang nantinya akan menampilkan halaman login.

<template>
    <div>
        <v-container>
                <v-row>
                    <v-col cols="2" md="4"></v-col>
                    <v-col cols="8" md="4">
                        <br/><br/>
                        <v-sheet class="text-center"
                            elevation="3"
                        >
                            <br/><br/>
                            <section id="firebaseui-auth-container"></section>
                            <br/><br/>
                        </v-sheet>
                    </v-col>
                    <v-col cols="2" md="4"></v-col>
                </v-row>
        </v-container>
    </div>
</template>

<script>
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/messaging';
import * as firebaseui from "firebaseui";
import "firebaseui/dist/firebaseui.css";
export default {
    name: "Login",
    data() {
        return {};
    },
    created() {
        firebase.auth().onAuthStateChanged(user => {
            if (user) {
                this.$store.state.isLogin=true;
                this.$store.state.user=user;
                this.user=this.$store.state.user;
                this.$router.push('/profile');
            }
        });
    },
    mounted() {
        let ui = firebaseui.auth.AuthUI.getInstance();
        if (!ui) {
            ui = new firebaseui.auth.AuthUI(firebase.auth());
        }
        var uiConfig = {
            signInSuccessUrl: "/profile",
            signInFlow: "popup",
            signInOptions: [
                //firebase.auth.FacebookAuthProvider.PROVIDER_ID,
                firebase.auth.GoogleAuthProvider.PROVIDER_ID,
                firebase.auth.EmailAuthProvider.PROVIDER_ID
            ]
        };
        ui.start("#firebaseui-auth-container", uiConfig);
    }
};
</script>

<style>
</style>

Perhatikan baris signInSuccessUrl: “/profile”, Jika login berhasil route akan diarahkan ke route /profile.

Buat NavBar

Selanjutnya kita akan membuat sebuah component NavBar.vue yang nantinya bisa diakses di setiap component jika dalam posisi login

components/NavBar.vue

<template>
    <section>
        <v-navigation-drawer v-model="drawer" app>
        <v-list-item>
            <v-list-item-content>
            <v-list-item-title class="title">
                Application Name
            </v-list-item-title>
            <v-list-item-subtitle>
                subtext
            </v-list-item-subtitle>
            </v-list-item-content>
        </v-list-item>
        <v-divider></v-divider>
        <v-list dense nav>
            <v-list-item to="/Home">
                <v-list-item-action>
                    <v-icon>mdi-home</v-icon>
                </v-list-item-action>
                <v-list-item-content>
                    <v-list-item-title>Home</v-list-item-title>
                </v-list-item-content>
            </v-list-item>
            <v-list-item  to="/About">
                <v-list-item-action>
                    <v-icon>mdi-help-circle</v-icon>
                </v-list-item-action>
                <v-list-item-content>
                    <v-list-item-title>About</v-list-item-title>
                </v-list-item-content>
            </v-list-item>
            <v-list-item  to="/ToDo">
                <v-list-item-action>
                    <v-icon>mdi-clipboard-list-outline</v-icon>
                </v-list-item-action>
                <v-list-item-content>
                    <v-list-item-title>ToDo List</v-list-item-title>
                </v-list-item-content>
            </v-list-item>
        </v-list>
        <v-divider></v-divider>
        <v-list dense nav>
            <v-list-item  to="/Messages">
                <v-list-item-action>
                    <v-badge>
                        <template v-slot:badge>0</template>
                        <v-icon>mdi-email</v-icon>
                    </v-badge>
                </v-list-item-action>
                <v-list-item-content>
                    <v-list-item-title>Messages</v-list-item-title>
                </v-list-item-content>
            </v-list-item>
        </v-list>
        <template v-slot:append>
            <div class="pa-2">
                <v-btn @click="signoutButtonPressed" color="red darken-4 white--text" block>Logout</v-btn>
            </div>
        </template>
        </v-navigation-drawer>

        <v-app-bar app color="blue darken-4" dark>
            <v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
            <v-toolbar-title>Application</v-toolbar-title>
            <div class="flex-grow-1"></div>

            <v-btn icon>
                <v-icon>mdi-heart</v-icon>
            </v-btn>
            <v-menu left bottom>
                <template v-slot:activator="{ on }">
                <v-btn icon v-on="on">
                    <v-icon>mdi-dots-vertical</v-icon>
                </v-btn>
                </template>

                <v-list>
                    <v-list-item  to="/Profile">
                        <v-list-item-action>
                            <v-icon>mdi-account-circle</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>Profile</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>
                    <v-list-item  to="/Setting">
                        <v-list-item-action>
                            <v-icon>mdi-settings</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>Setting</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>
                </v-list>
            </v-menu>
        </v-app-bar>
    </section>
</template>
<script>
import firebase from 'firebase/app';
import 'firebase/auth';
export default {
    props: {
      source: String,
    },
    data: () => ({
      drawer: null,
    }),
    created() {
        firebase.auth().onAuthStateChanged(user => {
            if (!user&amp;&amp;this.$route.name!='Login') {
                this.$router.push('/');
            }
        });
    },
    methods: {
        signoutButtonPressed(e) {
            e.stopPropagation();
            firebase.auth().signOut();
            this.$store.dispatch('clearState');
        }
    },
};
</script>

Buat Layout

Buat layout pada App.vue

<template>
  <v-app teal>
    <NavBar v-if="getStatusLogin"/>
    <v-content>
      <router-view />
    </v-content>
    <v-footer color="blue darken-4" app>
      <span class="white--text">© 2019</span>
    </v-footer>
  </v-app>
</template>

<script>
import NavBar from "./components/NavBar";
import firebase from 'firebase/app';
import 'firebase/auth';
export default {
    name: "App",
    components: {
        NavBar
    },
    data: () => ({
        //
    }),
    computed: {
        getStatusLogin: function () {
            console.log(this.$store.state.isLogin);
            return this.$store.state.isLogin;
        }
    }
};
</script>

perhatikan baris <NavBar v-if=”getStatusLogin”/> component ini hanya akan tampil jika posisi user login dimana getStatusLogin diambil dari store.

Buat View

Views/Profile.vue

<template>
    <section>
        <v-container class="pa-2" fluid>
            <v-row>
                <v-col>
                    <v-card color="#385F73" dark>
                        <v-card-text v-if="user" class="white--text">
                            <v-avatar><img :src="user.photoURL" alt="avatar"></v-avatar>
                            <div class="headline mb-2" v-if="user">{{user.displayName}}</div>
                            <p>
                                name:<strong>{{user.displayName}}</strong>
                                <br />email:
                                <strong>{{user.email}}</strong>
                                <br />uid:
                                <strong>{{user.uid}}</strong>
                                <br />provider:
                                <strong class="teal-text">{{user.providerData[0].providerId}}</strong>
                            </p>
                        </v-card-text>

                        <v-card-actions>
                            <v-btn text>Listen Now</v-btn>
                        </v-card-actions>
                    </v-card>
                </v-col>
            </v-row>
        </v-container>
    </section>
</template>

<script>
import firebase from 'firebase/app';
import 'firebase/auth';
export default {
    data: () => ({
        user:null,
        result:null,
        errors:null
    }),
    created() {
        firebase.auth().onAuthStateChanged(user => {
            if (user) {
                this.$store.state.isLogin=true;
                this.$store.state.user=user;
                this.user=this.$store.state.user;
            }
        });
    },
}
</script>

Initialize a Firebase project

Untuk menginisialisasi bahwa proyek ini menggunakan firebase

firebase init

Sehingga struktur direktori akan menjadi sbb:

Proyek akan di build di direktori dist

Setting Firebase Message

Tambahkan file firebase-messaging-sw.js pada folder public, isi program seperti di bawah ini:

// [START initialize_firebase_in_sw]
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/6.3.4/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/6.3.4/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the messagingSenderId.
firebase.initializeApp({
  'messagingSenderId': '81422xxxxx26' // 4. Get Firebase Configuration
});

// Retrieve an instance of Firebase Messaging so that it can handle background messages.
const messaging = firebase.messaging();
// [END initialize_firebase_in_sw]
messaging.setBackgroundMessageHandler(function(payload) {
    console.log('[firebase-messaging-sw.js] Received background message ', payload);
    // Customize notification here
    const notificationTitle = 'Background Message Title';
    const notificationOptions = {
      body: 'Background Message body.',
      icon: ''
    };
  
    return self.registration.showNotification(notificationTitle,
      notificationOptions);
});

Sesuaikan messagingSenderId dengan Firebase anda.

Build Proyek

PWA hanya akan berjalan di server distribution, build proyek anda:

npm run build

Tambahkan redirect

Tambahkan baris berikut pada router.js

{
    path: "/index.html",
    redirect: "/"
}

Deploy

firebase deploy

Dapatkan Token Firebase

Test Send Notification

sumber:

  1. https://medium.com/the-web-tub/creating-your-first-vue-js-pwa-project-22f7c552fb34
  2. https://medium.com/@eder.ramirez87/modern-pwa-with-vue-cli-3-vuetify-firestore-workbox-part-1-974383be5540

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>