mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-31 02:17:01 -04:00 
			
		
		
		
	* Update: `pages/items/_id` toast messages * Update: account modal strings * Update: audio file data modal strings * Update: sleep timer set string * Update: loading indicator string * Update: lazy book card strings * Reorder keys * Fix: syntax error in LazyBookCard * Fix: json ordering * Fix: fix double message definition * Update: login form toast strings * Update: batch delete toast * Update: collection add toast messages * Replace: toasts in BookShelfToolbar * Update: playlist edit toasts * Update: Details tab * Add: title required string * Update: ereader toasts * Update: author toasts, title and name required toasts * Clean up "no updates" strings * Change: slug strings * Update: cover modal toasts * Change: cancel encode toasts * Change: failed to share toasts * Simplify: "renameFail" and "removeFail" toasts * Fix: ordering * Change: chapters remove toast * Update: notification strings * Revert: loading indicator (error in browser) * Update: collectionBooksTable toast * Update: "failed to get" strings * Update: backup strings * Update: custom provider strings * Update: sessions strings * Update: email strings * Update sort display translation strings, update podcast episode queue strings to use translation * Fix loading indicator please wait translation * Consolidate translations and reduce number of toasts --------- Co-authored-by: advplyr <advplyr@protonmail.com>
		
			
				
	
	
		
			203 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <div class="w-full h-full bg-bg absolute top-0 left-0 px-4 py-4 z-10">
 | |
|     <div class="flex items-center py-1 mb-2">
 | |
|       <span class="material-symbols text-3xl cursor-pointer hover:text-gray-300" @click="$emit('back')">arrow_back</span>
 | |
|       <p class="px-4 text-xl">{{ $strings.HeaderChooseAFolder }}</p>
 | |
|     </div>
 | |
|     <div v-if="rootDirs.length" class="w-full bg-primary bg-opacity-70 py-1 px-4 mb-2">
 | |
|       <p class="font-mono truncate">{{ selectedPath || '/' }}</p>
 | |
|     </div>
 | |
|     <div v-if="rootDirs.length" class="relative flex bg-primary bg-opacity-50 p-4 folder-container">
 | |
|       <div class="w-1/2 border-r border-bg h-full overflow-y-auto">
 | |
|         <div v-if="level > 0" class="w-full p-1 cursor-pointer flex items-center hover:bg-white/10" @click="goBack">
 | |
|           <span class="material-symbols fill bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
 | |
|           <p class="text-base font-mono px-2">..</p>
 | |
|         </div>
 | |
|         <div v-for="dir in _directories" :key="dir.path" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200 hover:bg-white/10" :class="dir.className" @click="selectDir(dir)">
 | |
|           <span class="material-symbols fill bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
 | |
|           <p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
 | |
|           <span v-if="dir.path === selectedPath" class="material-symbols" style="font-size: 1.1rem">arrow_right</span>
 | |
|         </div>
 | |
|       </div>
 | |
|       <div class="w-1/2 h-full overflow-y-auto">
 | |
|         <div v-for="dir in _subdirs" :key="dir.path" :class="dir.className" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200 hover:bg-white/10" @click="selectSubDir(dir)">
 | |
|           <span class="material-symbols fill bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
 | |
|           <p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
 | |
|         </div>
 | |
|       </div>
 | |
|       <div v-if="loadingDirs" class="absolute inset-0 w-full h-full flex items-center justify-center bg-black/10">
 | |
|         <ui-loading-indicator />
 | |
|       </div>
 | |
|     </div>
 | |
|     <div v-else-if="initialLoad" class="py-12 text-center">
 | |
|       <p>{{ $strings.MessageLoadingFolders }}</p>
 | |
|     </div>
 | |
|     <div v-else class="py-12 text-center max-w-sm mx-auto">
 | |
|       <p class="text-lg mb-2">{{ $strings.MessageNoFoldersAvailable }}</p>
 | |
|       <p class="text-gray-300 mb-2">{{ $strings.NoteFolderPicker }}</p>
 | |
|     </div>
 | |
| 
 | |
|     <div class="w-full py-2">
 | |
|       <ui-btn :disabled="!selectedPath" color="primary" class="w-full mt-2" @click="selectFolder">{{ $strings.ButtonSelectFolderPath }}</ui-btn>
 | |
|     </div>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| export default {
 | |
|   props: {
 | |
|     paths: {
 | |
|       type: Array,
 | |
|       default: () => []
 | |
|     }
 | |
|   },
 | |
|   data() {
 | |
|     return {
 | |
|       initialLoad: false,
 | |
|       loadingDirs: false,
 | |
|       isPosix: true,
 | |
|       rootDirs: [],
 | |
|       directories: [],
 | |
|       selectedPath: '',
 | |
|       subdirs: [],
 | |
|       level: 0,
 | |
|       currentDir: null,
 | |
|       previousDir: null
 | |
|     }
 | |
|   },
 | |
|   computed: {
 | |
|     _directories() {
 | |
|       return this.directories.map((d) => {
 | |
|         var isUsed = !!this.paths.find((path) => path.endsWith(d.path))
 | |
|         var isSelected = d.path === this.selectedPath
 | |
|         var classes = []
 | |
|         if (isSelected) classes.push('dir-selected')
 | |
|         if (isUsed) classes.push('dir-used')
 | |
|         return {
 | |
|           isUsed,
 | |
|           isSelected,
 | |
|           className: classes.join(' '),
 | |
|           ...d
 | |
|         }
 | |
|       })
 | |
|     },
 | |
|     _subdirs() {
 | |
|       return this.subdirs.map((d) => {
 | |
|         var isUsed = !!this.paths.find((path) => path.endsWith(d.path))
 | |
|         var classes = []
 | |
|         if (isUsed) classes.push('dir-used')
 | |
|         return {
 | |
|           isUsed,
 | |
|           className: classes.join(' '),
 | |
|           ...d
 | |
|         }
 | |
|       })
 | |
|     }
 | |
|   },
 | |
|   methods: {
 | |
|     async goBack() {
 | |
|       let selPath = this.selectedPath.replace(/^\//, '')
 | |
|       var splitPaths = selPath.split('/')
 | |
| 
 | |
|       let previousPath = ''
 | |
|       let lookupPath = ''
 | |
| 
 | |
|       if (splitPaths.length > 2) {
 | |
|         lookupPath = splitPaths.slice(0, -2).join('/')
 | |
|       }
 | |
|       previousPath = splitPaths.slice(0, -1).join('/')
 | |
| 
 | |
|       if (!this.isPosix) {
 | |
|         // For windows drives add a trailing slash. e.g. C:/
 | |
|         if (!this.isPosix && lookupPath.endsWith(':')) {
 | |
|           lookupPath += '/'
 | |
|         }
 | |
|         if (!this.isPosix && previousPath.endsWith(':')) {
 | |
|           previousPath += '/'
 | |
|         }
 | |
|       } else {
 | |
|         // Add leading slash
 | |
|         if (previousPath) previousPath = '/' + previousPath
 | |
|         if (lookupPath) lookupPath = '/' + lookupPath
 | |
|       }
 | |
| 
 | |
|       this.level--
 | |
|       this.subdirs = this.directories
 | |
|       this.selectedPath = previousPath
 | |
|       this.directories = await this.fetchDirs(lookupPath, this.level)
 | |
|     },
 | |
|     async selectDir(dir) {
 | |
|       if (dir.isUsed) return
 | |
|       this.selectedPath = dir.path
 | |
|       this.level = dir.level
 | |
|       this.subdirs = await this.fetchDirs(dir.path, dir.level + 1)
 | |
|     },
 | |
|     async selectSubDir(dir) {
 | |
|       if (dir.isUsed) return
 | |
|       this.selectedPath = dir.path
 | |
|       this.level = dir.level
 | |
|       this.directories = this.subdirs
 | |
|       this.subdirs = await this.fetchDirs(dir.path, dir.level + 1)
 | |
|     },
 | |
|     selectFolder() {
 | |
|       if (!this.selectedPath) {
 | |
|         console.error('No Selected path')
 | |
|         return
 | |
|       }
 | |
|       if (this.paths.find((p) => p.startsWith(this.selectedPath))) {
 | |
|         this.$toast.error(`Oops, you cannot add a parent directory of a folder already added`)
 | |
|         return
 | |
|       }
 | |
|       this.$emit('select', this.selectedPath)
 | |
|       this.selectedPath = ''
 | |
|     },
 | |
|     fetchDirs(path, level) {
 | |
|       this.loadingDirs = true
 | |
|       return this.$axios
 | |
|         .$get(`/api/filesystem?path=${path}&level=${level}`)
 | |
|         .then((data) => {
 | |
|           console.log('Fetched directories', data.directories)
 | |
|           this.isPosix = !!data.posix
 | |
|           return data.directories
 | |
|         })
 | |
|         .catch((error) => {
 | |
|           console.error('Failed to get filesystem paths', error)
 | |
|           this.$toast.error(this.$strings.ToastFailedToLoadData)
 | |
|           return []
 | |
|         })
 | |
|         .finally(() => {
 | |
|           this.loadingDirs = false
 | |
|         })
 | |
|     },
 | |
|     async init() {
 | |
|       this.initialLoad = true
 | |
|       this.rootDirs = await this.fetchDirs('', 0)
 | |
|       this.initialLoad = false
 | |
| 
 | |
|       this.directories = this.rootDirs
 | |
|       this.subdirs = []
 | |
|       this.selectedPath = ''
 | |
|     }
 | |
|   },
 | |
|   mounted() {
 | |
|     this.init()
 | |
|   }
 | |
| }
 | |
| </script>
 | |
| 
 | |
| 
 | |
| 
 | |
| <style>
 | |
| .dir-item.dir-selected {
 | |
|   background-color: rgba(255, 255, 255, 0.1);
 | |
| }
 | |
| .dir-item.dir-used {
 | |
|   background-color: rgba(255, 25, 0, 0.1);
 | |
| }
 | |
| .folder-container {
 | |
|   max-height: calc(100% - 130px);
 | |
|   height: calc(100% - 130px);
 | |
|   min-height: calc(100% - 130px);
 | |
| }
 | |
| </style>
 |