Add Initial PWA Functionality
This commit is contained in:
parent
1dc37d7459
commit
320c305949
|
@ -146,13 +146,17 @@ if (isDevMode) {
|
||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
new CopyWebpackPlugin([
|
new CopyWebpackPlugin([
|
||||||
{
|
{
|
||||||
from: path.join(__dirname, '../static'),
|
from: path.join(__dirname, '../static/pwabuilder-sw.js'),
|
||||||
to: path.join(__dirname, '../dist/web/static'),
|
to: path.join(__dirname, '../dist/web/pwabuilder-sw.js'),
|
||||||
ignore: ['.*'],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: path.join(__dirname, '../__icons'),
|
from: path.join(__dirname, '../static'),
|
||||||
to: path.join(__dirname, '../dist/web/icons'),
|
to: path.join(__dirname, '../dist/web/static'),
|
||||||
|
ignore: ['.*', 'pwabuilder-sw.js'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: path.join(__dirname, '../_icons'),
|
||||||
|
to: path.join(__dirname, '../dist/web/_icons'),
|
||||||
ignore: ['.*'],
|
ignore: ['.*'],
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport"
|
<meta name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0" />
|
content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="manifest" href="static/manifest.json" />
|
||||||
<title></title>
|
<title></title>
|
||||||
<% if (htmlWebpackPlugin.options.nodeModules) { %>
|
<% if (htmlWebpackPlugin.options.nodeModules) { %>
|
||||||
<script>
|
<script>
|
||||||
|
@ -26,6 +27,28 @@
|
||||||
.replace(/\\/g, '\\\\')
|
.replace(/\\/g, '\\\\')
|
||||||
} catch {}
|
} catch {}
|
||||||
</script>
|
</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 -->
|
<!-- webpack builds are automatically injected -->
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
:id="id"
|
:id="id"
|
||||||
v-model="inputData"
|
|
||||||
class="ft-input"
|
class="ft-input"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
|
@input="e => inputData = e.target.value"
|
||||||
>
|
>
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
v-if="showArrow"
|
v-if="showArrow"
|
||||||
|
|
|
@ -13,6 +13,7 @@ export default Vue.extend({
|
||||||
data: () => {
|
data: () => {
|
||||||
return {
|
return {
|
||||||
component: this,
|
component: this,
|
||||||
|
windowWidth: 0,
|
||||||
showFilters: false
|
showFilters: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -30,6 +31,13 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
|
const appWidth = $(window).width()
|
||||||
|
|
||||||
|
if (appWidth <= 680) {
|
||||||
|
const searchContainer = $('.searchContainer').get(0)
|
||||||
|
searchContainer.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', function(event) {
|
window.addEventListener('resize', function(event) {
|
||||||
const width = event.srcElement.innerWidth
|
const width = event.srcElement.innerWidth
|
||||||
const searchContainer = $('.searchContainer').get(0)
|
const searchContainer = $('.searchContainer').get(0)
|
||||||
|
@ -43,12 +51,11 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goToSearch: function (query) {
|
goToSearch: function (query) {
|
||||||
console.log(this)
|
|
||||||
this.showFilters = false
|
|
||||||
const appWidth = $(window).width()
|
const appWidth = $(window).width()
|
||||||
|
|
||||||
if (appWidth <= 680) {
|
if (appWidth <= 680) {
|
||||||
const searchContainer = $('.searchContainer').get(0)
|
const searchContainer = $('.searchContainer').get(0)
|
||||||
|
searchContainer.blur()
|
||||||
searchContainer.style.display = 'none'
|
searchContainer.style.display = 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +70,8 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
this.showFilters = false
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleSearchContainer: function () {
|
toggleSearchContainer: function () {
|
||||||
|
|
|
@ -111,10 +111,6 @@ const router = new Router({
|
||||||
icon: 'fa-user'
|
icon: 'fa-user'
|
||||||
},
|
},
|
||||||
component: Watch
|
component: Watch
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '*',
|
|
||||||
redirect: '/subscriptions'
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
scrollBehavior (to, from, savedPosition) {
|
scrollBehavior (to, from, savedPosition) {
|
||||||
|
|
|
@ -30,7 +30,6 @@ export default Vue.extend({
|
||||||
showDashPlayer: true,
|
showDashPlayer: true,
|
||||||
showLegacyPlayer: false,
|
showLegacyPlayer: false,
|
||||||
showYouTubeNoCookieEmbed: false,
|
showYouTubeNoCookieEmbed: false,
|
||||||
proxyVideos: false,
|
|
||||||
hidePlayer: false,
|
hidePlayer: false,
|
||||||
activeFormat: 'legacy',
|
activeFormat: 'legacy',
|
||||||
videoId: '',
|
videoId: '',
|
||||||
|
@ -65,6 +64,10 @@ export default Vue.extend({
|
||||||
return this.$store.getters.getInvidiousInstance
|
return this.$store.getters.getInvidiousInstance
|
||||||
},
|
},
|
||||||
|
|
||||||
|
proxyVideos: function () {
|
||||||
|
return this.$store.getters.getProxyVideos
|
||||||
|
},
|
||||||
|
|
||||||
defaultVideoFormat: function () {
|
defaultVideoFormat: function () {
|
||||||
return this.$store.getters.getDefaultVideoFormat
|
return this.$store.getters.getDefaultVideoFormat
|
||||||
},
|
},
|
||||||
|
@ -82,9 +85,15 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
dashSrc: function () {
|
dashSrc: function () {
|
||||||
|
let url = `${this.invidiousInstance}/api/manifest/dash/${this.videoId}.mpd`
|
||||||
|
|
||||||
|
if (this.proxyVideos) {
|
||||||
|
url = url + '?local=true'
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
url: `${this.invidiousInstance}/api/manifest/dash/${this.videoId}.mpd`,
|
url: url,
|
||||||
type: 'application/dash+xml',
|
type: 'application/dash+xml',
|
||||||
label: 'Dash',
|
label: 'Dash',
|
||||||
},
|
},
|
||||||
|
@ -118,10 +127,6 @@ export default Vue.extend({
|
||||||
|
|
||||||
this.activeFormat = this.defaultVideoFormat
|
this.activeFormat = this.defaultVideoFormat
|
||||||
|
|
||||||
if (this.proxyVideos) {
|
|
||||||
this.dashSrc = this.dashSrc + '?local=true'
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.backendPreference) {
|
switch (this.backendPreference) {
|
||||||
case 'local':
|
case 'local':
|
||||||
this.getVideoInformationLocal()
|
this.getVideoInformationLocal()
|
||||||
|
@ -239,9 +244,9 @@ export default Vue.extend({
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.forceLocalBackendForLegacy) {
|
if (this.forceLocalBackendForLegacy) {
|
||||||
this.videoSourceList = result.formatStreams.reverse()
|
|
||||||
} else {
|
|
||||||
this.getLegacyFormats()
|
this.getLegacyFormats()
|
||||||
|
} else {
|
||||||
|
this.videoSourceList = result.formatStreams.reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
Loading…
Reference in New Issue