feat: add bookmark
This commit is contained in:
parent
2bf0926824
commit
a548aa7e75
5
frontend/src/api/context.js
Normal file
5
frontend/src/api/context.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { fetchJSON } from './utils'
|
||||||
|
|
||||||
|
export async function get () {
|
||||||
|
return await fetchJSON(`/api/context`, {})
|
||||||
|
}
|
||||||
@ -116,6 +116,18 @@ export async function post (url, content = '', overwrite = false, onupload) {
|
|||||||
}).finally(() => { window.onbeforeunload = null })
|
}).finally(() => { window.onbeforeunload = null })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function bookmark (items) {
|
||||||
|
let promises = []
|
||||||
|
|
||||||
|
for (let item of items) {
|
||||||
|
const path = removePrefix(item.path)
|
||||||
|
const url = `${path}?action=${item.bookmarked ? 'bookmark' : 'remove-bookmark'}`
|
||||||
|
promises.push(resourceAction(url, 'PATCH'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises)
|
||||||
|
}
|
||||||
|
|
||||||
function moveCopy (items, copy = false) {
|
function moveCopy (items, copy = false) {
|
||||||
let promises = []
|
let promises = []
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import * as files from './files'
|
|||||||
import * as share from './share'
|
import * as share from './share'
|
||||||
import * as users from './users'
|
import * as users from './users'
|
||||||
import * as settings from './settings'
|
import * as settings from './settings'
|
||||||
|
import * as context from './context'
|
||||||
import search from './search'
|
import search from './search'
|
||||||
import commands from './commands'
|
import commands from './commands'
|
||||||
|
|
||||||
@ -11,5 +12,6 @@ export {
|
|||||||
users,
|
users,
|
||||||
settings,
|
settings,
|
||||||
commands,
|
commands,
|
||||||
search
|
search,
|
||||||
|
context
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,7 @@
|
|||||||
<switch-button v-show="isListing"></switch-button>
|
<switch-button v-show="isListing"></switch-button>
|
||||||
<download-button v-show="showDownloadButton"></download-button>
|
<download-button v-show="showDownloadButton"></download-button>
|
||||||
<upload-button v-show="showUpload"></upload-button>
|
<upload-button v-show="showUpload"></upload-button>
|
||||||
|
<bookmark-button v-show="showBookmarkButton"></bookmark-button>
|
||||||
<info-button v-show="isFiles"></info-button>
|
<info-button v-show="isFiles"></info-button>
|
||||||
|
|
||||||
<button v-show="isListing" @click="openSelect" :aria-label="$t('buttons.selectMultiple')" :title="$t('buttons.selectMultiple')" class="action">
|
<button v-show="isListing" @click="openSelect" :aria-label="$t('buttons.selectMultiple')" :title="$t('buttons.selectMultiple')" class="action">
|
||||||
@ -71,6 +72,7 @@ import MoveButton from './buttons/Move'
|
|||||||
import CopyButton from './buttons/Copy'
|
import CopyButton from './buttons/Copy'
|
||||||
import ShareButton from './buttons/Share'
|
import ShareButton from './buttons/Share'
|
||||||
import ShellButton from './buttons/Shell'
|
import ShellButton from './buttons/Shell'
|
||||||
|
import BookmarkButton from './buttons/Bookmark'
|
||||||
import {mapGetters, mapState} from 'vuex'
|
import {mapGetters, mapState} from 'vuex'
|
||||||
import { logoURL } from '@/utils/constants'
|
import { logoURL } from '@/utils/constants'
|
||||||
import * as api from '@/api'
|
import * as api from '@/api'
|
||||||
@ -89,7 +91,8 @@ export default {
|
|||||||
UploadButton,
|
UploadButton,
|
||||||
SwitchButton,
|
SwitchButton,
|
||||||
MoveButton,
|
MoveButton,
|
||||||
ShellButton
|
ShellButton,
|
||||||
|
BookmarkButton
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
@ -145,6 +148,9 @@ export default {
|
|||||||
? (this.selectedCount === 1 && this.user.perm.rename)
|
? (this.selectedCount === 1 && this.user.perm.rename)
|
||||||
: this.user.perm.rename)
|
: this.user.perm.rename)
|
||||||
},
|
},
|
||||||
|
showBookmarkButton () {
|
||||||
|
return this.isFiles && this.isListing
|
||||||
|
},
|
||||||
showShareButton () {
|
showShareButton () {
|
||||||
return this.isFiles && (this.isListing
|
return this.isFiles && (this.isListing
|
||||||
? (this.selectedCount === 1 && this.user.perm.share)
|
? (this.selectedCount === 1 && this.user.perm.share)
|
||||||
|
|||||||
@ -6,7 +6,14 @@
|
|||||||
<span>{{ $t('sidebar.myFiles') }}</span>
|
<span>{{ $t('sidebar.myFiles') }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<div v-if="user.perm.create">
|
<div>
|
||||||
|
<router-link v-for="(bookmark) in bookmarks" :to="'/files'+bookmark.path" class="action" :aria-label="bookmark.name" :title="bookmark.name">
|
||||||
|
<i class="material-icons">folder</i>
|
||||||
|
<span>{{ bookmark.name }}</span>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="user.perm.create" v-show="showNew">
|
||||||
<button @click="$store.commit('showHover', 'newDir')" class="action" :aria-label="$t('sidebar.newFolder')" :title="$t('sidebar.newFolder')">
|
<button @click="$store.commit('showHover', 'newDir')" class="action" :aria-label="$t('sidebar.newFolder')" :title="$t('sidebar.newFolder')">
|
||||||
<i class="material-icons">create_new_folder</i>
|
<i class="material-icons">create_new_folder</i>
|
||||||
<span>{{ $t('sidebar.newFolder') }}</span>
|
<span>{{ $t('sidebar.newFolder') }}</span>
|
||||||
@ -46,7 +53,7 @@
|
|||||||
<span>
|
<span>
|
||||||
<span v-if="disableExternal">File Browser</span>
|
<span v-if="disableExternal">File Browser</span>
|
||||||
<a v-else rel="noopener noreferrer" target="_blank" href="https://github.com/filebrowser/filebrowser">File Browser</a>
|
<a v-else rel="noopener noreferrer" target="_blank" href="https://github.com/filebrowser/filebrowser">File Browser</a>
|
||||||
<span> {{ version }}</span>
|
<span> v{{ version }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span><a @click="help">{{ $t('sidebar.help') }}</a></span>
|
<span><a @click="help">{{ $t('sidebar.help') }}</a></span>
|
||||||
</p>
|
</p>
|
||||||
@ -62,10 +69,18 @@ export default {
|
|||||||
name: 'sidebar',
|
name: 'sidebar',
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'user' ]),
|
...mapState([ 'user' ]),
|
||||||
...mapGetters([ 'isLogged' ]),
|
...mapGetters([
|
||||||
|
'isLogged',
|
||||||
|
'bookmarks',
|
||||||
|
'isFiles',
|
||||||
|
'isListing'
|
||||||
|
]),
|
||||||
active () {
|
active () {
|
||||||
return this.$store.state.show === 'sidebar'
|
return this.$store.state.show === 'sidebar'
|
||||||
},
|
},
|
||||||
|
showNew() {
|
||||||
|
return this.isFiles && this.isListing
|
||||||
|
},
|
||||||
signup: () => signup,
|
signup: () => signup,
|
||||||
version: () => version,
|
version: () => version,
|
||||||
disableExternal: () => disableExternal,
|
disableExternal: () => disableExternal,
|
||||||
|
|||||||
43
frontend/src/components/buttons/Bookmark.vue
Normal file
43
frontend/src/components/buttons/Bookmark.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<button @click="toggle" :aria-label="$t('buttons.bookmark')" :title="$t('buttons.bookmark')" class="action" id="bookmark-button">
|
||||||
|
<i class="material-icons">{{ icon }}</i>
|
||||||
|
<span>{{ $t('buttons.bookmark') }}</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState, mapMutations } from 'vuex'
|
||||||
|
import { files as filesApi, context as contextApi } from '@/api'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'bookmark-button',
|
||||||
|
computed: {
|
||||||
|
...mapState(['req']),
|
||||||
|
icon: function () {
|
||||||
|
if (this.req.bookmarked) return 'bookmark'
|
||||||
|
return 'bookmark_border'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations([ 'updateBookmark', 'closeHovers' ]),
|
||||||
|
toggle: async function () {
|
||||||
|
this.closeHovers()
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
path: this.req.path,
|
||||||
|
bookmarked: (this.icon === 'bookmark_border')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await filesApi.bookmark([data])
|
||||||
|
this.updateBookmark(data)
|
||||||
|
|
||||||
|
const ctx = await contextApi.get()
|
||||||
|
this.$store.commit('updateContext', ctx)
|
||||||
|
} catch (e) {
|
||||||
|
this.$showError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -32,7 +32,8 @@
|
|||||||
"toggleSidebar": "Toggle sidebar",
|
"toggleSidebar": "Toggle sidebar",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"permalink": "Get Permanent Link"
|
"permalink": "Get Permanent Link",
|
||||||
|
"bookmark": "Bookmark"
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"linkCopied": "Link copied!"
|
"linkCopied": "Link copied!"
|
||||||
|
|||||||
@ -3,7 +3,8 @@ const getters = {
|
|||||||
isFiles: state => !state.loading && state.route.name === 'Files',
|
isFiles: state => !state.loading && state.route.name === 'Files',
|
||||||
isListing: (state, getters) => getters.isFiles && state.req.isDir,
|
isListing: (state, getters) => getters.isFiles && state.req.isDir,
|
||||||
isEditor: (state, getters) => getters.isFiles && (state.req.type === 'text' || state.req.type === 'textImmutable'),
|
isEditor: (state, getters) => getters.isFiles && (state.req.type === 'text' || state.req.type === 'textImmutable'),
|
||||||
selectedCount: state => state.selected.length
|
selectedCount: state => state.selected.length,
|
||||||
|
bookmarks: state => state.context.bookmarks
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getters
|
export default getters
|
||||||
|
|||||||
@ -8,6 +8,7 @@ Vue.use(Vuex)
|
|||||||
const state = {
|
const state = {
|
||||||
user: null,
|
user: null,
|
||||||
req: {},
|
req: {},
|
||||||
|
context: {},
|
||||||
oldReq: {},
|
oldReq: {},
|
||||||
clipboard: {
|
clipboard: {
|
||||||
key: '',
|
key: '',
|
||||||
|
|||||||
@ -71,10 +71,18 @@ const mutations = {
|
|||||||
state.user[field] = value[field]
|
state.user[field] = value[field]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updateBookmark: (state, value) => {
|
||||||
|
if (value.path === state.req.path) {
|
||||||
|
state.req.bookmarked = value.bookmarked
|
||||||
|
}
|
||||||
|
},
|
||||||
updateRequest: (state, value) => {
|
updateRequest: (state, value) => {
|
||||||
state.oldReq = state.req
|
state.oldReq = state.req
|
||||||
state.req = value
|
state.req = value
|
||||||
},
|
},
|
||||||
|
updateContext: (state, value) => {
|
||||||
|
state.context = value
|
||||||
|
},
|
||||||
updateClipboard: (state, value) => {
|
updateClipboard: (state, value) => {
|
||||||
state.clipboard.key = value.key
|
state.clipboard.key = value.key
|
||||||
state.clipboard.items = value.items
|
state.clipboard.items = value.items
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import Sidebar from '@/components/Sidebar'
|
|||||||
import Prompts from '@/components/prompts/Prompts'
|
import Prompts from '@/components/prompts/Prompts'
|
||||||
import SiteHeader from '@/components/Header'
|
import SiteHeader from '@/components/Header'
|
||||||
import Shell from '@/components/Shell'
|
import Shell from '@/components/Shell'
|
||||||
|
import { context as api } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'layout',
|
name: 'layout',
|
||||||
@ -38,6 +39,14 @@ export default {
|
|||||||
this.$store.commit('multiple', false)
|
this.$store.commit('multiple', false)
|
||||||
if (this.$store.state.show !== 'success') this.$store.commit('closeHovers')
|
if (this.$store.state.show !== 'success') this.$store.commit('closeHovers')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
try {
|
||||||
|
const res = await api.get()
|
||||||
|
this.$store.commit('updateContext', res)
|
||||||
|
} catch (e) {
|
||||||
|
this.$showError(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user