feat(fe2): updated saved view tabs (#5332)
This commit is contained in:
committed by
GitHub
parent
8dbd342a40
commit
74ebb21594
@@ -25,10 +25,10 @@ const props = defineProps<{
|
||||
isGenericErrorPage?: boolean
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const route = useCurrentRouteTillNavigated()
|
||||
|
||||
const isProjectRoute = computed(() => route.path.match(/\/projects\/[^/]+/))
|
||||
const isWorkspaceRoute = computed(() => route.path.match(/\/workspaces\/[^/]+/))
|
||||
const isProjectRoute = computed(() => route.value.path.match(/\/projects\/[^/]+/))
|
||||
const isWorkspaceRoute = computed(() => route.value.path.match(/\/workspaces\/[^/]+/))
|
||||
|
||||
const finalError = computed(() => formatAppError(props.error))
|
||||
const isNoProjectAccessError = computed(
|
||||
|
||||
@@ -106,10 +106,7 @@ import { useMutationLoading } from '@vue/apollo-composable'
|
||||
import { Search, FolderPlus, Plus, X } from 'lucide-vue-next'
|
||||
import { useSynchronizedCookie } from '~/lib/common/composables/reactiveCookie'
|
||||
import { graphql } from '~/lib/common/generated/gql'
|
||||
import {
|
||||
SavedViewVisibility,
|
||||
WorkspaceSeatType
|
||||
} from '~/lib/common/generated/gql/graphql'
|
||||
import { WorkspaceSeatType } from '~/lib/common/generated/gql/graphql'
|
||||
import { useCreateSavedView } from '~/lib/viewer/composables/savedViews/management'
|
||||
import { useInjectedViewerState } from '~/lib/viewer/composables/setup'
|
||||
import { ViewsType, viewsTypeLabels } from '~/lib/viewer/helpers/savedViews'
|
||||
@@ -147,7 +144,7 @@ const createSavedView = useCreateSavedView()
|
||||
const isLoading = useMutationLoading()
|
||||
const { on, bind, value: search } = useDebouncedTextInput()
|
||||
|
||||
const selectedViewsType = ref<ViewsType>(ViewsType.Personal)
|
||||
const selectedViewsType = ref<ViewsType>(ViewsType.All)
|
||||
const hideViewerSeatDisclaimer = useSynchronizedCookie<boolean>(
|
||||
'hideViewerSeatSavedViewsDisclaimer',
|
||||
{
|
||||
@@ -167,12 +164,7 @@ const isLowerPlan = computed(() => !project.value?.workspace?.planSupportsSavedV
|
||||
|
||||
const onAddView = async () => {
|
||||
if (isLoading.value) return
|
||||
const view = await createSavedView({
|
||||
visibility:
|
||||
selectedViewsType.value === ViewsType.Shared
|
||||
? SavedViewVisibility.Public
|
||||
: undefined
|
||||
})
|
||||
const view = await createSavedView({})
|
||||
if (view) {
|
||||
// Auto-open the group that the view created to
|
||||
openedGroupState.value.set(view.group.id, true)
|
||||
|
||||
@@ -63,18 +63,17 @@ import type { LayoutMenuItem } from '@speckle/ui-components'
|
||||
import { useMutationLoading } from '@vue/apollo-composable'
|
||||
import { Ellipsis, Plus } from 'lucide-vue-next'
|
||||
import { graphql } from '~/lib/common/generated/gql'
|
||||
import {
|
||||
SavedViewVisibility,
|
||||
type UseUpdateSavedViewGroup_SavedViewGroupFragment,
|
||||
type ViewerSavedViewsPanelViewsGroup_ProjectFragment,
|
||||
type ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragment,
|
||||
type ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroupFragment
|
||||
import type {
|
||||
UseUpdateSavedViewGroup_SavedViewGroupFragment,
|
||||
ViewerSavedViewsPanelViewsGroup_ProjectFragment,
|
||||
ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragment,
|
||||
ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroupFragment
|
||||
} from '~/lib/common/generated/gql/graphql'
|
||||
import {
|
||||
useCreateSavedView,
|
||||
useUpdateSavedViewGroup
|
||||
} from '~/lib/viewer/composables/savedViews/management'
|
||||
import { ViewsType } from '~/lib/viewer/helpers/savedViews'
|
||||
import type { ViewsType } from '~/lib/viewer/helpers/savedViews'
|
||||
|
||||
const MenuItems = StringEnum(['Delete', 'Rename'])
|
||||
type MenuItems = StringEnumValues<typeof MenuItems>
|
||||
@@ -182,9 +181,7 @@ const onActionChosen = async (item: LayoutMenuItem<MenuItems>) => {
|
||||
|
||||
const onAddGroupView = async () => {
|
||||
await createView({
|
||||
groupId: props.group.id,
|
||||
visibility:
|
||||
props.viewsType === ViewsType.Shared ? SavedViewVisibility.Public : undefined
|
||||
groupId: props.group.id
|
||||
})
|
||||
open.value = true
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ import type {
|
||||
RouteLocationAsPathGeneric
|
||||
} from '#vue-router'
|
||||
import { buildManualPromise } from '@speckle/shared'
|
||||
import { useScopedState } from '~/lib/common/composables/scopedState'
|
||||
|
||||
const useRouterNavigatingState = () =>
|
||||
useState('use_router_navigating_state', () => ({
|
||||
useScopedState('use_router_navigating_state', () => ({
|
||||
allActiveWaits: <Array<Promise<unknown>>>[],
|
||||
/**
|
||||
* Used for debugging to assign an incrementing id to each invocation
|
||||
@@ -20,8 +21,8 @@ const useRouterNavigatingDevUtils = () => {
|
||||
|
||||
const ret = {
|
||||
getLogId: () => {
|
||||
const newVal = state.value.logId + 1
|
||||
state.value.logId = newVal
|
||||
const newVal = state.logId + 1
|
||||
state.logId = newVal
|
||||
|
||||
return newVal + ''
|
||||
},
|
||||
@@ -40,6 +41,16 @@ const useRouterNavigatingDevUtils = () => {
|
||||
devTrace(...args)
|
||||
})
|
||||
},
|
||||
waitForNavigationsClear: async () =>
|
||||
await until($isNavigating)
|
||||
.toBe(false, {
|
||||
throwOnTimeout: true,
|
||||
timeout: 500
|
||||
})
|
||||
.catch((err) => {
|
||||
// Swallow throw, just log and continue
|
||||
$logger.error({ err }, 'Waiting for nuxt navigations to clear timed out')
|
||||
}),
|
||||
isNuxtNavigating: $isNavigating,
|
||||
logger: $logger
|
||||
}
|
||||
@@ -69,7 +80,7 @@ type SafeRouterNavigationOptions<
|
||||
* Supports debugRoutes=1 query param for debug logs
|
||||
*/
|
||||
export const useSafeRouter = () => {
|
||||
const { getLogId, debugLog, debugTrace, isNuxtNavigating, logger } =
|
||||
const { getLogId, debugLog, debugTrace, waitForNavigationsClear } =
|
||||
useRouterNavigatingDevUtils()
|
||||
const router = useRouter()
|
||||
const state = useRouterNavigatingState()
|
||||
@@ -87,27 +98,16 @@ export const useSafeRouter = () => {
|
||||
const waitPromise = buildManualPromise<void>()
|
||||
const logId = getLogId()
|
||||
|
||||
const waitForNavigationsClear = async () =>
|
||||
await until(isNuxtNavigating)
|
||||
.toBe(false, {
|
||||
throwOnTimeout: true,
|
||||
timeout: 500
|
||||
})
|
||||
.catch((err) => {
|
||||
// Swallow throw, just log and continue
|
||||
logger.error({ err }, 'Waiting for nuxt navigations to clear timed out')
|
||||
})
|
||||
|
||||
debugTrace(`[{logId}] Safe router ${action} registered`, {
|
||||
initialTo: to(),
|
||||
logId
|
||||
})
|
||||
|
||||
try {
|
||||
const activeWaits = state.value.allActiveWaits.slice()
|
||||
const activeWaits = state.allActiveWaits.slice()
|
||||
|
||||
// Queue up another wait
|
||||
state.value.allActiveWaits = [...state.value.allActiveWaits, waitPromise.promise]
|
||||
state.allActiveWaits = [...state.allActiveWaits, waitPromise.promise]
|
||||
|
||||
// Wait for all previously queued up waits
|
||||
await Promise.allSettled(activeWaits)
|
||||
@@ -150,7 +150,7 @@ export const useSafeRouter = () => {
|
||||
logId,
|
||||
navResult
|
||||
})
|
||||
state.value.allActiveWaits = state.value.allActiveWaits.filter(
|
||||
state.allActiveWaits = state.allActiveWaits.filter(
|
||||
(p) => p !== waitPromise.promise
|
||||
)
|
||||
waitPromise.resolve()
|
||||
@@ -160,7 +160,7 @@ export const useSafeRouter = () => {
|
||||
waitPromise.reject(e)
|
||||
throw e
|
||||
} finally {
|
||||
state.value.allActiveWaits = state.value.allActiveWaits.filter(
|
||||
state.allActiveWaits = state.allActiveWaits.filter(
|
||||
(p) => p !== waitPromise.promise
|
||||
)
|
||||
}
|
||||
@@ -186,3 +186,20 @@ export const useSafeRouter = () => {
|
||||
|
||||
return { ...router, push, replace }
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to useRoute, but will not change the value until the new/incoming route has fully finished navigating
|
||||
*/
|
||||
export const useCurrentRouteTillNavigated = () => {
|
||||
const baseRoute = useRoute()
|
||||
const { $isNavigating } = useNuxtApp()
|
||||
const route = shallowRef({ ...toRaw(baseRoute) })
|
||||
|
||||
watch($isNavigating, (newVal, oldVal) => {
|
||||
if (!newVal && oldVal) {
|
||||
route.value = { ...toRaw(baseRoute) }
|
||||
}
|
||||
})
|
||||
|
||||
return route
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ export const useUpdateSavedView = () => {
|
||||
const { input } = params
|
||||
|
||||
const oldGroupId = params.view.group.id
|
||||
const oldVisibility = params.view.visibility
|
||||
// const oldVisibility = params.view.visibility
|
||||
|
||||
const result = await mutate(
|
||||
{ input },
|
||||
@@ -267,17 +267,18 @@ export const useUpdateSavedView = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const newVisibility = update.visibility
|
||||
const visibilityChanged = oldVisibility !== newVisibility
|
||||
if (visibilityChanged) {
|
||||
// Update all SavedViewGroup.views to see if it now should appear in there or not
|
||||
modifyObjectField(
|
||||
cache,
|
||||
getCacheId('SavedViewGroup', newGroupId),
|
||||
'views',
|
||||
({ helpers: { evict } }) => evict()
|
||||
)
|
||||
}
|
||||
// W/ current filter setup, if u can change visibility, you're gonna see it in all filtered groups
|
||||
// const newVisibility = update.visibility
|
||||
// const visibilityChanged = oldVisibility !== newVisibility
|
||||
// if (visibilityChanged) {
|
||||
// // Update all SavedViewGroup.views to see if it now should appear in there or not
|
||||
// modifyObjectField(
|
||||
// cache,
|
||||
// getCacheId('SavedViewGroup', newGroupId),
|
||||
// 'views',
|
||||
// ({ helpers: { evict } }) => evict()
|
||||
// )
|
||||
// }
|
||||
}
|
||||
}
|
||||
).catch(convertThrowIntoFetchResult)
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { throwUncoveredError, type StringEnumValues } from '@speckle/shared'
|
||||
import { isObjectLike, isString } from 'lodash-es'
|
||||
import { SavedViewVisibility } from '~/lib/common/generated/gql/graphql'
|
||||
|
||||
export const ViewsType = {
|
||||
Personal: 'personal',
|
||||
Shared: 'shared'
|
||||
All: 'all',
|
||||
Mine: 'mine'
|
||||
} as const
|
||||
export type ViewsType = StringEnumValues<typeof ViewsType>
|
||||
|
||||
export const viewsTypeLabels: Record<ViewsType, string> = {
|
||||
[ViewsType.Personal]: 'Personal',
|
||||
[ViewsType.Shared]: 'Shared'
|
||||
[ViewsType.All]: 'All views',
|
||||
[ViewsType.Mine]: 'My views'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,14 +44,12 @@ export const serializeSavedViewUrlSettings = (
|
||||
}
|
||||
|
||||
export const viewsTypeToFilters = (type: ViewsType) => {
|
||||
if (type === ViewsType.Personal) {
|
||||
if (type === ViewsType.Mine) {
|
||||
return {
|
||||
onlyAuthored: true
|
||||
}
|
||||
} else if (type === ViewsType.Shared) {
|
||||
return {
|
||||
onlyVisibility: SavedViewVisibility.Public
|
||||
}
|
||||
} else if (type === ViewsType.All) {
|
||||
return {}
|
||||
} else {
|
||||
throwUncoveredError(type)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user