fix: multiple FE2 and server speed improvements, mainly focusing on the project page (#1975)

* introduced app cache & optimized /downloads

* added redis cache storage

* optimizing latest thread retrieval

* more dataloaders

* undid debug stuff

* deployment changes

* minor change to reqTouched

* connectorTag parallel resolution

* added redis key prefix

* gqlgen cleanup

* Amend network policy to allow egress to Redis

---------

Co-authored-by: Iain Sproat <68657+iainsproat@users.noreply.github.com>
This commit is contained in:
Kristaps Fabians Geikins
2024-01-22 11:08:53 +02:00
committed by GitHub
parent 37d51072fb
commit c3f13d4e66
27 changed files with 488 additions and 98 deletions
+1
View File
@@ -24,6 +24,7 @@ services:
NUXT_PUBLIC_SERVER_NAME: 'local'
NUXT_PUBLIC_API_ORIGIN: 'http://127.0.0.1'
NUXT_PUBLIC_BACKEND_API_ORIGIN: 'http://speckle-server:3000'
NUXT_REDIS_URL: 'redis://redis'
speckle-server:
build:
+3 -1
View File
@@ -9,4 +9,6 @@ NUXT_PUBLIC_API_ORIGIN=http://127.0.0.1:3000
NUXT_PUBLIC_MIXPANEL_TOKEN_ID=acd87c5a50b56df91a795e999812a3a4
NUXT_PUBLIC_MIXPANEL_API_HOST=https://analytics.speckle.systems
NUXT_PUBLIC_SERVER_NAME=local
NUXT_PUBLIC_SERVER_NAME=local
NUXT_REDIS_URL=redis://localhost:6379
@@ -48,7 +48,9 @@
Downloads
</FormButton>
<ConnectorsVersionsDownloadDialog v-model:open="dialogOpen" :tag="tag" />
<FormButton size="sm" :to="tag.url" target="_blank">Tutorials</FormButton>
<FormButton size="sm" :to="tag.url" target="_blank" external>
Tutorials
</FormButton>
</div>
</div>
</div>
@@ -62,7 +62,6 @@ graphql(`
...FormUsersSelectItem
}
}
...LinkableComment
}
`)
@@ -52,7 +52,7 @@ import dayjs from 'dayjs'
import type { ProjectPageLatestItemsCommentItemFragment } from '~~/lib/common/generated/gql/graphql'
import { useCommentScreenshotImage } from '~~/lib/projects/composables/previewImage'
import { times } from 'lodash-es'
import { getLinkToThread } from '~~/lib/viewer/helpers/comments'
import { getLightLinkToThread } from '~~/lib/viewer/helpers/comments'
import { CheckCircleIcon } from '@heroicons/vue/24/solid'
import type { AvatarUserWithId } from '@speckle/ui-components'
@@ -90,5 +90,7 @@ const allAvatars = computed((): AvatarUserWithId[] => [
)
])
const threadLink = computed(() => getLinkToThread(props.projectId, props.thread))
const threadLink = computed(() =>
getLightLinkToThread(props.projectId, props.thread.id)
)
</script>
@@ -44,7 +44,7 @@ import dayjs from 'dayjs'
import { times } from 'lodash-es'
import type { ProjectPageLatestItemsCommentItemFragment } from '~~/lib/common/generated/gql/graphql'
import { useCommentScreenshotImage } from '~~/lib/projects/composables/previewImage'
import { getLinkToThread } from '~~/lib/viewer/helpers/comments'
import { getLightLinkToThread } from '~~/lib/viewer/helpers/comments'
import { CheckCircleIcon } from '@heroicons/vue/24/solid'
import type { AvatarUserWithId } from '@speckle/ui-components'
@@ -81,5 +81,7 @@ const allAvatars = computed((): AvatarUserWithId[] => [
)
])
const threadLink = computed(() => getLinkToThread(props.projectId, props.thread))
const threadLink = computed(() =>
getLightLinkToThread(props.projectId, props.thread.id)
)
</script>
@@ -81,20 +81,31 @@ const logger = useLogger()
const areQueriesLoading = useQueryLoading()
const latestModelsQueryVariables = computed(
(): ProjectLatestModelsPaginationQueryVariables => ({
projectId: props.projectId,
filter: {
search: props.search || null,
excludeIds: props.excludedIds || null,
onlyWithVersions: !!props.excludeEmptyModels,
sourceApps: props.sourceApps?.length
? props.sourceApps.map((a) => a.searchKey)
: null,
contributors: props.contributors?.length
? props.contributors.map((c) => c.id)
(): ProjectLatestModelsPaginationQueryVariables => {
const shouldHaveFilter =
props.search?.length ||
props.excludedIds?.length ||
props.sourceApps?.length ||
props.contributors?.length ||
!!props.excludeEmptyModels
return {
projectId: props.projectId,
filter: shouldHaveFilter
? {
search: props.search || null,
excludeIds: props.excludedIds || null,
onlyWithVersions: !!props.excludeEmptyModels,
sourceApps: props.sourceApps?.length
? props.sourceApps.map((a) => a.searchKey)
: null,
contributors: props.contributors?.length
? props.contributors.map((c) => c.id)
: null
}
: null
}
})
}
)
const infiniteLoaderId = ref('')
+28
View File
@@ -0,0 +1,28 @@
import type { MaybeAsync } from '@speckle/shared'
/**
* In SSR: Provides a redis cache that is shared across app processes and requests
* In CSR: Provides an in-memory cache that is shared across the app session
*/
export function useAppCache() {
const app = useNuxtApp()
return app.$appCache
}
/**
* Get value from app cache or resolve and set it
*/
export async function useAppCached<V = unknown>(
key: string,
resolver: () => MaybeAsync<V>,
options?: Parameters<ReturnType<typeof useAppCache>['set']>['2']
): Promise<V> {
const cache = useAppCache()
if (await cache.has(key)) {
return (await cache.get(key)) as V
}
const data = await Promise.resolve(resolver())
await cache.set(key, data, options)
return data
}
@@ -35,7 +35,7 @@ const documents = {
"\n fragment ProjectModelsPageResults_Project on Project {\n ...ProjectPageLatestItemsModels\n }\n": types.ProjectModelsPageResults_ProjectFragmentDoc,
"\n fragment ProjectPageProjectHeader on Project {\n id\n role\n name\n description\n visibility\n allowPublicComments\n }\n": types.ProjectPageProjectHeaderFragmentDoc,
"\n fragment ProjectPageLatestItemsComments on Project {\n id\n commentThreadCount: commentThreads(limit: 0) {\n totalCount\n }\n }\n": types.ProjectPageLatestItemsCommentsFragmentDoc,
"\n fragment ProjectPageLatestItemsCommentItem on Comment {\n id\n author {\n ...FormUsersSelectItem\n }\n screenshot\n rawText\n createdAt\n updatedAt\n archived\n repliesCount: replies(limit: 0) {\n totalCount\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n ...LinkableComment\n }\n": types.ProjectPageLatestItemsCommentItemFragmentDoc,
"\n fragment ProjectPageLatestItemsCommentItem on Comment {\n id\n author {\n ...FormUsersSelectItem\n }\n screenshot\n rawText\n createdAt\n updatedAt\n archived\n repliesCount: replies(limit: 0) {\n totalCount\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n }\n": types.ProjectPageLatestItemsCommentItemFragmentDoc,
"\n fragment ProjectPageLatestItemsModels on Project {\n id\n role\n modelCount: models(limit: 0) {\n totalCount\n }\n }\n": types.ProjectPageLatestItemsModelsFragmentDoc,
"\n fragment ProjectPageModelsActions on Model {\n id\n name\n }\n": types.ProjectPageModelsActionsFragmentDoc,
"\n fragment ProjectPageModelsCardProject on Project {\n id\n role\n }\n": types.ProjectPageModelsCardProjectFragmentDoc,
@@ -163,6 +163,7 @@ const documents = {
"\n subscription OnViewerUserActivityBroadcasted(\n $target: ViewerUpdateTrackingTarget!\n $sessionId: String!\n ) {\n viewerUserActivityBroadcasted(target: $target, sessionId: $sessionId) {\n userName\n userId\n user {\n ...LimitedUserAvatar\n }\n state\n status\n sessionId\n }\n }\n": types.OnViewerUserActivityBroadcastedDocument,
"\n subscription OnViewerCommentsUpdated($target: ViewerUpdateTrackingTarget!) {\n projectCommentsUpdated(target: $target) {\n id\n type\n comment {\n id\n parent {\n id\n }\n ...ViewerCommentThread\n }\n }\n }\n": types.OnViewerCommentsUpdatedDocument,
"\n fragment LinkableComment on Comment {\n id\n viewerResources {\n modelId\n versionId\n objectId\n }\n }\n": types.LinkableCommentFragmentDoc,
"\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n comment(id: $commentId, streamId: $projectId) {\n ...LinkableComment\n }\n }\n": types.ResolveCommentLinkDocument,
"\n fragment ProjectPageProject on Project {\n id\n createdAt\n ...ProjectPageProjectHeader\n ...ProjectPageStatsBlockTeam\n ...ProjectPageTeamDialog\n ...ProjectPageStatsBlockVersions\n ...ProjectPageStatsBlockModels\n ...ProjectPageStatsBlockComments\n ...ProjectPageLatestItemsModels\n ...ProjectPageLatestItemsComments\n }\n": types.ProjectPageProjectFragmentDoc,
"\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n }\n": types.ModelPageProjectFragmentDoc,
};
@@ -272,7 +273,7 @@ export function graphql(source: "\n fragment ProjectPageLatestItemsComments on
/**
* 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 ProjectPageLatestItemsCommentItem on Comment {\n id\n author {\n ...FormUsersSelectItem\n }\n screenshot\n rawText\n createdAt\n updatedAt\n archived\n repliesCount: replies(limit: 0) {\n totalCount\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n ...LinkableComment\n }\n"): (typeof documents)["\n fragment ProjectPageLatestItemsCommentItem on Comment {\n id\n author {\n ...FormUsersSelectItem\n }\n screenshot\n rawText\n createdAt\n updatedAt\n archived\n repliesCount: replies(limit: 0) {\n totalCount\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n ...LinkableComment\n }\n"];
export function graphql(source: "\n fragment ProjectPageLatestItemsCommentItem on Comment {\n id\n author {\n ...FormUsersSelectItem\n }\n screenshot\n rawText\n createdAt\n updatedAt\n archived\n repliesCount: replies(limit: 0) {\n totalCount\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n }\n"): (typeof documents)["\n fragment ProjectPageLatestItemsCommentItem on Comment {\n id\n author {\n ...FormUsersSelectItem\n }\n screenshot\n rawText\n createdAt\n updatedAt\n archived\n repliesCount: replies(limit: 0) {\n totalCount\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -781,6 +782,10 @@ export function graphql(source: "\n subscription OnViewerCommentsUpdated($targe
* 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 LinkableComment on Comment {\n id\n viewerResources {\n modelId\n versionId\n objectId\n }\n }\n"): (typeof documents)["\n fragment LinkableComment on Comment {\n id\n viewerResources {\n modelId\n versionId\n objectId\n }\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 query ResolveCommentLink($commentId: String!, $projectId: String!) {\n comment(id: $commentId, streamId: $projectId) {\n ...LinkableComment\n }\n }\n"): (typeof documents)["\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n comment(id: $commentId, streamId: $projectId) {\n ...LinkableComment\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -2953,7 +2953,7 @@ export type ProjectPageProjectHeaderFragment = { __typename?: 'Project', id: str
export type ProjectPageLatestItemsCommentsFragment = { __typename?: 'Project', id: string, commentThreadCount: { __typename?: 'ProjectCommentCollection', totalCount: number } };
export type ProjectPageLatestItemsCommentItemFragment = { __typename?: 'Comment', id: string, screenshot?: string | null, rawText: string, createdAt: string, updatedAt: string, archived: boolean, author: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, repliesCount: { __typename?: 'CommentCollection', totalCount: number }, replyAuthors: { __typename?: 'CommentReplyAuthorCollection', totalCount: number, items: Array<{ __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }> }, viewerResources: Array<{ __typename?: 'ViewerResourceItem', modelId?: string | null, versionId?: string | null, objectId: string }> };
export type ProjectPageLatestItemsCommentItemFragment = { __typename?: 'Comment', id: string, screenshot?: string | null, rawText: string, createdAt: string, updatedAt: string, archived: boolean, author: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, repliesCount: { __typename?: 'CommentCollection', totalCount: number }, replyAuthors: { __typename?: 'CommentReplyAuthorCollection', totalCount: number, items: Array<{ __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }> } };
export type ProjectPageLatestItemsModelsFragment = { __typename?: 'Project', id: string, role?: string | null, modelCount: { __typename?: 'ModelCollection', totalCount: number } };
@@ -3348,7 +3348,7 @@ export type ProjectLatestCommentThreadsQueryVariables = Exact<{
}>;
export type ProjectLatestCommentThreadsQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, commentThreads: { __typename?: 'ProjectCommentCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Comment', id: string, screenshot?: string | null, rawText: string, createdAt: string, updatedAt: string, archived: boolean, author: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, repliesCount: { __typename?: 'CommentCollection', totalCount: number }, replyAuthors: { __typename?: 'CommentReplyAuthorCollection', totalCount: number, items: Array<{ __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }> }, viewerResources: Array<{ __typename?: 'ViewerResourceItem', modelId?: string | null, versionId?: string | null, objectId: string }> }> } } };
export type ProjectLatestCommentThreadsQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, commentThreads: { __typename?: 'ProjectCommentCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Comment', id: string, screenshot?: string | null, rawText: string, createdAt: string, updatedAt: string, archived: boolean, author: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, repliesCount: { __typename?: 'CommentCollection', totalCount: number }, replyAuthors: { __typename?: 'CommentReplyAuthorCollection', totalCount: number, items: Array<{ __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }> } }> } } };
export type ProjectInviteQueryVariables = Exact<{
projectId: Scalars['String'];
@@ -3682,6 +3682,14 @@ export type OnViewerCommentsUpdatedSubscription = { __typename?: 'Subscription',
export type LinkableCommentFragment = { __typename?: 'Comment', id: string, viewerResources: Array<{ __typename?: 'ViewerResourceItem', modelId?: string | null, versionId?: string | null, objectId: string }> };
export type ResolveCommentLinkQueryVariables = Exact<{
commentId: Scalars['String'];
projectId: Scalars['String'];
}>;
export type ResolveCommentLinkQuery = { __typename?: 'Query', comment?: { __typename?: 'Comment', id: string, viewerResources: Array<{ __typename?: 'ViewerResourceItem', modelId?: string | null, versionId?: string | null, objectId: string }> } | null };
export type ProjectPageProjectFragment = { __typename?: 'Project', id: string, createdAt: string, role?: string | null, name: string, description?: string | null, visibility: ProjectVisibility, allowPublicComments: boolean, team: Array<{ __typename?: 'ProjectCollaborator', role: string, user: { __typename?: 'LimitedUser', role?: string | null, id: string, name: string, avatar?: string | null } }>, invitedTeam?: Array<{ __typename?: 'PendingStreamCollaborator', id: string, title: string, inviteId: string, role: string, user?: { __typename?: 'LimitedUser', role?: string | null, id: string, name: string, avatar?: string | null } | null }> | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, modelCount: { __typename?: 'ModelCollection', totalCount: number }, commentThreadCount: { __typename?: 'ProjectCommentCollection', totalCount: number } };
export type ModelPageProjectFragment = { __typename?: 'Project', id: string, createdAt: string, name: string };
@@ -3709,8 +3717,7 @@ export const FormUsersSelectItemFragmentDoc = {"kind":"Document","definitions":[
export const ProjectModelsPageHeader_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectModelsPageHeader_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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApps"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormUsersSelectItem"}}]}}]}}]}}]} as unknown as DocumentNode<ProjectModelsPageHeader_ProjectFragment, unknown>;
export const ProjectPageLatestItemsModelsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModels"},"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":"role"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode<ProjectPageLatestItemsModelsFragment, unknown>;
export const ProjectModelsPageResults_ProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectModelsPageResults_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModels"}}]}}]} as unknown as DocumentNode<ProjectModelsPageResults_ProjectFragment, unknown>;
export const LinkableCommentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LinkableComment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"viewerResources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelId"}},{"kind":"Field","name":{"kind":"Name","value":"versionId"}},{"kind":"Field","name":{"kind":"Name","value":"objectId"}}]}}]}}]} as unknown as DocumentNode<LinkableCommentFragment, unknown>;
export const ProjectPageLatestItemsCommentItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsCommentItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormUsersSelectItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"screenshot"}},{"kind":"Field","name":{"kind":"Name","value":"rawText"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"archived"}},{"kind":"Field","alias":{"kind":"Name","value":"repliesCount"},"name":{"kind":"Name","value":"replies"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"replyAuthors"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormUsersSelectItem"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LinkableComment"}}]}}]} as unknown as DocumentNode<ProjectPageLatestItemsCommentItemFragment, unknown>;
export const ProjectPageLatestItemsCommentItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsCommentItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormUsersSelectItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"screenshot"}},{"kind":"Field","name":{"kind":"Name","value":"rawText"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"archived"}},{"kind":"Field","alias":{"kind":"Name","value":"repliesCount"},"name":{"kind":"Name","value":"replies"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"replyAuthors"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormUsersSelectItem"}}]}}]}}]}}]} as unknown as DocumentNode<ProjectPageLatestItemsCommentItemFragment, unknown>;
export const ModelPreviewFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelPreview"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}}]}}]} as unknown as DocumentNode<ModelPreviewFragment, unknown>;
export const ProjectPageModelsCardRenameDialogFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"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"}}]}}]} as unknown as DocumentNode<ProjectPageModelsCardRenameDialogFragment, unknown>;
export const ProjectPageModelsCardDeleteDialogFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]} as unknown as DocumentNode<ProjectPageModelsCardDeleteDialogFragment, unknown>;
@@ -3737,6 +3744,7 @@ export const ViewerCommentsReplyItemFragmentDoc = {"kind":"Document","definition
export const ViewerCommentsListItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerCommentsListItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rawText"}},{"kind":"Field","name":{"kind":"Name","value":"archived"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"viewedAt"}},{"kind":"Field","name":{"kind":"Name","value":"replies"},"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":"FragmentSpread","name":{"kind":"Name","value":"ViewerCommentsReplyItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"replyAuthors"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FormUsersSelectItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"resourceId"}},{"kind":"Field","name":{"kind":"Name","value":"resourceType"}}]}}]}}]} as unknown as DocumentNode<ViewerCommentsListItemFragment, unknown>;
export const ViewerCommentBubblesDataFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerCommentBubblesData"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"viewedAt"}},{"kind":"Field","name":{"kind":"Name","value":"viewerState"}}]}}]} as unknown as DocumentNode<ViewerCommentBubblesDataFragment, unknown>;
export const ViewerCommentThreadFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewerCommentThread"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerCommentsListItem"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerCommentBubblesData"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerCommentsReplyItem"}}]}}]} as unknown as DocumentNode<ViewerCommentThreadFragment, unknown>;
export const LinkableCommentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LinkableComment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"viewerResources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelId"}},{"kind":"Field","name":{"kind":"Name","value":"versionId"}},{"kind":"Field","name":{"kind":"Name","value":"objectId"}}]}}]}}]} as unknown as DocumentNode<LinkableCommentFragment, unknown>;
export const ProjectPageTeamDialogFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageTeamDialog"},"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]}}]} as unknown as DocumentNode<ProjectPageTeamDialogFragment, unknown>;
export const ProjectPageStatsBlockTeamFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageStatsBlockTeam"},"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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageTeamDialog"}}]}}]} as unknown as DocumentNode<ProjectPageStatsBlockTeamFragment, unknown>;
export const ProjectPageStatsBlockVersionsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageStatsBlockVersions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode<ProjectPageStatsBlockVersionsFragment, unknown>;
@@ -3795,7 +3803,7 @@ export const ProjectLatestModelsPaginationDocument = {"kind":"Document","definit
export const ProjectModelsTreeTopLevelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelsTreeTopLevel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectModelsTreeFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"modelsTree"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"NullValue"}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"8"}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"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":"FragmentSpread","name":{"kind":"Name","value":"SingleLevelModelTreeItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}}]}},...SingleLevelModelTreeItemFragmentDoc.definitions,...ProjectPageLatestItemsModelItemFragmentDoc.definitions,...PendingFileUploadFragmentDoc.definitions,...ProjectPageModelsCardRenameDialogFragmentDoc.definitions,...ProjectPageModelsCardDeleteDialogFragmentDoc.definitions,...ProjectPageModelsActionsFragmentDoc.definitions,...ModelCardAutomationStatus_ModelFragmentDoc.definitions,...ModelCardAutomationStatus_AutomationsStatusFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelsTreeTopLevelQuery, ProjectModelsTreeTopLevelQueryVariables>;
export const ProjectModelsTreeTopLevelPaginationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelsTreeTopLevelPagination"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectModelsTreeFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}},"defaultValue":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"modelsTree"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"8"}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"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":"FragmentSpread","name":{"kind":"Name","value":"SingleLevelModelTreeItem"}}]}}]}}]}}]}},...SingleLevelModelTreeItemFragmentDoc.definitions,...ProjectPageLatestItemsModelItemFragmentDoc.definitions,...PendingFileUploadFragmentDoc.definitions,...ProjectPageModelsCardRenameDialogFragmentDoc.definitions,...ProjectPageModelsCardDeleteDialogFragmentDoc.definitions,...ProjectPageModelsActionsFragmentDoc.definitions,...ModelCardAutomationStatus_ModelFragmentDoc.definitions,...ModelCardAutomationStatus_AutomationsStatusFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelsTreeTopLevelPaginationQuery, ProjectModelsTreeTopLevelPaginationQueryVariables>;
export const ProjectModelChildrenTreeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelChildrenTree"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"parentName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"modelChildrenTree"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"fullName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"parentName"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SingleLevelModelTreeItem"}}]}}]}}]}},...SingleLevelModelTreeItemFragmentDoc.definitions,...ProjectPageLatestItemsModelItemFragmentDoc.definitions,...PendingFileUploadFragmentDoc.definitions,...ProjectPageModelsCardRenameDialogFragmentDoc.definitions,...ProjectPageModelsCardDeleteDialogFragmentDoc.definitions,...ProjectPageModelsActionsFragmentDoc.definitions,...ModelCardAutomationStatus_ModelFragmentDoc.definitions,...ModelCardAutomationStatus_AutomationsStatusFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelChildrenTreeQuery, ProjectModelChildrenTreeQueryVariables>;
export const ProjectLatestCommentThreadsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectLatestCommentThreads"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}},"defaultValue":{"kind":"NullValue"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCommentsFilter"}},"defaultValue":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"8"}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"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":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsCommentItem"}}]}}]}}]}}]}},...ProjectPageLatestItemsCommentItemFragmentDoc.definitions,...FormUsersSelectItemFragmentDoc.definitions,...LinkableCommentFragmentDoc.definitions]} as unknown as DocumentNode<ProjectLatestCommentThreadsQuery, ProjectLatestCommentThreadsQueryVariables>;
export const ProjectLatestCommentThreadsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectLatestCommentThreads"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}},"defaultValue":{"kind":"NullValue"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCommentsFilter"}},"defaultValue":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"8"}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"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":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsCommentItem"}}]}}]}}]}}]}},...ProjectPageLatestItemsCommentItemFragmentDoc.definitions,...FormUsersSelectItemFragmentDoc.definitions]} as unknown as DocumentNode<ProjectLatestCommentThreadsQuery, ProjectLatestCommentThreadsQueryVariables>;
export const ProjectInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsInviteBanner"}}]}}]}},...ProjectsInviteBannerFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions]} as unknown as DocumentNode<ProjectInviteQuery, ProjectInviteQueryVariables>;
export const ProjectModelCheckDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelCheck"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"model"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode<ProjectModelCheckQuery, ProjectModelCheckQueryVariables>;
export const ProjectModelPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"versionsCursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageHeaderProject"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageVersionsProject"}}]}}]}},...ProjectModelPageHeaderProjectFragmentDoc.definitions,...ProjectModelPageVersionsProjectFragmentDoc.definitions,...ProjectPageProjectHeaderFragmentDoc.definitions,...PendingFileUploadFragmentDoc.definitions,...ProjectModelPageVersionsPaginationFragmentDoc.definitions,...ProjectModelPageVersionsCardVersionFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions,...ProjectModelPageDialogDeleteVersionFragmentDoc.definitions,...ProjectModelPageDialogMoveToVersionFragmentDoc.definitions,...ModelCardAutomationStatus_VersionFragmentDoc.definitions,...ModelCardAutomationStatus_AutomationsStatusFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelPageQuery, ProjectModelPageQueryVariables>;
@@ -3837,4 +3845,5 @@ export const ViewerModelVersionsDocument = {"kind":"Document","definitions":[{"k
export const ViewerDiffVersionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ViewerDiffVersions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"versionAId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"versionBId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","alias":{"kind":"Name","value":"versionA"},"name":{"kind":"Name","value":"version"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"versionAId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerModelVersionCardItem"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"versionB"},"name":{"kind":"Name","value":"version"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"versionBId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerModelVersionCardItem"}}]}}]}}]}}]}},...ViewerModelVersionCardItemFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions]} as unknown as DocumentNode<ViewerDiffVersionsQuery, ViewerDiffVersionsQueryVariables>;
export const ViewerLoadedThreadsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ViewerLoadedThreads"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCommentsFilter"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"25"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalArchivedCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerCommentThread"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LinkableComment"}}]}}]}}]}}]}},...ViewerCommentThreadFragmentDoc.definitions,...ViewerCommentsListItemFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions,...ViewerCommentsReplyItemFragmentDoc.definitions,...ThreadCommentAttachmentFragmentDoc.definitions,...FormUsersSelectItemFragmentDoc.definitions,...ViewerCommentBubblesDataFragmentDoc.definitions,...LinkableCommentFragmentDoc.definitions]} as unknown as DocumentNode<ViewerLoadedThreadsQuery, ViewerLoadedThreadsQueryVariables>;
export const OnViewerUserActivityBroadcastedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnViewerUserActivityBroadcasted"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"target"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ViewerUpdateTrackingTarget"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewerUserActivityBroadcasted"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"target"},"value":{"kind":"Variable","name":{"kind":"Name","value":"target"}}},{"kind":"Argument","name":{"kind":"Name","value":"sessionId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userName"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"sessionId"}}]}}]}},...LimitedUserAvatarFragmentDoc.definitions]} as unknown as DocumentNode<OnViewerUserActivityBroadcastedSubscription, OnViewerUserActivityBroadcastedSubscriptionVariables>;
export const OnViewerCommentsUpdatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnViewerCommentsUpdated"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"target"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ViewerUpdateTrackingTarget"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectCommentsUpdated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"target"},"value":{"kind":"Variable","name":{"kind":"Name","value":"target"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"comment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerCommentThread"}}]}}]}}]}},...ViewerCommentThreadFragmentDoc.definitions,...ViewerCommentsListItemFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions,...ViewerCommentsReplyItemFragmentDoc.definitions,...ThreadCommentAttachmentFragmentDoc.definitions,...FormUsersSelectItemFragmentDoc.definitions,...ViewerCommentBubblesDataFragmentDoc.definitions]} as unknown as DocumentNode<OnViewerCommentsUpdatedSubscription, OnViewerCommentsUpdatedSubscriptionVariables>;
export const OnViewerCommentsUpdatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnViewerCommentsUpdated"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"target"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ViewerUpdateTrackingTarget"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectCommentsUpdated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"target"},"value":{"kind":"Variable","name":{"kind":"Name","value":"target"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"comment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewerCommentThread"}}]}}]}}]}},...ViewerCommentThreadFragmentDoc.definitions,...ViewerCommentsListItemFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions,...ViewerCommentsReplyItemFragmentDoc.definitions,...ThreadCommentAttachmentFragmentDoc.definitions,...FormUsersSelectItemFragmentDoc.definitions,...ViewerCommentBubblesDataFragmentDoc.definitions]} as unknown as DocumentNode<OnViewerCommentsUpdatedSubscription, OnViewerCommentsUpdatedSubscriptionVariables>;
export const ResolveCommentLinkDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ResolveCommentLink"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"commentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"commentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LinkableComment"}}]}}]}},...LinkableCommentFragmentDoc.definitions]} as unknown as DocumentNode<ResolveCommentLinkQuery, ResolveCommentLinkQueryVariables>;
@@ -30,6 +30,9 @@ export const projectWebhooksRoute = (projectId: string) =>
export const automationDataPageRoute = (baseUrl: string, automationId: string) =>
new URL(`/automations/${automationId}`, baseUrl).toString()
export const threadRedirectRoute = (projectId: string, threadId: string) =>
`/projects/${projectId}/threads/${threadId}`
/**
* TODO: Page doesn't exist
*/
@@ -5,7 +5,7 @@ import type {
CommentContentInput,
LinkableCommentFragment
} from '~~/lib/common/generated/gql/graphql'
import { modelRoute } from '~~/lib/common/helpers/route'
import { modelRoute, threadRedirectRoute } from '~~/lib/common/helpers/route'
import type { CommentEditorValue } from '~~/lib/viewer/composables/commentManagement'
import { ViewerHashStateKeys } from '~~/lib/viewer/composables/setup/urlHashState'
@@ -41,6 +41,14 @@ graphql(`
}
`)
/**
* Resolving the actual full link requires viewerResources which are pretty heavy to fetch.
* This link defers viewerResources resolution to when the link is actually clicked
*/
export function getLightLinkToThread(projectId: string, threadId: string) {
return threadRedirectRoute(projectId, threadId)
}
export function getLinkToThread(projectId: string, thread: LinkableCommentFragment) {
if (!thread.viewerResources.length) return undefined
const sortedResources = sortBy(thread.viewerResources, (r) => {
+50
View File
@@ -0,0 +1,50 @@
import { useApolloClientFromNuxt } from '~/lib/common/composables/graphql'
import { graphql } from '~/lib/common/generated/gql'
import { convertThrowIntoFetchResult } from '~/lib/common/helpers/graphql'
import { getLinkToThread } from '~/lib/viewer/helpers/comments'
const resolveLinkQuery = graphql(`
query ResolveCommentLink($commentId: String!, $projectId: String!) {
comment(id: $commentId, streamId: $projectId) {
...LinkableComment
}
}
`)
export default defineNuxtRouteMiddleware(async (to) => {
const client = useApolloClientFromNuxt()
const threadId = to.params.threadId as string
const projectId = to.params.id as string
const res = await client
.query({
query: resolveLinkQuery,
variables: {
commentId: threadId,
projectId
}
})
.catch(convertThrowIntoFetchResult)
const comment = res.data?.comment
if (!comment) {
return abortNavigation(
createError({
message: 'Comment thread not found',
statusCode: 404
})
)
}
const link = getLinkToThread(projectId, comment)
if (!link) {
return abortNavigation(
createError({
message: 'Comment thread not found',
statusCode: 404
})
)
}
return navigateTo(link)
})
+5 -2
View File
@@ -39,6 +39,7 @@ export default defineNuxtConfig({
'@artmizu/nuxt-prometheus'
],
runtimeConfig: {
redisUrl: '',
public: {
apiOrigin: 'UNDEFINED',
backendApiOrigin: '',
@@ -105,8 +106,10 @@ export default defineNuxtConfig({
},
// older chrome version for CEF 65 support. all identifiers except the chrome one are default ones.
target: ['es2020', 'edge88', 'firefox78', 'chrome65', 'safari14']
// optionally disable minification for debugging
// minify: false
// // optionally disable minification for debugging
// minify: false,
// // optionally enable sourcemaps for debugging
// sourcemap: 'inline'
},
plugins: [
// again - only for CEF 65
+1
View File
@@ -50,6 +50,7 @@
"apollo-upload-client": "^17.0.0",
"dayjs": "^1.11.7",
"graphql": "^16.6.0",
"ioredis": "^5.3.2",
"js-cookie": "^3.0.1",
"lodash-es": "^4.17.21",
"mitt": "^3.0.0",
+71 -63
View File
@@ -54,7 +54,6 @@
</div>
</template>
<script setup lang="ts">
// import { useActiveUser } from '~~/lib/auth/composables/activeUser'
import type { ConnectorTag, ConnectorVersion, Tag } from '~~/lib/connectors'
import { MagnifyingGlassIcon } from '@heroicons/vue/24/solid'
@@ -62,75 +61,84 @@ useHead({
title: 'Speckle Connectors'
})
const response = await useFetch(
const spacesEndpoint = 'https://releases.speckle.dev'
const cmsTagsEndpoint =
'https://speckle.systems/ghost/api/v3/content/tags?key=c895981da23dbb5c87ee7192e2&limit=all'
const connectorTags = await useAppCached(
'connector-downloads',
async () => {
const cmsTags = await $fetch<{
tags: Record<string, unknown>[]
}>(cmsTagsEndpoint)
const relevantTags = cmsTags.tags.filter((tag) => {
if (!tag.codeinjection_head) return false
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, camelcase
tag.codeinjection_head = (tag.codeinjection_head as string).replace(/\s/g, '')
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
if ((tag.codeinjection_head as string).match(/(window.connectorTag=true)/))
return true
}) as Tag[]
const connectorTags = await Promise.all(
relevantTags.map(async (tag) => {
const connectorTag = { ...tag } as ConnectorTag
const community = tag.codeinjection_head.match(/window.community="([\s\S]*?)"/)
connectorTag.communityProvider = community ? community[1] : undefined
connectorTag.isCommunity = !!connectorTag.communityProvider
const installLink = tag.codeinjection_head.match(
/window.installLink="([\s\S]*?)"/
)
connectorTag.installLink = installLink ? installLink[1] : undefined
try {
if (connectorTag.installLink?.includes('SpeckleManager')) {
connectorTag.directDownload = true
const tagFeed = await $fetch<{
Versions: ConnectorVersion[]
}>(`${spacesEndpoint}/manager2/feeds/${tag.slug}.json`)
const versions = tagFeed.Versions.sort(
(a, b) => new Date(b.Date).getTime() - new Date(a.Date).getTime()
)
connectorTag.versions = versions && versions.length > 0 ? versions : []
connectorTag.stable = versions.find((x) => !x.Prerelease)?.Number
} else {
connectorTag.directDownload = false
connectorTag.versions = []
}
} catch (e) {
connectorTag.directDownload = false
connectorTag.versions = []
// gotta catch 'em all!
}
return connectorTag
})
)
connectorTags.sort((a, b) => {
return b.versions.length - a.versions.length
})
return connectorTags
},
{
expiryMs:
// Cache for an hour
1000 * 60 * 60
}
)
// useLazyFetch
const showManagerDownloadDialog = ref(false)
const spacesEndpoint = 'https://releases.speckle.dev'
const relevantTags = (
(response.data.value as Record<string, unknown>).tags as Record<string, unknown>[]
).filter((tag) => {
if (!tag.codeinjection_head) return false
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, camelcase
tag.codeinjection_head = (tag.codeinjection_head as string).replace(/\s/g, '')
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
if ((tag.codeinjection_head as string).match(/(window.connectorTag=true)/))
return true
}) as Tag[]
const connectorTags = ref<ConnectorTag[]>([])
for (const tag of relevantTags) {
const connectorTag = { ...tag } as ConnectorTag
const community = tag.codeinjection_head.match(/window.community="([\s\S]*?)"/)
connectorTag.communityProvider = community ? community[1] : undefined
connectorTag.isCommunity = !!connectorTag.communityProvider
const installLink = tag.codeinjection_head.match(/window.installLink="([\s\S]*?)"/)
connectorTag.installLink = installLink ? installLink[1] : undefined
try {
if (connectorTag.installLink?.includes('SpeckleManager')) {
connectorTag.directDownload = true
const { data } = await useFetch(
`${spacesEndpoint}/manager2/feeds/${tag.slug}.json`
)
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const versions = (data.value as { Versions: ConnectorVersion[] }).Versions.sort(
(a: ConnectorVersion, b: ConnectorVersion) =>
new Date(b.Date).getTime() - new Date(a.Date).getTime()
)
connectorTag.versions = versions && versions.length > 0 ? versions : []
connectorTag.stable = versions.find((x) => !x.Prerelease)?.Number
} else {
connectorTag.directDownload = false
connectorTag.versions = []
}
} catch (e) {
connectorTag.directDownload = false
connectorTag.versions = []
// gotta catch 'em all!
}
connectorTags.value.push(connectorTag)
}
connectorTags.value = connectorTags.value.sort((a, b) => {
return b.versions.length - a.versions.length
})
const searchString = ref<string>()
const searchResults = computed(() => {
if (!searchString.value) return connectorTags.value
return connectorTags.value.filter((t) =>
if (!searchString.value) return connectorTags
return connectorTags.filter((t) =>
t.name.toLowerCase().includes(searchString.value?.toLowerCase() as string)
)
})
@@ -0,0 +1,13 @@
<template>
<div />
</template>
<script setup lang="ts">
/**
* Utility route that allows us to generate comment links w/o having to fully
* resolve them from viewerResources first (which is a heavy operation)
*/
definePageMeta({
middleware: 'thread'
})
</script>
@@ -0,0 +1,15 @@
import { Redis } from 'ioredis'
/**
* Provide redis (only in SSR)
*/
export default defineNuxtPlugin(() => {
const { redisUrl } = useRuntimeConfig()
const redis = redisUrl?.length ? new Redis(redisUrl) : undefined
return {
provide: {
redis
}
}
})
+179
View File
@@ -0,0 +1,179 @@
/* eslint-disable @typescript-eslint/require-await */
import type { Optional } from '@speckle/shared'
import { has as objectHas } from 'lodash-es'
import type { Redis } from 'ioredis'
type AsyncCacheInterface = {
has(key: string): Promise<boolean>
get<V = unknown>(key: string): Promise<V | undefined>
set<V = unknown>(key: string, val: V, options?: { expiryMs: number }): Promise<void>
setMultiple<V = unknown>(
keyVals: Record<string, V>,
options?: { expiryMs: number }
): Promise<void>
getMultiple(keys: string[]): Promise<Record<string, unknown>>
}
let internalCache: Optional<AsyncCacheInterface> = undefined
const getOrInitInternalCache = async (params: { redis: Optional<Redis> }) => {
if (internalCache) return internalCache
if (params.redis) {
const client = params.redis
const redisKeyPrefix = 'fe2-app-cache:'
const finalKey = (key: string) => redisKeyPrefix + key
internalCache = {
has: async (key) => {
const exists = await client.exists(finalKey(key))
return !!exists
},
set: async (key, val, options) => {
if (options?.expiryMs) {
await client.set(finalKey(key), JSON.stringify(val), 'PX', options.expiryMs)
} else {
await client.set(finalKey(key), JSON.stringify(val))
}
},
get: async <V = unknown>(key: string) => {
const val = await client.get(finalKey(key))
if (!val) return undefined
return JSON.parse(val) as V
},
setMultiple: async (keyVals, options) => {
const entries = Object.entries(keyVals).map(([key, val]) => [
finalKey(key),
JSON.stringify(val)
])
if (options?.expiryMs) {
await client.mset(...entries.flat(), 'PX', options.expiryMs)
} else {
await client.mset(...entries.flat())
}
},
getMultiple: async (keys) => {
if (!keys?.length) return {}
const finalKeys = keys.map(finalKey)
const vals = await client.mget(...finalKeys)
const keyVals = {} as Record<string, unknown>
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const val = vals[i]
if (!val) continue
keyVals[key] = JSON.parse(val)
}
return keyVals
}
}
} else {
const cache: Record<string, unknown> = {}
internalCache = {
has: async (key) => objectHas(cache, key),
set: async (key, val, options) => {
cache[key] = val
if (options?.expiryMs) {
setTimeout(() => {
delete cache[key]
}, options.expiryMs)
}
},
get: async <V = unknown>(key: string) => {
if (!objectHas(cache, key)) return undefined
const val = cache[key] as V
return val
},
setMultiple: async (keyVals, options) => {
Object.assign(cache, keyVals)
if (options?.expiryMs) {
setTimeout(() => {
for (const key of Object.keys(keyVals)) {
delete cache[key]
}
}, options.expiryMs)
}
},
getMultiple: async (keys) => {
const keyVals = {} as Record<string, unknown>
for (const key of keys) {
if (!objectHas(cache, key)) continue
keyVals[key] = cache[key]
}
return keyVals
}
}
}
return internalCache
}
/**
* In SSR: Provides a redis cache that is shared across app processes and requests
* In CSR: Provides an in-memory cache that is shared across the app session
*/
export default defineNuxtPlugin(async (nuxtApp) => {
const internalCache = await getOrInitInternalCache({
redis: nuxtApp.$redis as Redis
})
const reqTouched: Record<string, boolean> = {}
if (process.server) {
nuxtApp.hook('app:rendered', async () => {
const touchedKeys = Object.keys(reqTouched)
const cacheToSend = await internalCache.getMultiple(touchedKeys)
nuxtApp.ssrContext!.payload.appCache = cacheToSend
})
} else if (process.client) {
const restorable = window.__NUXT__?.appCache as Optional<Record<string, unknown>>
if (restorable) {
await internalCache.setMultiple(restorable)
}
}
const finalCache: AsyncCacheInterface = {
has: async (key) => {
const has = await internalCache.has(key)
return has
},
set: async (key, val, options) => {
await internalCache.set(key, val, options)
reqTouched[key] = true
},
get: async <V = unknown>(key: string) => {
const val = await internalCache.get<V>(key)
reqTouched[key] = true
return val
},
setMultiple: async (keyVals, options) => {
await internalCache.setMultiple(keyVals, options)
for (const key of Object.keys(keyVals)) {
reqTouched[key] = true
}
},
getMultiple: async (keys) => {
const keyVals = await internalCache.getMultiple(keys)
for (const key of keys) {
reqTouched[key] = true
}
return keyVals
}
}
return {
provide: {
appCache: finalCache
}
}
})
@@ -86,7 +86,16 @@ module.exports = {
}
},
Comment: {
async replies(parent, args) {
async replies(parent, args, ctx) {
// If limit=0, short-cut full execution and use data loader
if (args.limit === 0) {
return {
totalCount: await ctx.loaders.comments.getReplyCount.load(parent.id),
items: [],
cursor: null
}
}
const resources = [{ resourceId: parent.id, resourceType: 'comment' }]
return await getComments({
resources,
@@ -28,7 +28,16 @@ import { CommitNotFoundError } from '@/modules/core/errors/commit'
export = {
Project: {
async models(parent, args) {
async models(parent, args, ctx) {
// If limit=0 & no filter, short-cut full execution and use data loader
if (args.limit === 0 && !args.filter) {
return {
totalCount: await ctx.loaders.streams.getBranchCount.load(parent.id),
items: [],
cursor: null
}
}
return await getPaginatedProjectModels(parent.id, args)
},
async model(_parent, args, ctx) {
@@ -58,7 +67,18 @@ export = {
loadedVersionsOnly
})
},
async versions(parent, args) {
async versions(parent, args, ctx) {
// If limit=0, short-cut full execution and use data loader
if (args.limit === 0) {
return {
totalCount: await ctx.loaders.streams.getCommitCountWithoutGlobals.load(
parent.id
),
items: [],
cursor: null
}
}
return await getPaginatedStreamCommits(parent.id, args)
}
},
@@ -83,7 +103,16 @@ export = {
async displayName(parent) {
return last(parent.name.split('/'))
},
async versions(parent, args) {
async versions(parent, args, ctx) {
// If limit=0 & no filter, short-cut full execution and use data loader
if (!args.filter && args.limit === 0) {
return {
totalCount: await ctx.loaders.branches.getCommitCount.load(parent.id),
items: [],
cursor: null
}
}
return await getPaginatedBranchCommits({
branchId: parent.id,
cursor: args.cursor,
+1 -1
View File
@@ -37,7 +37,7 @@ export function getLogger(
if (pretty) {
pinoOptions.transport = {
target: '../pinoPrettyTransport.js',
target: '@speckle/shared/pinoPrettyTransport.js',
options: {
colorize: true,
destination: 2, //stderr
@@ -45,6 +45,7 @@ services:
NUXT_PUBLIC_SERVER_NAME: 'TODO: change' # e.g. 'my-speckle-server'
NUXT_PUBLIC_API_ORIGIN: 'TODO: change' # e.g. 'http://127.0.0.1'
NUXT_PUBLIC_BACKEND_API_ORIGIN: 'http://speckle-server:3000'
NUXT_REDIS_URL: 'redis://redis'
speckle-server:
image: speckle/speckle-server:2
@@ -79,6 +79,11 @@ spec:
value: {{ .Values.frontend_2.logClientApiEndpoint }}
- name: NODE_TLS_REJECT_UNAUTHORIZED
value: {{ .Values.tlsRejectUnauthorized | quote }}
- name: NUXT_REDIS_URL
valueFrom:
secretKeyRef:
name: {{ default .Values.secretName .Values.redis.connectionString.secretName }}
key: {{ default "redis_url" .Values.redis.connectionString.secretKey }}
priorityClassName: high-priority
{{- if .Values.frontend_2.affinity }}
@@ -43,4 +43,6 @@ spec:
- ports:
- port: {{ printf "%s" ( include "server.port" $ | quote ) }}
{{- end -}}
# redis
{{ include "speckle.networkpolicy.egress.redis.cilium" $ | indent 4 }}
{{- end -}}
@@ -42,4 +42,6 @@ spec:
ports:
- port: 3000
{{- end -}}
# redis
{{ include "speckle.networkpolicy.egress.redis" $ | indent 4 }}
{{- end -}}
+1
View File
@@ -13626,6 +13626,7 @@ __metadata:
eslint-plugin-vue: ^9.18.1
eslint-plugin-vuejs-accessibility: ^1.2.0
graphql: ^16.6.0
ioredis: ^5.3.2
jest: 27
js-cookie: ^3.0.1
lodash-es: ^4.17.21