Shared pages refactoring part 2

This commit is contained in:
Čarodej
2022-02-08 12:58:47 +01:00
parent f04c057490
commit 7b0f9a19de
15 changed files with 273 additions and 154 deletions

View File

@@ -72,5 +72,6 @@
"/js/chunks/invitation.js": "/js/chunks/invitation.js",
"/css/tailwind.css": "/css/tailwind.css",
"/css/app.css": "/css/app.css",
"/js/chunks/app-adsense.js": "/js/chunks/app-adsense.js"
"/js/chunks/app-adsense.js": "/js/chunks/app-adsense.js",
"/js/chunks/shared/browser.js": "/js/chunks/shared/browser.js"
}

View File

@@ -3,11 +3,11 @@
<!--Arrow navigation-->
<div v-if="!$isMobile() && files.length > 1" class="">
<div @click.prevent="prev" class="fixed top-1/2 left-0 p-3 cursor-pointer">
<div @click.prevent="prev" class="fixed top-1/2 left-0 p-3 cursor-pointer z-20">
<chevron-left-icon size="20" />
</div>
<div @click.prevent="next" class="fixed top-1/2 right-0 p-3 cursor-pointer">
<div @click.prevent="next" class="fixed top-1/2 right-0 p-3 cursor-pointer z-20">
<chevron-right-icon size="20" />
</div>
</div>
@@ -148,11 +148,13 @@ export default {
})
// Scroll to the selected item
this.$nextTick(() => {
let element = document.getElementById(`group-${this.files[this.currentIndex].data.id}`)
if (this.$isMobile()) {
this.$nextTick(() => {
let element = document.getElementById(`group-${this.files[this.currentIndex].data.id}`)
this.$refs.scrollBox.scrollLeft = element.getBoundingClientRect().left
})
this.$refs.scrollBox.scrollLeft = element.getBoundingClientRect().left
})
}
},
next() {
if (!this.files.length > 1) return

View File

@@ -1,5 +1,5 @@
<template>
<div v-if="currentFile" class="items-center px-3.5 py-4 lg:grid lg:grid-cols-3 lg:py-3">
<div v-if="currentFile" class="items-center px-3.5 py-4 lg:grid lg:grid-cols-3 lg:py-3 select-none">
<div class="flex items-center justify-between lg:w-auto lg:justify-start">
<!--Close icon-->
<div @click="closeFullPreview" class="order-last -m-3 cursor-pointer p-3 lg:order-none">

View File

@@ -0,0 +1,133 @@
<template>
<div class="hidden lg:block">
<div class="flex items-center justify-between py-3">
<NavigationBar />
<div class="flex items-center">
<!--Create button-->
<PopoverWrapper v-if="canEdit">
<ToolbarButton
@click.stop.native="showCreateMenu"
source="cloud-plus"
:action="$t('actions.create')"
/>
<PopoverItem name="desktop-create" side="left">
<OptionGroup :title="$t('Upload')">
<OptionUpload :title="$t('actions.upload')" type="file" />
<OptionUpload :title="$t('actions.upload_folder')" type="folder" />
</OptionGroup>
<OptionGroup :title="$t('Create')">
<Option
@click.stop.native="$createFolder"
:title="$t('actions.create_folder')"
icon="folder-plus"
/>
</OptionGroup>
</PopoverItem>
</PopoverWrapper>
<!--Search bar-->
<SearchBar class="ml-5 hidden lg:block xl:ml-8" />
<!--File Controls-->
<div class="ml-5 flex items-center xl:ml-8">
<!--Action buttons-->
<div v-if="canEdit && !$isMobile()" class="flex items-center">
<ToolbarButton
@click.native="$moveFileOrFolder(clipboard[0])"
:class="{
'is-inactive': ! canManipulate,
}"
source="move"
:action="$t('actions.move')"
/>
<ToolbarButton
@click.native="$deleteFileOrFolder(clipboard[0])"
:class="{
'is-inactive': ! canManipulate,
}"
source="trash"
:action="$t('actions.delete')"
/>
</div>
</div>
<!--View Controls-->
<div class="ml-5 flex items-center xl:ml-8">
<PopoverWrapper>
<ToolbarButton
@click.stop.native="showSortingMenu"
source="preview-sorting"
:action="$t('actions.sorting_view')"
/>
<PopoverItem name="desktop-sorting" side="left">
<FileSortingOptions />
</PopoverItem>
</PopoverWrapper>
<ToolbarButton
@click.native="$store.dispatch('fileInfoToggle')"
:action="$t('actions.info_panel')"
source="info"
/>
</div>
</div>
</div>
<UploadProgress />
</div>
</template>
<script>
import PopoverWrapper from '../Desktop/PopoverWrapper'
import FileSortingOptions from './FileSortingOptions'
import PopoverItem from '../Desktop/PopoverItem'
import UploadProgress from './UploadProgress'
import NavigationBar from './NavigationBar'
import ToolbarButton from './ToolbarButton'
import OptionUpload from './OptionUpload'
import OptionGroup from './OptionGroup'
import SearchBar from './SearchBar'
import { events } from '../../bus'
import { mapGetters } from 'vuex'
import Option from './Option'
export default {
name: 'DesktopSharepageToolbar',
components: {
FileSortingOptions,
UploadProgress,
PopoverWrapper,
NavigationBar,
ToolbarButton,
OptionUpload,
OptionGroup,
PopoverItem,
SearchBar,
Option,
},
computed: {
...mapGetters([
'isVisibleNavigationBars',
'currentTeamFolder',
'currentFolder',
'sharedDetail',
'clipboard',
]),
canEdit() {
return this.sharedDetail && this.sharedDetail.data.attributes.permission === 'editor'
},
canManipulate() {
return this.clipboard[0]
}
},
methods: {
showCreateMenu() {
events.$emit('popover:open', 'desktop-create')
},
showSortingMenu() {
events.$emit('popover:open', 'desktop-sorting')
},
},
}
</script>

View File

@@ -236,9 +236,6 @@ export default {
showSortingMenu() {
events.$emit('popover:open', 'desktop-sorting')
},
folderActions() {
events.$emit('context-menu:current-folder', this.currentFolder)
},
},
}
</script>

View File

@@ -1,12 +1,10 @@
<template>
<div class="sticky top-14 z-20 block overflow-x-auto whitespace-nowrap bg-white px-4 pb-3 dark:bg-dark-background lg:hidden">
<!--Show Buttons-->
<div v-if="!isMultiSelectMode" class="mobile-actions">
<slot></slot>
</div>
<slot v-if="!isMultiSelectMode" />
<!-- Multi select mode -->
<div v-if="isMultiSelectMode" class="mobile-actions">
<div v-if="isMultiSelectMode">
<MobileActionButton @click.native="selectAll" icon="check-square">
{{ $t('mobile_selecting.select_all') }}
</MobileActionButton>

View File

@@ -1,5 +1,5 @@
<template>
<div class="hidden h-screen 2xl:w-[360px] w-[320px] shrink-0 overflow-y-auto overflow-x-hidden px-2.5 pt-2 lg:block">
<div class="hidden 2xl:w-[360px] w-[320px] shrink-0 overflow-y-auto overflow-x-hidden px-2.5 pt-2 lg:block">
<!--Is empty clipboard-->
<div v-if="isEmpty" class="flex h-full items-center justify-center">
<div class="text-center">

View File

@@ -9,15 +9,15 @@
<chevron-left-icon
size="17"
:class="{
'-translate-x-3 opacity-0': !isLoadedFolder,
'translate-x-0 opacity-100': isLoadedFolder,
'-translate-x-3 opacity-0': !isLoadedFolder || !isNotHomepage,
'translate-x-0 opacity-100': isLoadedFolder && isNotHomepage,
}"
class="lg:-mt-0.5 mr-2 -ml-1 cursor-pointer align-middle transition-all duration-200"
/>
<!--Folder Title-->
<b
:class="{ '-translate-x-4': !isLoadedFolder }"
:class="{ '-translate-x-4': !isLoadedFolder || !isNotHomepage }"
class="inline-block transform overflow-hidden text-ellipsis whitespace-nowrap align-middle text-sm font-bold transition-all duration-200 max-w-[200px]"
>
{{ $getCurrentLocationName() }}
@@ -26,8 +26,8 @@
<div
@click.stop="showItemActions"
:class="{
'-translate-x-4 opacity-0': !currentFolder,
'translate-x-0 opacity-100': currentFolder,
'-translate-x-4 opacity-0': !currentFolder || !isNotHomepage,
'translate-x-0 opacity-100': currentFolder && isNotHomepage,
}"
class="ml-3 transform rounded-md bg-light-background py-0.5 px-1.5 transition-all duration-200 dark:bg-dark-foreground cursor-pointer"
id="folder-actions"
@@ -56,7 +56,7 @@ export default {
]),
isNotHomepage() {
if (this.$isThisRoute(this.$route, ['Public'])) {
return this.sharedDetail && this.sharedDetail.data.attributes.item_id === this.$route.params.id
return this.sharedDetail && this.sharedDetail.data.attributes.item_id !== this.$route.params.id
}
return this.$route.params.id

View File

@@ -44,7 +44,7 @@
</div>
<!--Show tips-->
<div v-if="isEmptyQuery && !activeFilter" class="relative z-50 px-4 pb-4">
<div v-if="isEmptyQuery && !activeFilter && ! $isThisRoute($route, ['Public'])" class="relative z-50 px-4 pb-4">
<CategoryName>
{{ $t('Suggested Filters') }}
</CategoryName>
@@ -236,7 +236,7 @@ export default {
XIcon,
},
computed: {
...mapGetters(['config', 'user']),
...mapGetters(['config', 'user', 'sharedDetail']),
actionList() {
let adminLocations = [
{
@@ -381,6 +381,20 @@ export default {
value: 'Billing',
},
},
{
title: this.$t('Empty Your Trash'),
action: {
type: 'function',
value: 'empty-trash',
},
},
{
title: this.$t('Log Out'),
action: {
type: 'function',
value: 'log-out',
},
},
]
let createList = [
@@ -415,20 +429,6 @@ export default {
value: 'full-screen-mode',
},
},
{
title: this.$t('Empty Your Trash'),
action: {
type: 'function',
value: 'empty-trash',
},
},
{
title: this.$t('Log Out'),
action: {
type: 'function',
value: 'log-out',
},
},
]
// Available only for apple users
@@ -442,6 +442,12 @@ export default {
})
}
// Return commands for public page
if (this.$isThisRoute(this.$route, ['Public'])) {
return [].concat.apply([], [functionList])
}
// Return commands for logged admin
if (this.user.data.attributes.role === 'admin') {
// Available only for fixed subscription
if (this.config.subscriptionType === 'fixed') {
@@ -468,7 +474,8 @@ export default {
return [].concat.apply([], [functionList, createList, userSettings, fileLocations, adminLocations, adminActions])
}
if (this.user.data.attributes.role === 'user') {
// Return commands for logged user
if (this.user.data.attributes.role === 'user') {
return [].concat.apply([], [functionList, createList, userSettings, fileLocations])
}
},
@@ -616,7 +623,16 @@ export default {
openItem(file) {
// Show folder
if (file.data.type === 'folder') {
if (file.data.attributes.isTeamFolder) {
if (this.$isThisRoute(this.$route, ['Public'])) {
this.$router.push({
name: 'Public',
params: {
token: this.sharedDetail.data.attributes.token,
id: file.data.id,
},
})
} else if (file.data.attributes.isTeamFolder) {
if (file.data.relationships.owner.data.id === this.user.data.id) {
this.$router.push({
name: 'TeamFolders',
@@ -654,15 +670,9 @@ export default {
this.isLoading = true
// Get route
let route = undefined
if (this.$store.getters.sharedDetail) {
let permission = this.$store.getters.sharedDetail.data.attributes.protected ? 'private' : 'public'
route = `/api/browse/search/${permission}/${this.$router.currentRoute.params.token}`
} else {
route = '/api/browse/search'
}
let route = this.$store.getters.sharedDetail
? `/api/browse/search/${this.$router.currentRoute.params.token}`
: '/api/browse/search'
axios
.get(`${route}?filter=${this.activeFilter}`, {

View File

@@ -1,6 +1,7 @@
import i18n from '../../i18n'
import axios from 'axios'
import Vue from 'vue'
import router from "../../router";
const defaultState = {
isVisibleNavigationBars: localStorage.getItem('is_navigation_bars') !== 'false',
@@ -30,7 +31,9 @@ const actions = {
})
// Update user settings
Vue.prototype.$updateText('/user/settings', 'theme_mode', mode)
if (! Vue.prototype.$isThisRoute(router.currentRoute, ['Public'])) {
Vue.prototype.$updateText('/user/settings', 'theme_mode', mode)
}
},
toggleNavigationBars: ({ commit, state }) => {
// Store dark mode into localStorage

View File

@@ -1,28 +1,45 @@
<template>
<ContentSidebar v-if="navigationTree && navigationTree.length >= 1">
<!--Locations-->
<ContentGroup :title="$t('sidebar.locations_title')">
<div class="menu-list-wrapper vertical">
<a class="menu-list-item link" @click="goHome">
<div class="icon">
<home-icon size="17" />
</div>
<div class="label">
{{ $t('Home') }}
</div>
</a>
</div>
</ContentGroup>
<ContentSidebar v-if="(navigationTree && navigationTree.length >= 1) && isVisibleNavigationBars" class="relative lg:!grid">
<!--Full screen button-->
<div @click="$store.dispatch('toggleNavigationBars')" class="absolute top-2.5 right-0 inline-block cursor-pointer p-3 opacity-0 transition-all duration-200 hover:opacity-70">
<chevrons-left-icon size="18" />
</div>
<div class="mb-auto">
<!--Locations-->
<ContentGroup :title="$t('Base')">
<b @click="goHome" class="flex items-center py-2.5 cursor-pointer" :class="{'router-link-active': $route.params.id === sharedDetail.data.attributes.item_id}">
<home-icon size="17" class="vue-feather icon-active mr-2.5" />
<small class="text-active text-xs font-bold">
{{ $t('Home') }}
</small>
</b>
</ContentGroup>
<!--Navigator-->
<ContentGroup :title="$t('sidebar.navigator_title')" can-collapse="true">
<TreeMenuNavigator class="folder-tree" :depth="0" :nodes="folder" v-for="folder in navigationTree" :key="folder.id" />
</ContentGroup>
</div>
<ContentGroup class="mt-auto">
<div @click="$store.dispatch('toggleThemeMode')" :title="$t('dark_mode_toggle')" class="flex items-center cursor-pointer group">
<div class="button-icon inline-block cursor-pointer rounded-xl pr-3">
<sun-icon v-if="isDarkMode" size="14" class="vue-feather group-hover-text-theme" />
<moon-icon v-if="!isDarkMode" size="14" class="vue-feather group-hover-text-theme" />
</div>
<b class="text-xs group-hover-text-theme">
Set {{ isDarkMode ? 'Light' : 'Dark' }} Mode
</b>
</div>
</ContentGroup>
<!--Navigator-->
<ContentGroup :title="$t('sidebar.navigator_title')" class="navigator">
<TreeMenuNavigator class="folder-tree" :depth="0" :nodes="folder" v-for="folder in navigationTree" :key="folder.id" />
</ContentGroup>
</ContentSidebar>
</template>
<script>
import { FolderIcon, HomeIcon, LinkIcon, Trash2Icon, UploadCloudIcon, UserCheckIcon, UsersIcon, XIcon } from 'vue-feather-icons'
import { SunIcon, MoonIcon, ChevronsLeftIcon, FolderIcon, HomeIcon, LinkIcon, Trash2Icon, UploadCloudIcon, UserCheckIcon, UsersIcon, XIcon } from 'vue-feather-icons'
import TreeMenuNavigator from '../../../components/Others/TreeMenuNavigator'
import ContentSidebar from '../../../components/Sidebar/ContentSidebar'
import ContentGroup from '../../../components/Sidebar/ContentGroup'
@@ -35,7 +52,10 @@ export default {
TreeMenuNavigator,
ContentSidebar,
ContentGroup,
SunIcon,
MoonIcon,
UploadCloudIcon,
ChevronsLeftIcon,
UserCheckIcon,
FolderIcon,
Trash2Icon,
@@ -45,7 +65,7 @@ export default {
XIcon,
},
computed: {
...mapGetters(['sharedDetail', 'navigation', 'clipboard', 'config', 'user']),
...mapGetters(['sharedDetail', 'navigation', 'clipboard', 'config', 'user', 'isVisibleNavigationBars', 'isDarkMode']),
favourites() {
return this.user.data.relationships.favourites.data.attributes.folders
},
@@ -113,60 +133,6 @@ export default {
created() {
// Listen for dragstart folder items
events.$on('dragstart', (item) => (this.draggedItem = item))
// Get folder tree
this.$store.dispatch('getFolderTree')
},
}
</script>
<style lang="scss" scoped>
.empty-note {
&.navigator {
padding: 5px 25px 10px;
}
&.favourites {
padding: 5px 23px 10px;
}
}
.navigator {
width: 100%;
overflow-x: auto;
}
@media only screen and (max-width: 1024px) {
.empty-note {
&.navigator {
padding: 5px 20px 10px;
}
&.favourites {
padding: 5px 18px 10px;
}
}
}
// Transition
.folder-item-move {
transition: all 300s ease;
}
.folder-item-enter-active {
transition: all 300ms ease;
}
.folder-item-leave-active {
transition: all 300ms;
}
.folder-item-enter, .folder-item-leave-to /* .list-leave-active below version 2.1.8 */ {
opacity: 0;
transform: translateX(30px);
}
.folder-item-leave-active {
position: absolute;
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<ContentSidebar v-if="isVisibleNavigationBars" class="relative">
<!--Full screen button-->
<div @click="toggleNavigationBars" class="absolute top-2.5 right-0 inline-block cursor-pointer p-3 opacity-0 transition-all duration-200 hover:opacity-70">
<div @click="$store.dispatch('toggleNavigationBars')" class="absolute top-2.5 right-0 inline-block cursor-pointer p-3 opacity-0 transition-all duration-200 hover:opacity-70">
<chevrons-left-icon size="18" />
</div>
@@ -164,9 +164,6 @@ export default {
}
},
methods: {
toggleNavigationBars() {
this.$store.dispatch('toggleNavigationBars')
},
resetData() {
this.$store.commit('SET_CURRENT_TEAM_FOLDER', null)
this.$store.commit('CLIPBOARD_CLEAR')

View File

@@ -30,7 +30,7 @@
<MobileMultiSelectToolbar>
<template v-slot:visitor>
<ToolbarButton @click.native="downloadItem" class="action-btn" source="download" :action="$t('actions.download')" />
<ToolbarButton @click.native="$downloadSelection()" class="action-btn" source="download" :action="$t('actions.download')" />
</template>
<template v-slot:editor>
<ToolbarButton
@@ -47,7 +47,7 @@
:class="{ 'is-inactive': clipboard.length < 1 }"
:action="$t('actions.delete')"
/>
<ToolbarButton @click.native="downloadItem" class="action-btn" source="download" :action="$t('actions.download')" />
<ToolbarButton @click.native="$downloadSelection()" class="action-btn" source="download" :action="$t('actions.download')" />
</template>
</MobileMultiSelectToolbar>
@@ -104,7 +104,7 @@
<MobileActionButton @click.native="$openSpotlight()" icon="search">
{{ $t('Spotlight') }}
</MobileActionButton>
<MobileActionButton @click.native="$enableMultiSelectMode" icon="check-square">
<MobileActionButton @click.native="$enableMultiSelectMode()" icon="check-square">
{{ $t('context_menu.select') }}
</MobileActionButton>
<MobileActionButton @click.native="$showMobileMenu('file-sorting')" icon="preview-sorting">
@@ -127,7 +127,7 @@
</template>
<template v-if="$checkPermission('visitor')">
<h1 class="title">
{{ $t('empty_page.title') }}
{{ $t('There is nothing.') }}
</h1>
</template>
</EmptyFilePage>
@@ -188,6 +188,7 @@ export default {
events.$on('context-menu:show', (event, item) => (this.item = item))
events.$on('mobile-context-menu:show', (item) => (this.item = item))
},
events.$on('context-menu:current-folder', (folder) => (this.item = folder))
},
}
</script>

View File

@@ -26,7 +26,7 @@
@contextmenu.prevent.capture="contextMenu($event, undefined)"
class="transition-transform duration-200 lg:grid lg:flex-grow lg:content-start lg:px-3.5"
>
<DesktopToolbar />
<DesktopSharepageToolbar />
<MobileToolbar />
@@ -61,10 +61,13 @@ import DragUI from '../components/FilesView/DragUI'
import Alert from '../components/FilesView/Alert'
import { mapGetters } from 'vuex'
import { events } from '../bus'
import router from "../router";
import DesktopSharepageToolbar from "../components/FilesView/DesktopSharepageToolbar";
export default {
name: 'Shared',
components: {
DesktopSharepageToolbar,
MobileToolbar,
InfoSidebar,
NavigationSharePanel,
@@ -98,6 +101,9 @@ export default {
// TODO: new scaledown effect
events.$on('mobile-menu:show', () => (this.isScaledDown = true))
events.$on('mobile-menu:hide', () => (this.isScaledDown = false))
this.$store.dispatch('getShareDetail', this.$router.currentRoute.params.token)
.then(() => this.$store.dispatch('getFolderTree'))
},
}
</script>

View File

@@ -32,14 +32,6 @@ class VisitorSearchFilesAndFoldersController extends Controller
$request->input('query')
);
// Search files id db
$searched_files = File::search($query)
->where('user_id', $shared->user_id)
->get();
$searched_folders = Folder::search($query)
->where('user_id', $shared->user_id)
->get();
// Get all children content
$foldersIds = Folder::with('folders:id,parent_id,id,name')
->where('user_id', $shared->user_id)
@@ -49,20 +41,33 @@ class VisitorSearchFilesAndFoldersController extends Controller
// Get accessible folders
$accessible_parent_ids = Arr::flatten([filter_folders_ids($foldersIds), $shared->item_id]);
// Filter files
$files = $searched_files->filter(function ($file) use ($accessible_parent_ids, $shared) {
// Set public urls
$file->setPublicUrl($shared->token);
// Prepare eloquent builder
$folder = new Folder();
$file = new File();
// check if item is in accessible folders
return in_array($file->parent_id, $accessible_parent_ids);
});
// Prepare folders constrain
$folderConstrain = $folder
->where('user_id', $shared->user_id)
->whereIn('id', $accessible_parent_ids);
// Filter folders
$folders = $searched_folders->filter(function ($folder) use ($accessible_parent_ids) {
// check if item is in accessible folders
return in_array($folder->id, $accessible_parent_ids);
});
// Prepare files constrain
$fileConstrain = $file
->where('user_id', $shared->user_id)
->whereIn('parent_id', $accessible_parent_ids);
// Search files and folders
$files = File::search($query)
->constrain($fileConstrain)
->get()
->take(3);
// Map files and set public url for files
$files->map(fn ($file) => $file->setPublicUrl($shared->token));
$folders = Folder::search($query)
->constrain($folderConstrain)
->get()
->take(3);
// Collect folders and files to single array
return [