Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix PWA refresh issues and reopen the app on the last opened tab #41

Merged
merged 2 commits into from
Sep 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Read this in other languages: [Español](READMEes.md), [Português](READMEpt.md)

## Features

- PWA support (install this app on your phone)
- [Email Explorer](https://r2explorer.dev/guides/setup-email-explorer/) (using Cloudflare Email Routing)
- [Basic Auth](https://r2explorer.dev/getting-started/security/#basic-auth)
- [Cloudflare Access Authentication](https://r2explorer.dev/getting-started/security/)
Expand Down
37 changes: 25 additions & 12 deletions packages/dashboard/src/components/BucketExplorerWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,29 @@
</template>

<script>
import FolderTree from "@/components/FolderTree.vue";
import EventBus from "@/EventBus";
import FolderTree from '@/components/FolderTree.vue'
import EventBus from '@/EventBus'
import router from '@/router'

export default {
components: {FolderTree},
components: { FolderTree },
methods: {
changeToStorage() {
changeToStorage () {
this.$store.commit('changeTab', 'storage')
this.$router.push({name: 'storage-home', params: {bucket: this.$route.params.bucket}})
this.$router.push({ name: 'storage-home', params: { bucket: this.$route.params.bucket } })
localStorage.setItem('lastOpenTab', JSON.stringify({ name: 'storage-home', params: { bucket: this.$route.params.bucket } }))
},
changeToEmail() {
changeToEmail () {
this.$store.commit('changeTab', 'email')
this.$router.push({name: 'email-folder', params: {bucket: this.$route.params.bucket, folder: 'inbox'}})
this.$router.push({ name: 'email-folder', params: { bucket: this.$route.params.bucket, folder: 'inbox' } })
localStorage.setItem('lastOpenTab', JSON.stringify({ name: 'email-home', params: { bucket: this.$route.params.bucket } }))
}
},
async created() {
async created () {
if (this.$route.params.bucket) {
this.$store.commit('changeBucket', this.$route.params.bucket)
if (this.$route.params.folder) {

if (this.$route.params.folder !== 'IA==') { // IA== is empty space
if (this.$route.params.folder !== 'IA==') { // IA== is empty space
if (this.$store.state.activeTab === 'email') {
await this.$store.dispatch('navigate', this.$route.params.folder)
await this.$store.dispatch('refreshObjects')
Expand All @@ -63,7 +65,6 @@ export default {
if (this.$route.params.file) {
EventBus.$emit('openFile', decodeURIComponent(escape(atob(this.$route.params.file))))
}

} else {
this.$store.dispatch('refreshObjects')
}
Expand All @@ -76,10 +77,22 @@ export default {
this.$store.commit('changeBucket', bucket)
this.$store.dispatch('refreshObjects')
this.$store.commit('toggleMobileSidebar', false)

const lastOpenTab = localStorage.getItem('lastOpenTab')
if (lastOpenTab) {
const parsed = JSON.parse(lastOpenTab)
localStorage.setItem('lastOpenTab', JSON.stringify({
...parsed,
params: {
...parsed.params,
bucket
}
}))
}
}
}
)
},
}
}
</script>

Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/components/storage/Folders.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default {
this.$store.dispatch('navigate', folder.Prefix)
}
},
created () {
mounted () {
this.$watch(
() => this.$route.params.folder,
(newFolder, oldFolder) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/dashboard/src/registerServiceWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ if (process.env.NODE_ENV === 'production') {
console.log('New content is downloading.')
},
updated () {
console.log('New content is available; please refresh.')
console.log('New content is available; refreshing...')
window.location.reload(true)
},
offline () {
console.log('No internet connection found. App is running in offline mode.')
Expand Down
185 changes: 101 additions & 84 deletions packages/dashboard/src/store/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { createStore } from "vuex";
import axios from "axios";
import repo from "@/api";
import router from "@/router";
import { createStore } from 'vuex'
import axios from 'axios'
import repo from '@/api'
import router from '@/router'

export default createStore({
state: {
user: {
username: "[email protected]"
username: '[email protected]'
},
config: {},
activeBucket: null,
currentFolder: "",
currentFolder: '',
files: [],
folders: [],
buckets: [],
Expand All @@ -20,164 +20,181 @@ export default createStore({
mobileSidebar: false,
serverVersion: null,
serverVersionInt: 0,
activeTab: "storage",
activeTab: 'storage',
serverUrl: null,
loginMethod: null,
loginMethod: null
},
getters: {},
mutations: {
setServerUrl(state, serverUrl) {
state.serverUrl = serverUrl;
setServerUrl (state, serverUrl) {
state.serverUrl = serverUrl
},
loadObjects(state, payload) {
state.files = payload.files;
state.folders = payload.folders;
loadObjects (state, payload) {
state.files = payload.files
state.folders = payload.folders
},
toggleMobileSidebar(state, payload) {
toggleMobileSidebar (state, payload) {
if (payload !== true && payload !== false) {
state.mobileSidebar = !state.mobileSidebar;
state.mobileSidebar = !state.mobileSidebar
} else {
state.mobileSidebar = payload;
state.mobileSidebar = payload
}
},
changeBucket(state, payload) {
state.activeBucket = payload;
if (this.state.activeTab === "email") {
state.currentFolder = "inbox";
changeBucket (state, payload) {
state.activeBucket = payload
if (this.state.activeTab === 'email') {
state.currentFolder = 'inbox'
} else {
state.currentFolder = "";
state.currentFolder = ''
}
},
changeTab(state, payload) {
changeTab (state, payload) {
if (payload === state.activeTab) {
return;
return
}

state.activeTab = payload;
state.currentFolder = "";
state.activeTab = payload
state.currentFolder = ''

// state.files = []
// state.folders = []
},
goTo(state, folder) {
state.currentFolder = folder;
goTo (state, folder) {
state.currentFolder = folder
},
loadUserDisks(state, data) {
state.buckets = data.buckets;
loadUserDisks (state, data) {
state.buckets = data.buckets

if ((state.activeBucket === null && data.buckets.length > 0) || router.currentRoute.value.href.startsWith('/auth')) {
const targetView = (location.pathname.startsWith('/email')) ? 'email-home' : 'storage-home'

router.push({ name: targetView, params: { bucket: data.buckets[0].name } });
const lastOpenTab = localStorage.getItem('lastOpenTab')
if (lastOpenTab && location.pathname === '/') {
router.push(JSON.parse(lastOpenTab))
return
}

router.push({ name: targetView, params: { bucket: data.buckets[0].name } })
}
},
loadServerConfigs(state, data) {
state.user = data.user;
state.config = data.config;
state.serverVersion = data.version;
state.serverVersionInt = parseInt(data.version.replace("v", "").replaceAll(".", ""));
loadServerConfigs (state, data) {
state.user = data.user
state.config = data.config
state.serverVersion = data.version
state.serverVersionInt = parseInt(data.version.replace('v', '').replaceAll('.', ''))
},
changeToastMessage(state, { message, spin }) {
state.toastMessage = message;
state.toastSpin = spin || false;
changeToastMessage (state, { message, spin }) {
state.toastMessage = message
state.toastSpin = spin || false
},
addUploadingFiles(state, filenames) {
addUploadingFiles (state, filenames) {
for (const filename of filenames) {
state.uploadingFiles[filename] = {};
state.uploadingFiles[filename] = {}
}
},
clearUploadingFiles(state) {
state.uploadingFiles = {};
clearUploadingFiles (state) {
state.uploadingFiles = {}
},
setUploadProgress(state, { filename, progress }) {
setUploadProgress (state, { filename, progress }) {
if (state.uploadingFiles[filename] === undefined) {
return;
return
}

state.uploadingFiles[filename].progress = Math.ceil(progress);
state.uploadingFiles[filename].progress = Math.ceil(progress)
}
},
actions: {
makeToast(context, { message, timeout, spin }) {
context.commit("changeToastMessage", {
makeToast (context, { message, timeout, spin }) {
context.commit('changeToastMessage', {
message, spin
});
})

if (timeout !== null && timeout !== undefined) {
setTimeout(() => {
context.commit("changeToastMessage", {
context.commit('changeToastMessage', {
message: null, spin: false
});
}, timeout);
})
}, timeout)
}
},
async navigate(context, folder) {
if (folder === "/") {
folder = "";
async navigate (context, folder) {
if (folder === '/') {
folder = ''
}
context.commit("goTo", folder);
await context.dispatch("refreshObjects");
context.commit('goTo', folder)
await context.dispatch('refreshObjects')
},
navigateToHash(context, folder) {
folder = decodeURIComponent(escape(atob(folder)));
context.dispatch("navigate", folder);
navigateToHash (context, folder) {
folder = decodeURIComponent(escape(atob(folder)))
context.dispatch('navigate', folder)
},
addUploadingFiles(context, filenames) {
context.commit("addUploadingFiles", filenames);
addUploadingFiles (context, filenames) {
context.commit('addUploadingFiles', filenames)
},
clearUploadingFiles(context, filenames) {
context.commit("clearUploadingFiles");
clearUploadingFiles (context, filenames) {
context.commit('clearUploadingFiles')
},
setUploadProgress(context, { filename, progress }) {
context.commit("setUploadProgress", { filename, progress });
setUploadProgress (context, { filename, progress }) {
context.commit('setUploadProgress', { filename, progress })
},
loadUserDisks({ commit }) {
axios.get("/api/buckets").then((response) => {
commit("loadUserDisks", response.data);
});
loadUserDisks ({ commit }) {
axios.get('/api/buckets').then((response) => {
commit('loadUserDisks', response.data)
})
},
async tryLogin(context, data) {
async tryLogin (context, data) {
const token = 'Basic ' + btoa(data.username + ':' + data.password)
let result

try {
result = await axios.get('/api/server/config', {
headers: {
Authorization: token
},
validateStatus: function (status) {
return status >= 200 && status < 300
}
})
} catch (e) {}
} catch (e) {
console.log(e)
if (e.response.status === 302) {
const nextUrl = e.response.headers.Location
if (nextUrl) {
window.location.replace(nextUrl)
}
}
}

if (result?.status === 200) {
axios.defaults.headers.common.Authorization = token
localStorage.setItem('basicAuth', token)

context.state.loginMethod = "basic"
context.commit("loadServerConfigs", result.data);
context.state.loginMethod = 'basic'
context.commit('loadServerConfigs', result.data)
context.dispatch('loadUserDisks')
} else {
return 'Invalid username or password'
}
},
async checkBasicAuthStorage(context) {
async checkBasicAuthStorage (context) {
const token = localStorage.getItem('basicAuth')
if (token) {
axios.defaults.headers.common.Authorization = token
context.state.loginMethod = "basic"
context.state.loginMethod = 'basic'
}
},
loadServerConfigs({ commit }) {
axios.get("/api/server/config").then((response) => {
commit("loadServerConfigs", response.data);
}).catch(function(error) {
loadServerConfigs ({ commit }) {
axios.get('/api/server/config').then((response) => {
commit('loadServerConfigs', response.data)
}).catch(function (error) {
if (error.response?.status === 401) {
router.push({ name: "login"});
router.push({ name: 'login' })
}
});
})
},
async refreshObjects({ commit }) {
await commit("loadObjects", await repo.listObjects());
async refreshObjects ({ commit }) {
await commit('loadObjects', await repo.listObjects())
}
},
modules: {}
});
})
5 changes: 4 additions & 1 deletion packages/dashboard/vue.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ module.exports = {
url: '/storage',
icons: [{ src: '/img/icons/android-chrome-192x192.png', sizes: '192x192' }]
}
]
],
workboxOptions: {
skipWaiting: true
}

// configure the workbox plugin
// workboxPluginMode: "InjectManifest",
Expand Down
Loading