diff --git a/packages/frontend-2/components/viewer/saved-views/panel/View.vue b/packages/frontend-2/components/viewer/saved-views/panel/View.vue
index 015704242..3a3042af6 100644
--- a/packages/frontend-2/components/viewer/saved-views/panel/View.vue
+++ b/packages/frontend-2/components/viewer/saved-views/panel/View.vue
@@ -49,7 +49,7 @@
name="editView"
class="shrink-0 opacity-0 group-hover:opacity-100"
:disabled="!canUpdate?.authorized || isLoading"
- @click="showEditDialog = !showEditDialog"
+ @click="onEdit"
/>
@@ -63,7 +63,6 @@
-
diff --git a/packages/frontend-2/components/viewer/saved-views/panel/Views.vue b/packages/frontend-2/components/viewer/saved-views/panel/Views.vue
index ac60e14ea..f1cfa80e5 100644
--- a/packages/frontend-2/components/viewer/saved-views/panel/Views.vue
+++ b/packages/frontend-2/components/viewer/saved-views/panel/Views.vue
@@ -18,6 +18,15 @@
hide-when-complete
@infinite="onInfiniteLoad"
/>
+
+
@@ -25,6 +34,10 @@
import { omit } from 'lodash-es'
import { usePaginatedQuery } from '~/lib/common/composables/graphql'
import { graphql } from '~/lib/common/generated/gql'
+import type {
+ ViewerSavedViewsPanelViewEditDialog_SavedViewFragment,
+ ViewerSavedViewsPanelViewMoveDialog_SavedViewFragment
+} from '~/lib/common/generated/gql/graphql'
import { useInjectedViewerState } from '~/lib/viewer/composables/setup'
import { ViewsType } from '~/lib/viewer/helpers/savedViews'
@@ -68,8 +81,11 @@ const {
request: { resourceIdString }
}
} = useInjectedViewerState()
+const eventBus = useEventBus()
const search = ref('')
+const viewBeingEdited = ref()
+const viewBeingMoved = ref()
const {
identifier,
@@ -111,6 +127,24 @@ const groups = computed(() => {
return result.value?.project.savedViewGroups.items || []
})
+const showEditDialog = computed({
+ get: () => !!viewBeingEdited.value,
+ set: (value) => {
+ if (!value) {
+ viewBeingEdited.value = undefined
+ }
+ }
+})
+
+const showMoveDialog = computed({
+ get: () => !!viewBeingMoved.value,
+ set: (value) => {
+ if (!value) {
+ viewBeingMoved.value = undefined
+ }
+ }
+})
+
watch(
groups,
(newGroups) => {
@@ -120,4 +154,16 @@ watch(
},
{ immediate: true }
)
+
+eventBus.on(ViewerEventBusKeys.MarkSavedViewForEdit, ({ type, view }) => {
+ if (type === 'edit') {
+ viewBeingEdited.value = view
+ } else if (type === 'move') {
+ viewBeingMoved.value = view
+ }
+})
+
+const onMoveSuccess = (groupId: string) => {
+ selectedGroupId.value = groupId
+}
diff --git a/packages/frontend-2/components/viewer/saved-views/panel/view/EditDialog.vue b/packages/frontend-2/components/viewer/saved-views/panel/view/EditDialog.vue
index 3e6757c34..1220d5b82 100644
--- a/packages/frontend-2/components/viewer/saved-views/panel/view/EditDialog.vue
+++ b/packages/frontend-2/components/viewer/saved-views/panel/view/EditDialog.vue
@@ -77,7 +77,7 @@ type FormType = {
}
const props = defineProps<{
- view: ViewerSavedViewsPanelViewEditDialog_SavedViewFragment
+ view: ViewerSavedViewsPanelViewEditDialog_SavedViewFragment | undefined
}>()
const open = defineModel('open', {
@@ -126,6 +126,8 @@ const radioOptions = computed((): FormRadioGroupItem[] => [
])
const onSubmit = handleSubmit(async (values) => {
+ if (!props.view) return
+
const name =
values.name.trim() && values.name.trim() !== props.view.name
? values.name.trim()
@@ -156,6 +158,8 @@ const onSubmit = handleSubmit(async (values) => {
})
watch(open, (newVal, oldVal) => {
+ if (!props.view) return
+
if (newVal && !oldVal) {
// Reset form state when dialog opens
setValues({
diff --git a/packages/frontend-2/components/viewer/saved-views/panel/view/MoveDialog.vue b/packages/frontend-2/components/viewer/saved-views/panel/view/MoveDialog.vue
new file mode 100644
index 000000000..e1de79d03
--- /dev/null
+++ b/packages/frontend-2/components/viewer/saved-views/panel/view/MoveDialog.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
diff --git a/packages/frontend-2/composables/globals.ts b/packages/frontend-2/composables/globals.ts
index 3450b289b..0016401cc 100644
--- a/packages/frontend-2/composables/globals.ts
+++ b/packages/frontend-2/composables/globals.ts
@@ -1,6 +1,7 @@
import { useActiveUser } from '~/lib/auth/composables/activeUser'
import { usePageQueryStandardFetchPolicy } from '~/lib/common/composables/graphql'
import { useGlobalToast } from '~/lib/common/composables/toast'
+import { useEventBus } from '~/lib/core/composables/eventBus'
export const useIsAccModuleEnabled = () => {
const {
@@ -83,4 +84,4 @@ export const useIsRhinoFileImporterEnabled = () => {
return ref(FF_RHINO_FILE_IMPORTER_ENABLED)
}
-export { useGlobalToast, useActiveUser, usePageQueryStandardFetchPolicy }
+export { useGlobalToast, useActiveUser, usePageQueryStandardFetchPolicy, useEventBus }
diff --git a/packages/frontend-2/lib/common/generated/gql/gql.ts b/packages/frontend-2/lib/common/generated/gql/gql.ts
index 6b007067f..553c7f27f 100644
--- a/packages/frontend-2/lib/common/generated/gql/gql.ts
+++ b/packages/frontend-2/lib/common/generated/gql/gql.ts
@@ -171,6 +171,7 @@ type Documents = {
"\n fragment ViewerSavedViewsPanelViews_Project on Project {\n id\n savedViewGroups(input: $savedViewGroupsInput) {\n totalCount\n cursor\n items {\n id\n ...ViewerSavedViewsPanelViewsGroup_SavedViewGroup\n }\n }\n }\n": typeof types.ViewerSavedViewsPanelViews_ProjectFragmentDoc,
"\n query ViewerSavedViewsPanelViews_Groups(\n $projectId: String!\n $savedViewGroupsInput: SavedViewGroupsInput!\n ) {\n project(id: $projectId) {\n id\n ...ViewerSavedViewsPanelViews_Project\n }\n }\n": typeof types.ViewerSavedViewsPanelViews_GroupsDocument,
"\n fragment ViewerSavedViewsPanelViewEditDialog_SavedView on SavedView {\n id\n name\n description\n visibility\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n": typeof types.ViewerSavedViewsPanelViewEditDialog_SavedViewFragmentDoc,
+ "\n fragment ViewerSavedViewsPanelViewMoveDialog_SavedView on SavedView {\n id\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n": typeof types.ViewerSavedViewsPanelViewMoveDialog_SavedViewFragmentDoc,
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n }\n": typeof types.ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragmentDoc,
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup_Paginated on SavedViewGroup {\n id\n views(input: $savedViewsInput) {\n cursor\n totalCount\n items {\n id\n ...ViewerSavedViewsPanelView_SavedView\n }\n }\n }\n": typeof types.ViewerSavedViewsPanelViewsGroup_SavedViewGroup_PaginatedFragmentDoc,
"\n fragment ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup on SavedViewGroup {\n id\n title\n }\n": typeof types.ViewerSavedViewsPanelViewsGroupInner_SavedViewGroupFragmentDoc,
@@ -665,6 +666,7 @@ const documents: Documents = {
"\n fragment ViewerSavedViewsPanelViews_Project on Project {\n id\n savedViewGroups(input: $savedViewGroupsInput) {\n totalCount\n cursor\n items {\n id\n ...ViewerSavedViewsPanelViewsGroup_SavedViewGroup\n }\n }\n }\n": types.ViewerSavedViewsPanelViews_ProjectFragmentDoc,
"\n query ViewerSavedViewsPanelViews_Groups(\n $projectId: String!\n $savedViewGroupsInput: SavedViewGroupsInput!\n ) {\n project(id: $projectId) {\n id\n ...ViewerSavedViewsPanelViews_Project\n }\n }\n": types.ViewerSavedViewsPanelViews_GroupsDocument,
"\n fragment ViewerSavedViewsPanelViewEditDialog_SavedView on SavedView {\n id\n name\n description\n visibility\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n": types.ViewerSavedViewsPanelViewEditDialog_SavedViewFragmentDoc,
+ "\n fragment ViewerSavedViewsPanelViewMoveDialog_SavedView on SavedView {\n id\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n": types.ViewerSavedViewsPanelViewMoveDialog_SavedViewFragmentDoc,
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n }\n": types.ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragmentDoc,
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup_Paginated on SavedViewGroup {\n id\n views(input: $savedViewsInput) {\n cursor\n totalCount\n items {\n id\n ...ViewerSavedViewsPanelView_SavedView\n }\n }\n }\n": types.ViewerSavedViewsPanelViewsGroup_SavedViewGroup_PaginatedFragmentDoc,
"\n fragment ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup on SavedViewGroup {\n id\n title\n }\n": types.ViewerSavedViewsPanelViewsGroupInner_SavedViewGroupFragmentDoc,
@@ -1644,6 +1646,10 @@ export function graphql(source: "\n query ViewerSavedViewsPanelViews_Groups(\n
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment ViewerSavedViewsPanelViewEditDialog_SavedView on SavedView {\n id\n name\n description\n visibility\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n"): (typeof documents)["\n fragment ViewerSavedViewsPanelViewEditDialog_SavedView on SavedView {\n id\n name\n description\n visibility\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n fragment ViewerSavedViewsPanelViewMoveDialog_SavedView on SavedView {\n id\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n"): (typeof documents)["\n fragment ViewerSavedViewsPanelViewMoveDialog_SavedView on SavedView {\n id\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
diff --git a/packages/frontend-2/lib/common/generated/gql/graphql.ts b/packages/frontend-2/lib/common/generated/gql/graphql.ts
index b7703c74c..202b98f0f 100644
--- a/packages/frontend-2/lib/common/generated/gql/graphql.ts
+++ b/packages/frontend-2/lib/common/generated/gql/graphql.ts
@@ -6096,6 +6096,8 @@ export type ViewerSavedViewsPanelViews_GroupsQuery = { __typename?: 'Query', pro
export type ViewerSavedViewsPanelViewEditDialog_SavedViewFragment = { __typename?: 'SavedView', id: string, name: string, description?: string | null, visibility: SavedViewVisibility, projectId: string, group: { __typename?: 'SavedViewGroup', id: string, title: string, isUngroupedViewsGroup: boolean } };
+export type ViewerSavedViewsPanelViewMoveDialog_SavedViewFragment = { __typename?: 'SavedView', id: string, projectId: string, group: { __typename?: 'SavedViewGroup', id: string, title: string, isUngroupedViewsGroup: boolean } };
+
export type ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragment = { __typename?: 'SavedViewGroup', id: string, isUngroupedViewsGroup: boolean, title: string };
export type ViewerSavedViewsPanelViewsGroup_SavedViewGroup_PaginatedFragment = { __typename?: 'SavedViewGroup', id: string, views: { __typename?: 'SavedViewCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'SavedView', id: string, name: string, description?: string | null, screenshot: string, visibility: SavedViewVisibility, updatedAt: string, projectId: string, author?: { __typename?: 'LimitedUser', id: string, name: string } | null, permissions: { __typename?: 'SavedViewPermissionChecks', canUpdate: { __typename?: 'PermissionCheckResult', authorized: boolean, code: string, message: string, payload?: {} | null, errorMessage?: string | null } }, group: { __typename?: 'SavedViewGroup', id: string, title: string, isUngroupedViewsGroup: boolean } }> } };
@@ -8156,9 +8158,10 @@ export const ViewerSavedViewsPanel_ProjectFragmentDoc = {"kind":"Document","defi
export const ViewerSavedViewsPanelViewsGroupInner_SavedViewGroupFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]} as unknown as DocumentNode;
export const ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewsGroup_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isUngroupedViewsGroup"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]} as unknown as DocumentNode;
export const ViewerSavedViewsPanelViews_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViews_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"savedViewGroups"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"savedViewGroupsInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewsGroup_SavedViewGroup"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewsGroup_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isUngroupedViewsGroup"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup"}}]}}]} as unknown as DocumentNode;
-export const UseDeleteSavedView_SavedViewFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseDeleteSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode;
-export const UseUpdateSavedView_SavedViewFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode;
export const FormSelectSavedViewGroup_SavedViewGroupFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FormSelectSavedViewGroup_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"isUngroupedViewsGroup"}}]}}]} as unknown as DocumentNode;
+export const UseUpdateSavedView_SavedViewFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode;
+export const ViewerSavedViewsPanelViewMoveDialog_SavedViewFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewMoveDialog_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormSelectSavedViewGroup_SavedViewGroup"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FormSelectSavedViewGroup_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"isUngroupedViewsGroup"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode;
+export const UseDeleteSavedView_SavedViewFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseDeleteSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode;
export const ViewerSavedViewsPanelViewEditDialog_SavedViewFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewEditDialog_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormSelectSavedViewGroup_SavedViewGroup"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FormSelectSavedViewGroup_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"isUngroupedViewsGroup"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode;
export const ViewerSavedViewsPanelView_SavedViewFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"screenshot"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"canUpdate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FullPermissionCheckResult"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseDeleteSavedView_SavedView"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewEditDialog_SavedView"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FormSelectSavedViewGroup_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"isUngroupedViewsGroup"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FullPermissionCheckResult"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PermissionCheckResult"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"authorized"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"payload"}},{"kind":"Field","name":{"kind":"Name","value":"errorMessage"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseDeleteSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewEditDialog_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormSelectSavedViewGroup_SavedViewGroup"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"}}]}}]} as unknown as DocumentNode;
export const ViewerSavedViewsPanelViewsGroup_SavedViewGroup_PaginatedFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewsGroup_SavedViewGroup_Paginated"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"savedViewsInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerSavedViewsPanelView_SavedView"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FullPermissionCheckResult"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PermissionCheckResult"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"authorized"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"payload"}},{"kind":"Field","name":{"kind":"Name","value":"errorMessage"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseDeleteSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FormSelectSavedViewGroup_SavedViewGroup"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"isUngroupedViewsGroup"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewEditDialog_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormSelectSavedViewGroup_SavedViewGroup"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerSavedViewsPanelView_SavedView"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SavedView"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"screenshot"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"canUpdate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FullPermissionCheckResult"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseDeleteSavedView_SavedView"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseUpdateSavedView_SavedView"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerSavedViewsPanelViewEditDialog_SavedView"}}]}}]} as unknown as DocumentNode;
diff --git a/packages/frontend-2/lib/viewer/composables/savedViews/general.ts b/packages/frontend-2/lib/viewer/composables/savedViews/general.ts
index 0bbd6b0e0..b17fe1af9 100644
--- a/packages/frontend-2/lib/viewer/composables/savedViews/general.ts
+++ b/packages/frontend-2/lib/viewer/composables/savedViews/general.ts
@@ -41,7 +41,7 @@ export const useViewerSavedViewsUtils = () => {
const applyView = (settings: SavedViewUrlSettings) => {
// Force update, even if the view id is already set
// (in case this is a frustration click w/ the state not applying)
- eventBus.emit(ViewerEventBusKeys.UpdateSavedView, settings)
+ eventBus.emit(ViewerEventBusKeys.ApplySavedView, settings)
}
return {
diff --git a/packages/frontend-2/lib/viewer/composables/savedViews/management.ts b/packages/frontend-2/lib/viewer/composables/savedViews/management.ts
index 6350ab208..f4363d89a 100644
--- a/packages/frontend-2/lib/viewer/composables/savedViews/management.ts
+++ b/packages/frontend-2/lib/viewer/composables/savedViews/management.ts
@@ -13,6 +13,7 @@ import {
onGroupViewRemovalCacheUpdates,
onNewGroupViewCacheUpdates
} from '~/lib/viewer/helpers/savedViews/cache'
+import { isUngroupedGroup } from '@speckle/shared/saved-views'
const createSavedViewMutation = graphql(`
mutation CreateSavedView($input: CreateSavedViewInput!) {
@@ -320,7 +321,7 @@ export const useCreateSavedViewGroup = () => {
// default comes first, then new group
const defaultIdx = newItems.findIndex((i) =>
- fromRef(i).id.startsWith('default-')
+ isUngroupedGroup(fromRef(i).id)
)
newItems.splice(defaultIdx + 1, 0, ref('SavedViewGroup', group.id))
diff --git a/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts b/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts
index fb08b8e69..4e877955a 100644
--- a/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts
+++ b/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts
@@ -1003,7 +1003,7 @@ const useViewerSavedViewSetup = () => {
}
// Allow force update
- on(ViewerEventBusKeys.UpdateSavedView, async (settings) => {
+ on(ViewerEventBusKeys.ApplySavedView, async (settings) => {
await update({ settings })
})
diff --git a/packages/frontend-2/lib/viewer/helpers/eventBus.ts b/packages/frontend-2/lib/viewer/helpers/eventBus.ts
index 7a27dd18c..0deb71b11 100644
--- a/packages/frontend-2/lib/viewer/helpers/eventBus.ts
+++ b/packages/frontend-2/lib/viewer/helpers/eventBus.ts
@@ -1,10 +1,26 @@
-import type { ViewerSavedViewEventBusPayloads } from '~/lib/viewer/helpers/savedViews'
+import type {
+ ViewerSavedViewsPanelViewEditDialog_SavedViewFragment,
+ ViewerSavedViewsPanelViewMoveDialog_SavedViewFragment
+} from '~/lib/common/generated/gql/graphql'
+import type { SavedViewUrlSettings } from '~/lib/viewer/helpers/savedViews'
export enum ViewerEventBusKeys {
- UpdateSavedView = 'aaa'
+ ApplySavedView = 'UpdateSavedView',
+ MarkSavedViewForEdit = 'MarkSavedViewForEdit'
+}
+
+export type ViewerSavedViewEventBusPayloads = {
+ [ViewerEventBusKeys.ApplySavedView]: SavedViewUrlSettings
+ [ViewerEventBusKeys.MarkSavedViewForEdit]:
+ | {
+ type: 'edit'
+ view: ViewerSavedViewsPanelViewEditDialog_SavedViewFragment
+ }
+ | { type: 'move'; view: ViewerSavedViewsPanelViewMoveDialog_SavedViewFragment }
}
// Add mappings between event keys and expected payloads here
export type ViewerEventBusKeyPayloadMap = {
- [ViewerEventBusKeys.UpdateSavedView]: ViewerSavedViewEventBusPayloads[ViewerEventBusKeys.UpdateSavedView]
+ [ViewerEventBusKeys.ApplySavedView]: ViewerSavedViewEventBusPayloads[ViewerEventBusKeys.ApplySavedView]
+ [ViewerEventBusKeys.MarkSavedViewForEdit]: ViewerSavedViewEventBusPayloads[ViewerEventBusKeys.MarkSavedViewForEdit]
} & { [k in ViewerEventBusKeys]: unknown } & Record
diff --git a/packages/frontend-2/lib/viewer/helpers/savedViews.ts b/packages/frontend-2/lib/viewer/helpers/savedViews.ts
index 9dc9fe50f..813cc475c 100644
--- a/packages/frontend-2/lib/viewer/helpers/savedViews.ts
+++ b/packages/frontend-2/lib/viewer/helpers/savedViews.ts
@@ -1,6 +1,5 @@
import type { StringEnumValues } from '@speckle/shared'
import { isObjectLike, isString } from 'lodash-es'
-import type { ViewerEventBusKeys } from '~/lib/viewer/helpers/eventBus'
export const ViewsType = {
All: 'all-views',
@@ -23,10 +22,6 @@ export type SavedViewUrlSettings = {
loadOriginal?: boolean
}
-export type ViewerSavedViewEventBusPayloads = {
- [ViewerEventBusKeys.UpdateSavedView]: SavedViewUrlSettings
-}
-
export const parseSavedViewUrlSettings = (
settingsString: string | null
): SavedViewUrlSettings | null => {
diff --git a/packages/frontend-2/lib/viewer/helpers/savedViews/cache.ts b/packages/frontend-2/lib/viewer/helpers/savedViews/cache.ts
index e6ef5dc00..ae0dc4404 100644
--- a/packages/frontend-2/lib/viewer/helpers/savedViews/cache.ts
+++ b/packages/frontend-2/lib/viewer/helpers/savedViews/cache.ts
@@ -1,4 +1,5 @@
import type { ApolloCache } from '@apollo/client/cache'
+import { isUngroupedGroup } from '@speckle/shared/saved-views'
/**
* Cache mutations for when a group gets a new view:
@@ -75,7 +76,7 @@ export const onGroupViewRemovalCacheUpdates = (
const { viewId: id, groupId, projectId } = params
// Check if default/ungrouped group
- const isDefaultGroup = groupId.startsWith('default-')
+ const isDefaultGroup = isUngroupedGroup(groupId)
// If default group and its now empty - remove it as it doesn't exist otherwise
let shouldEvict
diff --git a/packages/frontend-2/utils/globals.ts b/packages/frontend-2/utils/globals.ts
index 69c864cda..6b71b1c15 100644
--- a/packages/frontend-2/utils/globals.ts
+++ b/packages/frontend-2/utils/globals.ts
@@ -13,6 +13,7 @@ import {
ROOT_SUBSCRIPTION
} from '~/lib/common/helpers/graphql'
import { checkIfIsInPlaceNavigation } from '~/lib/common/helpers/navigation'
+import { ViewerEventBusKeys } from '~/lib/viewer/helpers/eventBus'
/**
* Debugging helper to ensure variables are available in debugging scope
@@ -38,5 +39,6 @@ export {
checkIfIsInPlaceNavigation,
ROOT_QUERY,
ROOT_MUTATION,
- ROOT_SUBSCRIPTION
+ ROOT_SUBSCRIPTION,
+ ViewerEventBusKeys
}
diff --git a/packages/server/modules/viewer/helpers/savedViews.ts b/packages/server/modules/viewer/helpers/savedViews.ts
index ae895dabc..7099a8db3 100644
--- a/packages/server/modules/viewer/helpers/savedViews.ts
+++ b/packages/server/modules/viewer/helpers/savedViews.ts
@@ -1,5 +1,6 @@
import { base64Decode, base64Encode } from '@/modules/shared/helpers/cryptoHelper'
import type { Nullable } from '@speckle/shared'
+import { isUngroupedGroup } from '@speckle/shared/saved-views'
import type { ViewerResourcesTarget } from '@speckle/shared/viewer/route'
import {
isModelResource,
@@ -29,7 +30,7 @@ export const buildDefaultGroupId = (params: {
export const decodeDefaultGroupId = (id: string): Nullable => {
try {
- if (!id.startsWith('default-')) return null
+ if (!isUngroupedGroup(id)) return null
const json = base64Decode(id.replace('default-', ''))
const obj = JSON.parse(json)
if (
diff --git a/packages/server/modules/viewer/repositories/savedViews.ts b/packages/server/modules/viewer/repositories/savedViews.ts
index ac804c3d0..4249c0895 100644
--- a/packages/server/modules/viewer/repositories/savedViews.ts
+++ b/packages/server/modules/viewer/repositories/savedViews.ts
@@ -31,6 +31,7 @@ import {
decodeDefaultGroupId,
formatResourceIdsForGroup
} from '@/modules/viewer/helpers/savedViews'
+import { isUngroupedGroup } from '@speckle/shared/saved-views'
import { resourceBuilder } from '@speckle/shared/viewer/route'
import cryptoRandomString from 'crypto-random-string'
import dayjs from 'dayjs'
@@ -249,7 +250,7 @@ export const getProjectSavedViewGroupsPageItemsFactory =
// Adjust cursor, in case it points to non-existant default group
let cursor = decode(params.cursor)
- if (cursor?.id.startsWith('default-')) {
+ if (cursor?.id && isUngroupedGroup(cursor.id)) {
// Default appears first, so just unset the cursor to get the real first item
cursor = null
}
diff --git a/packages/server/modules/viewer/services/savedViewsManagement.ts b/packages/server/modules/viewer/services/savedViewsManagement.ts
index dee84a034..0098dac81 100644
--- a/packages/server/modules/viewer/services/savedViewsManagement.ts
+++ b/packages/server/modules/viewer/services/savedViewsManagement.ts
@@ -33,9 +33,10 @@ import { inputToVersionedState } from '@speckle/shared/viewer/state'
import { isValidBase64Image } from '@speckle/shared/images/base64'
import type { GetViewerResourceGroups } from '@/modules/viewer/domain/operations/resources'
import { formatResourceIdsForGroup } from '@/modules/viewer/helpers/savedViews'
-import { omit } from 'lodash-es'
+import { isUndefined, omit } from 'lodash-es'
import type { DependenciesOf } from '@/modules/shared/helpers/factory'
import { removeNullOrUndefinedKeys } from '@speckle/shared'
+import { isUngroupedGroup } from '@speckle/shared/saved-views'
/**
* Validates an incoming resourceIdString against the resources in the project and returns the validated list (as a builder)
@@ -388,7 +389,14 @@ export const updateSavedViewFactory =
}
// Check if there's any actual changes
- const changes = removeNullOrUndefinedKeys(omit(input, ['id', 'projectId']))
+ const changes = {
+ ...removeNullOrUndefinedKeys(omit(input, ['id', 'projectId'])),
+ ...(!isUndefined(input.groupId)
+ ? {
+ groupId: input.groupId // we want to allow null, which means - no group
+ }
+ : {})
+ }
if (Object.keys(changes).length === 0) {
throw new SavedViewUpdateValidationError('No changes submitted with the input.', {
info: {
@@ -429,20 +437,26 @@ export const updateSavedViewFactory =
// Validate groupId - group is a valid and accessible group in the project
if (changes.groupId) {
- const group = await deps.getSavedViewGroup({
- id: changes.groupId,
- projectId
- })
- if (!group) {
- throw new SavedViewUpdateValidationError(
- 'Provided groupId does not exist in the project.',
- {
- info: {
- input,
- userId
+ // Check if default group (actually means - null group)
+ const isDefaultGroup = changes.groupId && isUngroupedGroup(changes.groupId)
+ if (isDefaultGroup) {
+ changes.groupId = null
+ } else {
+ const group = await deps.getSavedViewGroup({
+ id: changes.groupId,
+ projectId
+ })
+ if (!group) {
+ throw new SavedViewUpdateValidationError(
+ 'Provided groupId does not exist in the project.',
+ {
+ info: {
+ input,
+ userId
+ }
}
- }
- )
+ )
+ }
}
}
diff --git a/packages/server/modules/viewer/tests/integration/savedViewsCrud.graph.spec.ts b/packages/server/modules/viewer/tests/integration/savedViewsCrud.graph.spec.ts
index f9dbea51e..f157cdd0c 100644
--- a/packages/server/modules/viewer/tests/integration/savedViewsCrud.graph.spec.ts
+++ b/packages/server/modules/viewer/tests/integration/savedViewsCrud.graph.spec.ts
@@ -186,6 +186,11 @@ const fakeViewerState = (overrides?: PartialDeep apollo.execute(UpdateSavedViewDocument, input, options)
+ const getProjectViewGroups = (
+ input: GetProjectSavedViewGroupsQueryVariables,
+ options?: ExecuteOperationOptions
+ ) => apollo.execute(GetProjectSavedViewGroupsDocument, input, options)
+
const model1ResourceIds = () => ViewerRoute.resourceBuilder().addModel(myModel1.id)
const model2ResourceIds = () => ViewerRoute.resourceBuilder().addModel(myModel2.id)
@@ -866,6 +871,73 @@ const fakeViewerState = (overrides?: PartialDeep {
+ const newGroupId = optionalGroup.id
+
+ const res = await updateView({
+ input: {
+ id: testView.id,
+ projectId: updatablesProject.id,
+ groupId: newGroupId
+ }
+ })
+
+ expect(res).to.not.haveGraphQLErrors()
+ const updatedView = res.data?.projectMutations.savedViewMutations.updateView
+ expect(updatedView).to.be.ok
+ expect(updatedView!.id).to.equal(testView.id)
+ expect(updatedView!.groupId).to.equal(newGroupId)
+
+ // Unset group
+ const res2 = await updateView({
+ input: {
+ id: testView.id,
+ projectId: updatablesProject.id,
+ groupId: null
+ }
+ })
+
+ expect(res2).to.not.haveGraphQLErrors()
+ const updatedView2 = res2.data?.projectMutations.savedViewMutations.updateView
+ expect(updatedView2).to.be.ok
+ expect(updatedView2!.id).to.equal(testView.id)
+ expect(updatedView2!.groupId).to.be.null
+ })
+
+ it('allow setting default group as group, which actually sets it to null', async () => {
+ // Get default group id
+ const groupsRes = await getProjectViewGroups(
+ {
+ projectId: updatablesProject.id,
+ input: {
+ limit: 1,
+ resourceIdString: models[0].id
+ }
+ },
+ { assertNoErrors: true }
+ )
+
+ const defaultGroup = groupsRes.data?.project.savedViewGroups.items[0]
+ expect(defaultGroup).to.be.ok
+
+ // Update view to have that be the group
+ const update = await updateView(
+ {
+ input: {
+ id: testView.id,
+ projectId: updatablesProject.id,
+ groupId: defaultGroup!.id
+ }
+ },
+ { assertNoErrors: true }
+ )
+
+ const updatedView = update.data?.projectMutations.savedViewMutations.updateView
+ expect(updatedView?.id).to.be.ok
+ expect(updatedView?.groupId).to.be.null
+ expect(updatedView?.group.id).to.equal(defaultGroup!.id)
+ })
+
it('fails if user has no access to update the view', async () => {
const newName = 'Updated View Name'
@@ -1130,11 +1202,6 @@ const fakeViewerState = (overrides?: PartialDeep new ViewerRoute.ViewerModelResource(id))
)
- const getProjectViewGroups = (
- input: GetProjectSavedViewGroupsQueryVariables,
- options?: ExecuteOperationOptions
- ) => apollo.execute(GetProjectSavedViewGroupsDocument, input, options)
-
before(async () => {
otherReader = await createTestUser(buildBasicTestUser({ name: 'other-reader' }))
await assignToWorkspace(
@@ -1317,22 +1384,42 @@ const fakeViewerState = (overrides?: PartialDeep {
- // const res = await getProjectViewGroups({
- // projectId: readTestProject.id,
- // input: {
- // limit: GROUP_COUNT, // all in 1 page
- // resourceIdString: 'ayy'
- // }
- // })
+ it('should support default groups cursor', async () => {
+ const res1 = await getProjectViewGroups(
+ {
+ projectId: readTestProject.id,
+ input: {
+ limit: 1, // only get default group
+ resourceIdString: getAllReadModelResourceIds().toString()
+ }
+ },
+ { assertNoErrors: true }
+ )
- // expect(res).to.not.haveGraphQLErrors()
- // const data = res.data?.project.savedViewGroups
- // expect(data).to.be.ok
- // expect(data!.totalCount).to.equal(1) // only default group returned
- // expect(data!.items.length).to.equal(1)
- // expect(data!.cursor).to.not.be.null
- // })
+ const group = res1.data?.project.savedViewGroups.items[0]
+ const cursor = res1.data?.project.savedViewGroups.cursor
+
+ expect(group).to.be.ok
+ expect(group!.isUngroupedViewsGroup).to.be.true
+ expect(cursor).to.be.ok
+
+ const res2 = await getProjectViewGroups(
+ {
+ projectId: readTestProject.id,
+ input: {
+ limit: 1, // get first real item
+ resourceIdString: getAllReadModelResourceIds().toString(),
+ cursor
+ }
+ },
+ { assertNoErrors: true }
+ )
+
+ const group2 = res2.data?.project.savedViewGroups.items[0]
+
+ expect(group2).to.be.ok
+ expect(group2!.isUngroupedViewsGroup).to.be.false
+ })
it('should respect search filter and filter out groups w/ views that dont have the search string in their name', async () => {
const res = await getProjectViewGroups({
diff --git a/packages/server/package.json b/packages/server/package.json
index 6319a3874..75a6d75bf 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -145,7 +145,7 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
- "@apollo/rover": "^0.23.0",
+ "@apollo/rover": "^0.35.0",
"@faker-js/faker": "^8.4.1",
"@graphql-codegen/cli": "^5.0.7",
"@graphql-codegen/typed-document-node": "^5.1.2",
diff --git a/packages/shared/src/saved-views/helpers/values.spec.ts b/packages/shared/src/saved-views/helpers/values.spec.ts
new file mode 100644
index 000000000..2b1bfed54
--- /dev/null
+++ b/packages/shared/src/saved-views/helpers/values.spec.ts
@@ -0,0 +1,12 @@
+import { describe, expect, it } from 'vitest'
+import { isUngroupedGroup } from './values.js'
+
+describe('isUngroupedGroup', () => {
+ it('should return true for ungrouped groups', () => {
+ expect(isUngroupedGroup('default-group')).toBe(true)
+ })
+
+ it('should return false for grouped groups', () => {
+ expect(isUngroupedGroup('custom-group')).toBe(false)
+ })
+})
diff --git a/packages/shared/src/saved-views/helpers/values.ts b/packages/shared/src/saved-views/helpers/values.ts
index 75f5efb97..0a503bb3a 100644
--- a/packages/shared/src/saved-views/helpers/values.ts
+++ b/packages/shared/src/saved-views/helpers/values.ts
@@ -2,3 +2,5 @@
* Title used for the default 'Ungrouped Scenes' group in the saved views panel.
*/
export const ungroupedScenesGroupTitle = 'Ungrouped'
+
+export const isUngroupedGroup = (groupId: string) => groupId.startsWith('default-')
diff --git a/yarn.lock b/yarn.lock
index 901992514..d71ed28de 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -135,18 +135,18 @@ __metadata:
languageName: node
linkType: hard
-"@apollo/rover@npm:^0.23.0":
- version: 0.23.0
- resolution: "@apollo/rover@npm:0.23.0"
+"@apollo/rover@npm:^0.35.0":
+ version: 0.35.0
+ resolution: "@apollo/rover@npm:0.35.0"
dependencies:
axios: "npm:^1.6.5"
axios-proxy-builder: "npm:^0.1.1"
console.table: "npm:^0.10.0"
detect-libc: "npm:^2.0.0"
- tar: "npm:^6.2.0"
+ tar: "npm:^7.0.0"
bin:
rover: run.js
- checksum: 10/945f723c0a076503daf6622225653db761c3a0e2e80ee11f58b5f8cdc1cfe92eeaa732a5488881967b9123c49a92589800d3586ef61355767de16012be0af34a
+ checksum: 10/d3f87f65e2fe9f57360315a5f8cb57c00fd9eb7a3c6154d9aea2644cc023c882c60caa92a189db9008695223927e76d6941d760ca740619dae3bf811ee446616
languageName: node
linkType: hard
@@ -16278,7 +16278,7 @@ __metadata:
resolution: "@speckle/server@workspace:packages/server"
dependencies:
"@apollo/client": "npm:^3.7.0"
- "@apollo/rover": "npm:^0.23.0"
+ "@apollo/rover": "npm:^0.35.0"
"@apollo/server": "npm:^4.11.0"
"@aws-sdk/client-s3": "npm:^3.276.0"
"@aws-sdk/lib-storage": "npm:^3.100.0"
@@ -47036,7 +47036,7 @@ __metadata:
languageName: node
linkType: hard
-"tar@npm:^7.4.0":
+"tar@npm:^7.0.0, tar@npm:^7.4.0":
version: 7.4.3
resolution: "tar@npm:7.4.3"
dependencies: