@@ -3,42 +3,56 @@ import LoadingSpinner from '@/components/shared/LoadingSpinner.vue'
33import SvgButton from ' @/components/SvgButton.vue'
44import SvgIcon from ' @/components/SvgIcon.vue'
55import { useBackend } from ' @/composables/backend'
6+ import { injectBackend } from ' @/providers/backend'
67import type { ToValue } from ' @/util/reactivity'
8+ import { useToast } from ' @/util/toast'
79import type {
10+ DatalinkAsset ,
11+ DatalinkId ,
812 DirectoryAsset ,
913 DirectoryId ,
1014 FileAsset ,
1115 FileId ,
1216} from ' enso-common/src/services/Backend'
13- import Backend , { assetIsDirectory , assetIsFile } from ' enso-common/src/services/Backend'
17+ import Backend , {
18+ assetIsDatalink ,
19+ assetIsDirectory ,
20+ assetIsFile ,
21+ } from ' enso-common/src/services/Backend'
1422import { computed , ref , toValue , watch } from ' vue'
23+ import { Err , Ok , Result } from ' ydoc-shared/util/data/result'
1524
1625const emit = defineEmits <{
1726 pathSelected: [path : string ]
1827}>()
1928
20- const { query, ensureQueryData } = useBackend (' remote' )
29+ const { query, fetch, ensureQueryData } = useBackend (' remote' )
30+ const { remote : backend } = injectBackend ()
31+
32+ const errorToast = useToast .error ()
2133
2234// === Current Directory ===
2335
2436interface Directory {
25- id: DirectoryId | null
37+ id: DirectoryId
2638 title: string
2739}
2840
29- const directoryStack = ref <Directory []>([
30- {
31- id: null ,
32- title: ' Cloud' ,
33- },
34- ])
35- const currentDirectory = computed (() => directoryStack .value [directoryStack .value .length - 1 ]! )
3641const currentUser = query (' usersMe' , [])
37- const currentPath = computed (
38- () =>
39- currentUser .data .value &&
40- ` enso://Users/${currentUser .data .value .name }${Array .from (directoryStack .value .slice (1 ), (frame ) => ' /' + frame .title ).join ()} ` ,
41- )
42+ const currentOrganization = query (' getOrganization' , [])
43+ const directoryStack = ref <Directory []>([])
44+ const isDirectoryStackInitializing = computed (() => directoryStack .value .length === 0 )
45+ const currentDirectory = computed (() => directoryStack .value [directoryStack .value .length - 1 ])
46+
47+ const currentPath = computed (() => {
48+ if (! currentUser .data .value ) return
49+ let root = backend ?.rootPath (currentUser .data .value ) ?? ' enso://'
50+ if (! root .endsWith (' /' )) root += ' /'
51+ return ` ${root }${directoryStack .value
52+ .slice (1 )
53+ .map ((dir ) => ` ${dir .title }/ ` )
54+ .join (' ' )} `
55+ })
4256
4357// === Directory Contents ===
4458
@@ -65,17 +79,19 @@ const { isPending, isError, data, error } = query(
6579)
6680const compareTitle = (a : { title: string }, b : { title: string }) => a .title .localeCompare (b .title )
6781const directories = computed (
68- () => data .value && data .value .filter < DirectoryAsset >( assetIsDirectory ).sort (compareTitle ),
82+ () => data .value && data .value .filter (( asset ) => assetIsDirectory ( asset ) ).sort (compareTitle ),
6983)
7084const files = computed (
71- () => data .value && data .value .filter <FileAsset >(assetIsFile ).sort (compareTitle ),
85+ () =>
86+ data .value &&
87+ data .value .filter ((asset ) => assetIsFile (asset ) || assetIsDatalink (asset )).sort (compareTitle ),
7288)
7389const isEmpty = computed (() => directories .value ?.length === 0 && files .value ?.length === 0 )
7490
7591// === Selected File ===
7692
7793interface File {
78- id: FileId
94+ id: FileId | DatalinkId
7995 title: string
8096}
8197
@@ -97,16 +113,27 @@ function enterDir(dir: DirectoryAsset) {
97113 directoryStack .value .push (dir )
98114}
99115
116+ class DirNotFoundError {
117+ constructor (public dirName : string ) {}
118+
119+ toString() {
120+ return ` Directory "${this .dirName }" not found `
121+ }
122+ }
123+
100124function popTo(index : number ) {
101125 directoryStack .value .splice (index + 1 )
102126}
103127
104- function chooseFile(file : FileAsset ) {
128+ function chooseFile(file : FileAsset | DatalinkAsset ) {
105129 selectedFile .value = file
106130}
107131
108132const isBusy = computed (
109- () => isPending .value || (selectedFile .value && currentUser .isPending .value ),
133+ () =>
134+ isDirectoryStackInitializing .value ||
135+ isPending .value ||
136+ (selectedFile .value && currentUser .isPending .value ),
110137)
111138
112139const anyError = computed (() =>
@@ -117,12 +144,44 @@ const anyError = computed(() =>
117144
118145const selectedFilePath = computed (
119146 () =>
120- selectedFile .value && currentPath .value && ` ${currentPath .value }/ ${selectedFile .value .title } ` ,
147+ selectedFile .value && currentPath .value && ` ${currentPath .value }${selectedFile .value .title } ` ,
121148)
122149
123150watch (selectedFilePath , (path ) => {
124151 if (path ) emit (' pathSelected' , path )
125152})
153+
154+ // === Initialization ===
155+
156+ async function enterDirByName(name : string , stack : Directory []): Promise <Result > {
157+ const currentDir = stack [stack .length - 1 ]
158+ if (currentDir == null ) return Err (' Stack is empty' )
159+ const content = await fetch (' listDirectory' , listDirectoryArgs (currentDir ))
160+ const nextDir = content .find (
161+ (asset ): asset is DirectoryAsset => assetIsDirectory (asset ) && asset .title === name ,
162+ )
163+ if (! nextDir ) return Err (new DirNotFoundError (name ))
164+ stack .push (nextDir )
165+ return Ok ()
166+ }
167+
168+ Promise .all ([currentUser .promise .value , currentOrganization .promise .value ]).then (
169+ async ([user , organization ]) => {
170+ if (! user ) {
171+ errorToast .show (' Cannot load file list: not logged in.' )
172+ return
173+ }
174+ const rootDirectoryId =
175+ backend ?.rootDirectoryId (user , organization , null ) ?? user .rootDirectoryId
176+ const stack = [{ id: rootDirectoryId , title: ' Cloud' }]
177+ if (rootDirectoryId != user .rootDirectoryId ) {
178+ let result = await enterDirByName (' Users' , stack )
179+ result = result .ok ? await enterDirByName (user .name , stack ) : result
180+ if (! result .ok ) errorToast .reportError (result .error , ' Cannot enter home directory' )
181+ }
182+ directoryStack .value = stack
183+ },
184+ )
126185 </script >
127186
128187<template >
@@ -143,7 +202,7 @@ watch(selectedFilePath, (path) => {
143202 <div v-if =" isBusy" class =" centerContent contents" ><LoadingSpinner /></div >
144203 <div v-else-if =" anyError" class =" centerContent contents" >Error: {{ anyError }}</div >
145204 <div v-else-if =" isEmpty" class =" centerContent contents" >Directory is empty</div >
146- <div v-else :key =" currentDirectory.id ?? 'root'" class =" listing contents" >
205+ <div v-else :key =" currentDirectory? .id ?? 'root'" class =" listing contents" >
147206 <TransitionGroup >
148207 <div v-for =" entry in directories" :key =" entry.id" >
149208 <SvgButton :label =" entry.title" name =" folder" class =" entry" @click =" enterDir(entry)" />
0 commit comments