Add Initial PWA Functionality

This commit is contained in:
Preston 2020-04-13 22:59:25 -04:00
parent 1dc37d7459
commit 320c305949
8 changed files with 238 additions and 20 deletions

View File

@ -146,13 +146,17 @@ if (isDevMode) {
config.plugins.push(
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/web/static'),
ignore: ['.*'],
from: path.join(__dirname, '../static/pwabuilder-sw.js'),
to: path.join(__dirname, '../dist/web/pwabuilder-sw.js'),
},
{
from: path.join(__dirname, '../__icons'),
to: path.join(__dirname, '../dist/web/icons'),
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/web/static'),
ignore: ['.*', 'pwabuilder-sw.js'],
},
{
from: path.join(__dirname, '../_icons'),
to: path.join(__dirname, '../dist/web/_icons'),
ignore: ['.*'],
},
]),

View File

@ -5,6 +5,7 @@
<meta charset="utf-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<link rel="manifest" href="static/manifest.json" />
<title></title>
<% if (htmlWebpackPlugin.options.nodeModules) { %>
<script>
@ -26,6 +27,28 @@
.replace(/\\/g, '\\\\')
} catch {}
</script>
<script>
// This is the service worker with the Advanced caching
// Add this below content to your HTML page, or add the js file to your page at the very top to register service worker
// Check compatibility for the browser we're running this in
if ("serviceWorker" in navigator) {
if (navigator.serviceWorker.controller) {
console.log("[PWA Builder] active service worker found, no need to register");
} else {
// Register the service worker
navigator.serviceWorker
.register("pwabuilder-sw.js", {
scope: "./"
})
.then(function (reg) {
console.log("[PWA Builder] Service worker has been registered for scope: " + reg.scope);
});
}
}
</script>
<!-- webpack builds are automatically injected -->
</body>

View File

@ -5,10 +5,10 @@
>
<input
:id="id"
v-model="inputData"
class="ft-input"
type="text"
:placeholder="placeholder"
@input="e => inputData = e.target.value"
>
<font-awesome-icon
v-if="showArrow"

View File

@ -13,6 +13,7 @@ export default Vue.extend({
data: () => {
return {
component: this,
windowWidth: 0,
showFilters: false
}
},
@ -30,6 +31,13 @@ export default Vue.extend({
}
},
mounted: function () {
const appWidth = $(window).width()
if (appWidth <= 680) {
const searchContainer = $('.searchContainer').get(0)
searchContainer.style.display = 'none'
}
window.addEventListener('resize', function(event) {
const width = event.srcElement.innerWidth
const searchContainer = $('.searchContainer').get(0)
@ -43,12 +51,11 @@ export default Vue.extend({
},
methods: {
goToSearch: function (query) {
console.log(this)
this.showFilters = false
const appWidth = $(window).width()
if (appWidth <= 680) {
const searchContainer = $('.searchContainer').get(0)
searchContainer.blur()
searchContainer.style.display = 'none'
}
@ -63,6 +70,8 @@ export default Vue.extend({
}
}
)
this.showFilters = false
},
toggleSearchContainer: function () {

View File

@ -111,10 +111,6 @@ const router = new Router({
icon: 'fa-user'
},
component: Watch
},
{
path: '*',
redirect: '/subscriptions'
}
],
scrollBehavior (to, from, savedPosition) {

View File

@ -30,7 +30,6 @@ export default Vue.extend({
showDashPlayer: true,
showLegacyPlayer: false,
showYouTubeNoCookieEmbed: false,
proxyVideos: false,
hidePlayer: false,
activeFormat: 'legacy',
videoId: '',
@ -65,6 +64,10 @@ export default Vue.extend({
return this.$store.getters.getInvidiousInstance
},
proxyVideos: function () {
return this.$store.getters.getProxyVideos
},
defaultVideoFormat: function () {
return this.$store.getters.getDefaultVideoFormat
},
@ -82,9 +85,15 @@ export default Vue.extend({
},
dashSrc: function () {
let url = `${this.invidiousInstance}/api/manifest/dash/${this.videoId}.mpd`
if (this.proxyVideos) {
url = url + '?local=true'
}
return [
{
url: `${this.invidiousInstance}/api/manifest/dash/${this.videoId}.mpd`,
url: url,
type: 'application/dash+xml',
label: 'Dash',
},
@ -118,10 +127,6 @@ export default Vue.extend({
this.activeFormat = this.defaultVideoFormat
if (this.proxyVideos) {
this.dashSrc = this.dashSrc + '?local=true'
}
switch (this.backendPreference) {
case 'local':
this.getVideoInformationLocal()
@ -239,9 +244,9 @@ export default Vue.extend({
})
if (this.forceLocalBackendForLegacy) {
this.videoSourceList = result.formatStreams.reverse()
} else {
this.getLegacyFormats()
} else {
this.videoSourceList = result.formatStreams.reverse()
}
this.isLoading = false

19
static/manifest.json Normal file
View File

@ -0,0 +1,19 @@
{
"dir" : "ltr",
"lang" : "de",
"name" : "FreeTube",
"scope" : "/",
"display" : "standalone",
"start_url" : "https://app.freetubeapp.io/",
"short_name" : "FreeTube",
"theme_color" : "transparent",
"description" : "A description",
"orientation" : "any",
"background_color" : "transparent",
"related_applications" : [],
"prefer_related_applications" : false,
"icons" : ["/_icons/logoColor.png"],
"url" : "https://app.freetubeapp.io",
"screenshots" : [],
"generated" : "true"
}

162
static/pwabuilder-sw.js Normal file
View File

@ -0,0 +1,162 @@
// This is the service worker with the Advanced caching
const CACHE = 'pwabuilder-adv-cache'
const precacheFiles = [
/* Add an array of files to precache for your app */
'index.html',
'web.js',
'web.css',
'static/*',
'_icons/*',
'fonts/*'
]
// TODO: replace the following with the correct offline fallback page i.e.: const offlineFallbackPage = "offline.html";
const offlineFallbackPage = 'index.html'
const networkFirstPaths = [
/* Add an array of regex of paths that should go network first */
// Example: /\/api\/.*/
]
const avoidCachingPaths = [
/* Add an array of regex of paths that shouldn't be cached */
// Example: /\/api\/.*/
]
function pathComparer(requestUrl, pathRegEx) {
return requestUrl.match(new RegExp(pathRegEx))
}
function comparePaths(requestUrl, pathsArray) {
if (requestUrl) {
for (let index = 0; index < pathsArray.length; index++) {
const pathRegEx = pathsArray[index]
if (pathComparer(requestUrl, pathRegEx)) {
return true
}
}
}
return false
}
self.addEventListener('install', function (event) {
console.log('[PWA Builder] Install Event processing')
console.log('[PWA Builder] Skip waiting on install')
self.skipWaiting()
event.waitUntil(
caches.open(CACHE).then(function (cache) {
console.log('[PWA Builder] Caching pages during install')
return cache.addAll(precacheFiles).then(function () {
if (offlineFallbackPage === 'ToDo-replace-this-name.html') {
return cache.add(new Response('TODO: Update the value of the offlineFallbackPage constant in the serviceworker.'))
}
return cache.add(offlineFallbackPage)
})
})
)
})
// Allow sw to control of current page
self.addEventListener('activate', function (event) {
console.log('[PWA Builder] Claiming clients for current page')
event.waitUntil(self.clients.claim())
})
// If any fetch fails, it will look for the request in the cache and serve it from there first
self.addEventListener('fetch', function (event) {
if (event.request.method !== 'GET') return
if (comparePaths(event.request.url, networkFirstPaths)) {
networkFirstFetch(event)
} else {
cacheFirstFetch(event)
}
})
function cacheFirstFetch(event) {
event.respondWith(
fromCache(event.request).then(
function (response) {
// The response was found in the cache so we responde with it and update the entry
// This is where we call the server to get the newest version of the
// file to use the next time we show view
event.waitUntil(
fetch(event.request).then(function (response) {
return updateCache(event.request, response)
})
)
return response
},
function () {
// The response was not found in the cache so we look for it on the server
return fetch(event.request)
.then(function (response) {
// If request was success, add or update it in the cache
event.waitUntil(updateCache(event.request, response.clone()))
return response
})
.catch(function (error) {
// The following validates that the request was for a navigation to a new document
if (event.request.destination !== 'document' || event.request.mode !== 'navigate') {
return
}
console.log('[PWA Builder] Network request failed and no cache.' + error)
// Use the precached offline page as fallback
return caches.open(CACHE).then(function (cache) {
cache.match(offlineFallbackPage)
})
})
}
)
)
}
function networkFirstFetch(event) {
event.respondWith(
fetch(event.request)
.then(function (response) {
// If request was success, add or update it in the cache
event.waitUntil(updateCache(event.request, response.clone()))
return response
})
.catch(function (error) {
console.log('[PWA Builder] Network request Failed. Serving content from cache: ' + error)
return fromCache(event.request)
})
)
}
function fromCache(request) {
// Check to see if you have it in the cache
// Return response
// If not in the cache, then return error page
return caches.open(CACHE).then(function (cache) {
return cache.match(request).then(function (matching) {
if (!matching || matching.status === 404) {
return Promise.reject('no-match')
}
return matching
})
})
}
function updateCache(request, response) {
if (!comparePaths(request.url, avoidCachingPaths)) {
return caches.open(CACHE).then(function (cache) {
return cache.put(request, response)
})
}
return Promise.resolve()
}