-
-
Save yyx990803/8854f8f6a97631576c14b63c8acd8f2e to your computer and use it in GitHub Desktop.
<script setup> | |
import { useQuery, mutate } from 'vue-apollo' | |
import { ref, reactive, watch, nextTick } from 'vue' | |
import { onBeforeRouteLeave } from 'vue-router' | |
// Reusable functions not specific to this component | |
import { useNetworkState } from '@/functions/network' | |
import { usePathUtils } from '@/functions/path' | |
import { resetCwdOnLeave, useCwdUtils } from '@/functions/cwd' | |
// GraphQL | |
import FOLDER_CURRENT from '@/graphql/folder/folderCurrent.gql' | |
import FOLDERS_FAVORITE from '@/graphql/folder/favoriteFolders.gql' | |
import FOLDER_OPEN from '@/graphql/folder/folderOpen.gql' | |
import FOLDER_OPEN_PARENT from '@/graphql/folder/folderOpenParent.gql' | |
import FOLDER_SET_FAVORITE from '@/graphql/folder/folderSetFavorite.gql' | |
import PROJECT_CWD_RESET from '@/graphql/project/projectCwdReset.gql' | |
import FOLDER_CREATE from '@/graphql/folder/folderCreate.gql' | |
// Misc | |
import { isValidMultiName } from '@/util/folders' | |
const SHOW_HIDDEN = 'vue-ui.show-hidden-folders' | |
// Network | |
const { networkState } = useNetworkState() | |
// Folder | |
const { folders, currentFolderData } = useCurrentFolderData(networkState) | |
const folderNavigation = useFolderNavigation({ networkState, currentFolderData }) | |
const { favoriteFolders, toggleFavorite } = useFavoriteFolders(currentFolderData) | |
const { showHiddenFolders } = useHiddenFolders() | |
const createFolder = useCreateFolder(folderNavigation.openFolder) | |
// Current working directory | |
resetCwdOnLeave() | |
const { updateOnCwdChanged } = useCwdUtils() | |
// Utils | |
const { slicePath } = usePathUtils() | |
// Reusable functions specific to this component | |
function useCurrentFolderData (networkState) { | |
const folders = ref(null) | |
const currentFolderData = useQuery({ | |
query: FOLDER_CURRENT, | |
fetchPolicy: 'networkState-only', | |
networkState, | |
async result () { | |
await nextTick() | |
folders.scrollTop = 0 | |
} | |
}, {}) | |
return { | |
folders, | |
currentFolderData | |
} | |
} | |
function useFolderNavigation ({ networkState, currentFolderData }) { | |
// Path editing | |
const pathEditing = reactive({ | |
editingPath: false, | |
editedPath: '', | |
}) | |
// DOM ref | |
const pathInput = ref(null) | |
async function openPathEdit () { | |
pathEditing.editedPath = currentFolderData.path | |
pathEditing.editingPath = true | |
await nextTick() | |
pathInput.focus() | |
} | |
function submitPathEdit () { | |
openFolder(pathEditing.editedPath) | |
} | |
// Folder opening | |
const openFolder = async (path) => { | |
pathEditing.editingPath = false | |
networkState.error = null | |
networkState.loading++ | |
try { | |
await mutate({ | |
mutation: FOLDER_OPEN, | |
variables: { | |
path | |
}, | |
update: (store, { data: { folderOpen } }) => { | |
store.writeQuery({ query: FOLDER_CURRENT, data: { currentFolderData: folderOpen } }) | |
} | |
}) | |
} catch (e) { | |
networkState.error = e | |
} | |
networkState.loading-- | |
} | |
async function openParentFolder () { | |
pathEditing.editingPath = false | |
networkState.error = null | |
networkState.loading++ | |
try { | |
await mutate({ | |
mutation: FOLDER_OPEN_PARENT, | |
update: (store, { data: { folderOpenParent } }) => { | |
store.writeQuery({ query: FOLDER_CURRENT, data: { currentFolderData: folderOpenParent } }) | |
} | |
}) | |
} catch (e) { | |
networkState.error = e | |
} | |
networkState.loading-- | |
} | |
// Refresh | |
function refreshFolder () { | |
openFolder(currentFolderData.path) | |
} | |
return { | |
pathInput, | |
pathEditing, | |
openPathEdit, | |
submitPathEdit, | |
openFolder, | |
openParentFolder, | |
refreshFolder | |
} | |
} | |
function useFavoriteFolders (currentFolderData) { | |
const favoriteFolders = useQuery(FOLDERS_FAVORITE, []) | |
async function toggleFavorite () { | |
await mutate({ | |
mutation: FOLDER_SET_FAVORITE, | |
variables: { | |
path: currentFolderData.path, | |
favorite: !currentFolderData.favorite | |
}, | |
update: (store, { data: { folderSetFavorite } }) => { | |
store.writeQuery({ query: FOLDER_CURRENT, data: { currentFolderData: folderSetFavorite } }) | |
let data = store.readQuery({ query: FOLDERS_FAVORITE }) | |
// TODO this is a workaround | |
// See: https://github.com/apollographql/apollo-client/issues/4031#issuecomment-433668473 | |
data = { | |
favoriteFolders: data.favoriteFolders.slice() | |
} | |
if (folderSetFavorite.favorite) { | |
data.favoriteFolders.push(folderSetFavorite) | |
} else { | |
const index = data.favoriteFolders.findIndex( | |
f => f.path === folderSetFavorite.path | |
) | |
index !== -1 && data.favoriteFolders.splice(index, 1) | |
} | |
store.writeQuery({ query: FOLDERS_FAVORITE, data }) | |
} | |
}) | |
} | |
return { | |
favoriteFolders, | |
toggleFavorite | |
} | |
} | |
function useHiddenFolders () { | |
const showHiddenFolders = ref(localStorage.getItem(SHOW_HIDDEN) === 'true') | |
watch(showHiddenFolders, value => { | |
if (value) { | |
localStorage.setItem(SHOW_HIDDEN, 'true') | |
} else { | |
localStorage.removeItem(SHOW_HIDDEN) | |
} | |
}, { lazy: true }) | |
return { | |
showHiddenFolders | |
} | |
} | |
function useCreateFolder (openFolder) { | |
const showNewFolder = ref(false) | |
const newFolderName = ref('') | |
const newFolderValid = computed(() => isValidMultiName(newFolderName.value)) | |
async function createFolder () { | |
if (!newFolderValid.value) return | |
const result = await mutate({ | |
mutation: FOLDER_CREATE, | |
variables: { | |
name: newFolderName.value | |
} | |
}) | |
openFolder(result.data.folderCreate.path) | |
newFolderName.value = '' | |
showNewFolder.value = false | |
} | |
return { | |
showNewFolder, | |
newFolderName, | |
newFolderValid, | |
createFolder | |
} | |
} | |
</script> | |
<template> | |
...omitted | |
</template> |
Where does state() come from (imported from 'vue', used on line 78)? I can't find anything about it in Vue 2.0 or vue-composition API docs.
It was renamed to reactive
. value
also became ref
.
Is it possible for OP to post their @/functions/network' component along with this example for more understanding? @yyx990803
How to create colored code block ?
@yyx990803 My question is, should these composition functions be in the same file as the component or in separate files like this:
fileExplorer/useCurrentFolderData.js
fileExplorer/useFolderNavigation.js
fileExplorer/useFavoriteFolderes.js
What is the best practice here? It seems that SOLID principles require each module to be responsible for just one thing.
Nothing to preview
For me this looks just awful.
If you will force us (the community) to use CompA over OptA we will move to React because there will be no more differences. We have created over 50 complex projects in Vue/Nuxt and I need to say that all employees just loved to work on them because of the OptA approach (all my employees have React background).
Maybe I'm a dinosaur with 19 years of experience, but I have to say it - I returned to programming after many years after a burnout because programming in Vue with OptA is pure pleasure.
Please don't change that.
For me this looks just awful.
If you will force us (the community) to use CompA over OptA we will move to React because there will be no more differences. We have created over 50 complex projects in Vue/Nuxt and I need to say that all employees just loved to work on them because of the OptA approach (all my employees have React background).
Maybe I'm a dinosaur with 19 years of experience, but I have to say it - I returned to programming after many years after a burnout because programming in Vue with OptA is pure pleasure.
Please don't change that.
@rafal-ksiazek-rmtcfm-com I strongly disagree with you. Have you created a large-scale app with Vue options API? Code organization and reuse becomes a real nightmare. In contrast, with the new composition API, the code is much cleaner, better organized, more readable, and more maintainable. The Composition API is a pleasure to work with if you value things like clean code and best coding practices.
@rafal-ksiazek-rmtcfm-com I strongly disagree with you. Have you created a large-scale app with Vue options API? Code organization and reuse becomes a real nightmare. In contrast, with the new composition API, the code is much cleaner, better organized, more readable, and more maintainable. The Composition API is a pleasure to work with if you value things like clean code and best coding practices.
@martinszeltins Yes, we have. Few of our apps are the #1 in some countries. I understand that CompA has advantages over OptA and the whole process of development of VueJS framework is much better (much easier to write unit tests). From architectural point of view you are right.
But please don't remove the OptA and don't force us to use CompA. We are using it in rare cases. The reason we move from React to Vue was the simplicity and understandability of the code.
And the code I see on the top of this page is again awful. For me, it's like stepping back in time 15 years.
But please don't remove the OptA and don't force us to use CompA. We are using it in rare cases. The reason we move from React to Vue was the simplicity and understandability of the code.
And the code I see on the top of this page is again awful. For me, it's like stepping back in time 15 years.
@rafal-ksiazek-rmtcfm-com The Options API is not being removed, it will stay and co-exist with the Composition API. However, I do not see anything awful about the code you are referring to. The only thing I would improve would be to move each composable into its own file / module. What don't you like about this code? It nicely follows the SOLID principles for clean code, which is the right direction.
@El-Mehdi-Abdi it is not currently available, but you can track the issue for it here: vuejs/apollo#687