Merge branch 'main' into andrew/web-2865-implement-upgrade-confirmation-modal
This commit is contained in:
@@ -92,8 +92,20 @@ type WorkspacePlan {
|
||||
}
|
||||
|
||||
type WorkspaceSubscriptionSeats {
|
||||
plan: Int!
|
||||
guest: Int!
|
||||
plan: Int! @deprecated
|
||||
guest: Int! @deprecated
|
||||
"""
|
||||
Total number of seats purchased and available in the current subscription cycle
|
||||
"""
|
||||
totalCount: Int!
|
||||
"""
|
||||
Number assigned seats in the current billing cycle
|
||||
"""
|
||||
assigned: Int!
|
||||
"""
|
||||
Number of viewer seats currently assigned in the workspace
|
||||
"""
|
||||
viewersCount: Int!
|
||||
}
|
||||
|
||||
type WorkspaceSubscription {
|
||||
|
||||
@@ -84,6 +84,7 @@ generates:
|
||||
ServerRegionMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
ServerRegionItem: '@/modules/multiregion/helpers/graphTypes#ServerRegionItemGraphQLReturn'
|
||||
Price: '@/modules/gatekeeperCore/helpers/graphTypes#PriceGraphQLReturn'
|
||||
WorkspaceSubscription: '@/modules/gatekeeper/helpers/graphTypes#WorkspaceSubscriptionGraphQLReturn'
|
||||
modules/cross-server-sync/graph/generated/graphql.ts:
|
||||
plugins:
|
||||
- 'typescript'
|
||||
|
||||
@@ -6,7 +6,7 @@ import { PendingStreamCollaboratorGraphQLReturn } from '@/modules/serverinvites/
|
||||
import { FileUploadGraphQLReturn } from '@/modules/fileuploads/helpers/types';
|
||||
import { AutomateFunctionGraphQLReturn, AutomateFunctionReleaseGraphQLReturn, AutomationGraphQLReturn, AutomationRevisionGraphQLReturn, AutomationRevisionFunctionGraphQLReturn, AutomateRunGraphQLReturn, AutomationRunTriggerGraphQLReturn, AutomationRevisionTriggerDefinitionGraphQLReturn, AutomateFunctionRunGraphQLReturn, TriggeredAutomationsStatusGraphQLReturn, ProjectAutomationMutationsGraphQLReturn, ProjectTriggeredAutomationsStatusUpdatedMessageGraphQLReturn, ProjectAutomationsUpdatedMessageGraphQLReturn, UserAutomateInfoGraphQLReturn } from '@/modules/automate/helpers/graphTypes';
|
||||
import { WorkspaceGraphQLReturn, WorkspaceSsoGraphQLReturn, WorkspaceMutationsGraphQLReturn, WorkspaceJoinRequestMutationsGraphQLReturn, WorkspaceInviteMutationsGraphQLReturn, WorkspaceProjectMutationsGraphQLReturn, PendingWorkspaceCollaboratorGraphQLReturn, WorkspaceCollaboratorGraphQLReturn, WorkspaceJoinRequestGraphQLReturn, LimitedWorkspaceJoinRequestGraphQLReturn, ProjectRoleGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes';
|
||||
import { WorkspaceBillingMutationsGraphQLReturn } from '@/modules/gatekeeper/helpers/graphTypes';
|
||||
import { WorkspaceBillingMutationsGraphQLReturn, WorkspaceSubscriptionGraphQLReturn } from '@/modules/gatekeeper/helpers/graphTypes';
|
||||
import { WebhookGraphQLReturn } from '@/modules/webhooks/helpers/graphTypes';
|
||||
import { SmartTextEditorValueGraphQLReturn } from '@/modules/core/services/richTextEditorService';
|
||||
import { BlobStorageItem } from '@/modules/blobstorage/domain/types';
|
||||
@@ -4878,8 +4878,16 @@ export type WorkspaceSubscription = {
|
||||
|
||||
export type WorkspaceSubscriptionSeats = {
|
||||
__typename?: 'WorkspaceSubscriptionSeats';
|
||||
/** Number assigned seats in the current billing cycle */
|
||||
assigned: Scalars['Int']['output'];
|
||||
/** @deprecated Field no longer supported */
|
||||
guest: Scalars['Int']['output'];
|
||||
/** @deprecated Field no longer supported */
|
||||
plan: Scalars['Int']['output'];
|
||||
/** Total number of seats purchased and available in the current subscription cycle */
|
||||
totalCount: Scalars['Int']['output'];
|
||||
/** Number of viewer seats currently assigned in the workspace */
|
||||
viewersCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type WorkspaceTeamFilter = {
|
||||
@@ -5297,7 +5305,7 @@ export type ResolversTypes = {
|
||||
WorkspaceSso: ResolverTypeWrapper<WorkspaceSsoGraphQLReturn>;
|
||||
WorkspaceSsoProvider: ResolverTypeWrapper<WorkspaceSsoProvider>;
|
||||
WorkspaceSsoSession: ResolverTypeWrapper<WorkspaceSsoSession>;
|
||||
WorkspaceSubscription: ResolverTypeWrapper<WorkspaceSubscription>;
|
||||
WorkspaceSubscription: ResolverTypeWrapper<WorkspaceSubscriptionGraphQLReturn>;
|
||||
WorkspaceSubscriptionSeats: ResolverTypeWrapper<WorkspaceSubscriptionSeats>;
|
||||
WorkspaceTeamFilter: WorkspaceTeamFilter;
|
||||
WorkspaceUpdateInput: WorkspaceUpdateInput;
|
||||
@@ -5583,7 +5591,7 @@ export type ResolversParentTypes = {
|
||||
WorkspaceSso: WorkspaceSsoGraphQLReturn;
|
||||
WorkspaceSsoProvider: WorkspaceSsoProvider;
|
||||
WorkspaceSsoSession: WorkspaceSsoSession;
|
||||
WorkspaceSubscription: WorkspaceSubscription;
|
||||
WorkspaceSubscription: WorkspaceSubscriptionGraphQLReturn;
|
||||
WorkspaceSubscriptionSeats: WorkspaceSubscriptionSeats;
|
||||
WorkspaceTeamFilter: WorkspaceTeamFilter;
|
||||
WorkspaceUpdateInput: WorkspaceUpdateInput;
|
||||
@@ -7241,8 +7249,11 @@ export type WorkspaceSubscriptionResolvers<ContextType = GraphQLContext, ParentT
|
||||
};
|
||||
|
||||
export type WorkspaceSubscriptionSeatsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['WorkspaceSubscriptionSeats'] = ResolversParentTypes['WorkspaceSubscriptionSeats']> = {
|
||||
assigned?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
guest?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
plan?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
totalCount?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
viewersCount?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
|
||||
@@ -4858,8 +4858,16 @@ export type WorkspaceSubscription = {
|
||||
|
||||
export type WorkspaceSubscriptionSeats = {
|
||||
__typename?: 'WorkspaceSubscriptionSeats';
|
||||
/** Number assigned seats in the current billing cycle */
|
||||
assigned: Scalars['Int']['output'];
|
||||
/** @deprecated Field no longer supported */
|
||||
guest: Scalars['Int']['output'];
|
||||
/** @deprecated Field no longer supported */
|
||||
plan: Scalars['Int']['output'];
|
||||
/** Total number of seats purchased and available in the current subscription cycle */
|
||||
totalCount: Scalars['Int']['output'];
|
||||
/** Number of viewer seats currently assigned in the workspace */
|
||||
viewersCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type WorkspaceTeamFilter = {
|
||||
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
} from '@/modules/gatekeeper/repositories/workspaceSeat'
|
||||
import { assignWorkspaceSeatFactory } from '@/modules/workspaces/services/workspaceSeat'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { getTotalSeatsCountByPlanFactory } from '@/modules/gatekeeper/services/subscriptions'
|
||||
|
||||
const { FF_GATEKEEPER_MODULE_ENABLED, FF_BILLING_INTEGRATION_ENABLED } =
|
||||
getFeatureFlags()
|
||||
@@ -158,6 +159,45 @@ export = FF_GATEKEEPER_MODULE_ENABLED
|
||||
})
|
||||
}
|
||||
},
|
||||
WorkspaceSubscription: {
|
||||
seats: async (parent) => {
|
||||
const workspacePlan = await getWorkspacePlanFactory({ db })({
|
||||
workspaceId: parent.workspaceId
|
||||
})
|
||||
if (!workspacePlan || !isNewPlanType(workspacePlan.name)) {
|
||||
return {
|
||||
...calculateSubscriptionSeats({
|
||||
subscriptionData: parent.subscriptionData,
|
||||
guestSeatProductId: getWorkspacePlanProductId({
|
||||
workspacePlan: 'guest'
|
||||
})
|
||||
}),
|
||||
// These values have no reference in the old plans FF_WORKSPACES_NEW_PLANS_ENABLED
|
||||
totalCount: 0,
|
||||
assigned: 0
|
||||
}
|
||||
}
|
||||
// Only editor seats are considered
|
||||
const assignedSeatsCount = await countSeatsByTypeInWorkspaceFactory({ db })({
|
||||
workspaceId: parent.workspaceId,
|
||||
type: 'editor'
|
||||
})
|
||||
return {
|
||||
assigned: assignedSeatsCount,
|
||||
totalCount: getTotalSeatsCountByPlanFactory({ getWorkspacePlanProductId })({
|
||||
workspacePlan,
|
||||
subscriptionData: parent.subscriptionData
|
||||
}),
|
||||
viewersCount: await countSeatsByTypeInWorkspaceFactory({ db })({
|
||||
workspaceId: parent.workspaceId,
|
||||
type: 'viewer'
|
||||
}),
|
||||
// These values have no reference in the new plans
|
||||
guest: 0,
|
||||
plan: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
WorkspaceCollaborator: {
|
||||
seatType: async (parent, _args, context) => {
|
||||
const seat = await context.loaders
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import { MutationsObjectGraphQLReturn } from '@/modules/core/helpers/graphTypes'
|
||||
import { WorkspaceSubscription } from '@/modules/gatekeeper/domain/billing'
|
||||
import { Workspace } from '@/modules/workspacesCore/domain/types'
|
||||
|
||||
export type WorkspaceBillingMutationsGraphQLReturn = MutationsObjectGraphQLReturn
|
||||
export type WorkspaceSubscriptionGraphQLReturn = WorkspaceSubscription & {
|
||||
parent: Workspace
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
} from '@speckle/shared'
|
||||
import { cloneDeep, sum } from 'lodash'
|
||||
import { CountSeatsByTypeInWorkspace } from '@/modules/gatekeeper/domain/operations'
|
||||
import { WorkspacePlan } from '@/modules/gatekeeperCore/domain/billing'
|
||||
|
||||
export const handleSubscriptionUpdateFactory =
|
||||
({
|
||||
@@ -292,3 +293,28 @@ export const addWorkspaceSubscriptionSeatIfNeededFactoryOld =
|
||||
prorationBehavior: 'create_prorations'
|
||||
})
|
||||
}
|
||||
|
||||
export const getTotalSeatsCountByPlanFactory =
|
||||
({
|
||||
getWorkspacePlanProductId
|
||||
}: {
|
||||
getWorkspacePlanProductId: GetWorkspacePlanProductId
|
||||
}) =>
|
||||
({
|
||||
workspacePlan,
|
||||
subscriptionData
|
||||
}: {
|
||||
workspacePlan: Pick<WorkspacePlan, 'name'>
|
||||
subscriptionData: Pick<SubscriptionData, 'products'>
|
||||
}) => {
|
||||
if (workspacePlan.name === 'free') {
|
||||
return 3 // Max editors seats in the free plan
|
||||
}
|
||||
const productId = getWorkspacePlanProductId({
|
||||
workspacePlan: workspacePlan.name as 'pro' | 'team'
|
||||
})
|
||||
const product = subscriptionData.products.find(
|
||||
(product) => product.productId === productId
|
||||
)
|
||||
return product?.quantity ?? 0
|
||||
}
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
import { db } from '@/db/knex'
|
||||
import { AllScopes } from '@/modules/core/helpers/mainConstants'
|
||||
import {
|
||||
createRandomEmail,
|
||||
createRandomString
|
||||
} from '@/modules/core/helpers/testHelpers'
|
||||
import { WorkspaceSeatType } from '@/modules/gatekeeper/domain/billing'
|
||||
import { upsertWorkspaceSubscriptionFactory } from '@/modules/gatekeeper/repositories/billing'
|
||||
import { getFeatureFlags } from '@/modules/shared/helpers/envHelper'
|
||||
import { createTestWorkspace } from '@/modules/workspaces/tests/helpers/creation'
|
||||
import {
|
||||
assignToWorkspace,
|
||||
createTestWorkspace
|
||||
} from '@/modules/workspaces/tests/helpers/creation'
|
||||
import {
|
||||
BasicTestUser,
|
||||
createAuthTokenForUser,
|
||||
createTestUsers
|
||||
createTestUser,
|
||||
createTestUsers,
|
||||
login
|
||||
} from '@/test/authHelper'
|
||||
import { GetWorkspaceDocument } from '@/test/graphql/generated/graphql'
|
||||
import {
|
||||
GetWorkspaceDocument,
|
||||
GetWorkspaceWithSubscriptionDocument
|
||||
} from '@/test/graphql/generated/graphql'
|
||||
import {
|
||||
createTestContext,
|
||||
testApolloServer,
|
||||
@@ -16,6 +31,7 @@ import { beforeEachContext } from '@/test/hooks'
|
||||
import { Roles } from '@speckle/shared'
|
||||
import { expect } from 'chai'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const { FF_BILLING_INTEGRATION_ENABLED } = getFeatureFlags()
|
||||
|
||||
@@ -103,4 +119,136 @@ describe('Workspaces Billing', () => {
|
||||
})
|
||||
}
|
||||
)
|
||||
;(FF_BILLING_INTEGRATION_ENABLED ? describe : describe.skip)(
|
||||
'workspace.subscription',
|
||||
() => {
|
||||
describe('subscription.seats', () => {
|
||||
it('should return the number of assigned seats', async () => {
|
||||
const user = await createTestUser({
|
||||
name: createRandomString(),
|
||||
email: createRandomEmail(),
|
||||
role: Roles.Server.Admin,
|
||||
verified: true
|
||||
})
|
||||
const workspace = {
|
||||
id: createRandomString(),
|
||||
name: createRandomString(),
|
||||
slug: cryptoRandomString({ length: 10 }),
|
||||
ownerId: user.id
|
||||
}
|
||||
await createTestWorkspace(workspace, user, {
|
||||
addPlan: { name: 'pro', status: 'valid' }
|
||||
})
|
||||
await upsertWorkspaceSubscriptionFactory({ db })({
|
||||
workspaceSubscription: {
|
||||
workspaceId: workspace.id,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
currentBillingCycleEnd: dayjs().add(1, 'month').toDate(),
|
||||
billingInterval: 'monthly',
|
||||
subscriptionData: {
|
||||
subscriptionId: cryptoRandomString({ length: 10 }),
|
||||
customerId: cryptoRandomString({ length: 10 }),
|
||||
cancelAt: null,
|
||||
status: 'active',
|
||||
currentPeriodEnd: new Date(),
|
||||
products: [
|
||||
{
|
||||
priceId: createRandomString(),
|
||||
quantity: 12,
|
||||
productId: createRandomString(),
|
||||
subscriptionItemId: createRandomString()
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
const session = await login(user)
|
||||
|
||||
const res = await session.execute(GetWorkspaceWithSubscriptionDocument, {
|
||||
workspaceId: workspace.id
|
||||
})
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
const seats = res.data?.workspace.subscription?.seats
|
||||
expect(seats?.assigned).to.eq(1)
|
||||
})
|
||||
it('should return the number of viewers', async () => {
|
||||
const user = await createTestUser({
|
||||
name: createRandomString(),
|
||||
email: createRandomEmail(),
|
||||
role: Roles.Server.Admin,
|
||||
verified: true
|
||||
})
|
||||
const workspace = {
|
||||
id: createRandomString(),
|
||||
name: createRandomString(),
|
||||
slug: cryptoRandomString({ length: 10 }),
|
||||
ownerId: user.id
|
||||
}
|
||||
await createTestWorkspace(workspace, user, {
|
||||
addPlan: { name: 'pro', status: 'valid' }
|
||||
})
|
||||
await upsertWorkspaceSubscriptionFactory({ db })({
|
||||
workspaceSubscription: {
|
||||
workspaceId: workspace.id,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
currentBillingCycleEnd: dayjs().add(1, 'month').toDate(),
|
||||
billingInterval: 'monthly',
|
||||
subscriptionData: {
|
||||
subscriptionId: cryptoRandomString({ length: 10 }),
|
||||
customerId: cryptoRandomString({ length: 10 }),
|
||||
cancelAt: null,
|
||||
status: 'active',
|
||||
currentPeriodEnd: new Date(),
|
||||
products: [
|
||||
{
|
||||
priceId: createRandomString(),
|
||||
quantity: 12,
|
||||
productId: createRandomString(),
|
||||
subscriptionItemId: createRandomString()
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
const viewer1 = await createTestUser({
|
||||
name: createRandomString(),
|
||||
email: createRandomEmail(),
|
||||
role: Roles.Server.User,
|
||||
verified: true
|
||||
})
|
||||
await assignToWorkspace(
|
||||
workspace,
|
||||
viewer1,
|
||||
Roles.Workspace.Member,
|
||||
WorkspaceSeatType.Viewer
|
||||
)
|
||||
const viewer2 = await createTestUser({
|
||||
name: createRandomString(),
|
||||
email: createRandomEmail(),
|
||||
role: Roles.Server.User,
|
||||
verified: true
|
||||
})
|
||||
await assignToWorkspace(
|
||||
workspace,
|
||||
viewer2,
|
||||
Roles.Workspace.Member,
|
||||
WorkspaceSeatType.Viewer
|
||||
)
|
||||
|
||||
const session = await login(user)
|
||||
|
||||
const res = await session.execute(GetWorkspaceWithSubscriptionDocument, {
|
||||
workspaceId: workspace.id
|
||||
})
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
const seats = res.data?.workspace.subscription?.seats
|
||||
expect(seats?.viewersCount).to.eq(2)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import {
|
||||
addWorkspaceSubscriptionSeatIfNeededFactoryNew,
|
||||
addWorkspaceSubscriptionSeatIfNeededFactoryOld,
|
||||
getTotalSeatsCountByPlanFactory,
|
||||
handleSubscriptionUpdateFactory
|
||||
} from '@/modules/gatekeeper/services/subscriptions'
|
||||
import {
|
||||
@@ -2350,4 +2351,40 @@ describe('subscriptions @gatekeeper', () => {
|
||||
expect(newProduct!.priceId).to.equal('newPlanPrice')
|
||||
})
|
||||
})
|
||||
describe('getTotalSeatsCountByPlanFactory returns a function that, ', () => {
|
||||
it('should return the fixed value for the free plan', () => {
|
||||
const getWorkspacePlanProductId = () => expect.fail()
|
||||
expect(
|
||||
getTotalSeatsCountByPlanFactory({ getWorkspacePlanProductId })({
|
||||
workspacePlan: { name: 'free' },
|
||||
subscriptionData: { products: [] }
|
||||
})
|
||||
).to.eq(3)
|
||||
})
|
||||
it('should return 0 if subscription data has no product', () => {
|
||||
const getWorkspacePlanProductId = () => 'any'
|
||||
expect(
|
||||
getTotalSeatsCountByPlanFactory({ getWorkspacePlanProductId })({
|
||||
workspacePlan: { name: 'pro' },
|
||||
subscriptionData: { products: [] }
|
||||
})
|
||||
).to.eq(0)
|
||||
})
|
||||
it('should return the number of purchased seats in the current billing period for the subscription', () => {
|
||||
const getWorkspacePlanProductId = () => 'productId'
|
||||
expect(
|
||||
getTotalSeatsCountByPlanFactory({ getWorkspacePlanProductId })({
|
||||
workspacePlan: { name: 'pro' },
|
||||
subscriptionData: {
|
||||
products: [
|
||||
{
|
||||
productId: 'productId',
|
||||
quantity: 4
|
||||
} as SubscriptionData['products'][number]
|
||||
]
|
||||
}
|
||||
})
|
||||
).to.eq(4)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -363,6 +363,28 @@ export const getWorkspaceWithJoinRequestsQuery = gql`
|
||||
${basicWorkspaceFragment}
|
||||
`
|
||||
|
||||
export const getWorkspaceWithSubscriptionQuery = gql`
|
||||
query GetWorkspaceWithSubscription($workspaceId: String!) {
|
||||
workspace(id: $workspaceId) {
|
||||
...BasicWorkspace
|
||||
subscription {
|
||||
createdAt
|
||||
updatedAt
|
||||
currentBillingCycleEnd
|
||||
billingInterval
|
||||
seats {
|
||||
guest
|
||||
plan
|
||||
assigned
|
||||
totalCount
|
||||
viewersCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${basicWorkspaceFragment}
|
||||
`
|
||||
|
||||
export const updateWorkspaceProjectRoleMutation = gql`
|
||||
mutation UpdateWorkspaceProjectRole($input: ProjectUpdateRoleInput!) {
|
||||
workspaceMutations {
|
||||
|
||||
@@ -4859,8 +4859,16 @@ export type WorkspaceSubscription = {
|
||||
|
||||
export type WorkspaceSubscriptionSeats = {
|
||||
__typename?: 'WorkspaceSubscriptionSeats';
|
||||
/** Number assigned seats in the current billing cycle */
|
||||
assigned: Scalars['Int']['output'];
|
||||
/** @deprecated Field no longer supported */
|
||||
guest: Scalars['Int']['output'];
|
||||
/** @deprecated Field no longer supported */
|
||||
plan: Scalars['Int']['output'];
|
||||
/** Total number of seats purchased and available in the current subscription cycle */
|
||||
totalCount: Scalars['Int']['output'];
|
||||
/** Number of viewer seats currently assigned in the workspace */
|
||||
viewersCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type WorkspaceTeamFilter = {
|
||||
@@ -5180,6 +5188,13 @@ export type GetWorkspaceWithJoinRequestsQueryVariables = Exact<{
|
||||
|
||||
export type GetWorkspaceWithJoinRequestsQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null, readOnly: boolean, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, createdAt: string, user: { __typename?: 'LimitedUser', id: string, name: string }, workspace: { __typename?: 'Workspace', id: string, name: string } }> } | null } };
|
||||
|
||||
export type GetWorkspaceWithSubscriptionQueryVariables = Exact<{
|
||||
workspaceId: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetWorkspaceWithSubscriptionQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null, readOnly: boolean, subscription?: { __typename?: 'WorkspaceSubscription', createdAt: string, updatedAt: string, currentBillingCycleEnd: string, billingInterval: BillingInterval, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number, assigned: number, totalCount: number, viewersCount: number } } | null } };
|
||||
|
||||
export type UpdateWorkspaceProjectRoleMutationVariables = Exact<{
|
||||
input: ProjectUpdateRoleInput;
|
||||
}>;
|
||||
@@ -6020,6 +6035,7 @@ export const OnWorkspaceUpdatedDocument = {"kind":"Document","definitions":[{"ki
|
||||
export const DismissWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"dismissWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceDismissInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dismiss"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode<DismissWorkspaceMutation, DismissWorkspaceMutationVariables>;
|
||||
export const RequestToJoinWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"requestToJoinWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceRequestToJoinInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"requestToJoin"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode<RequestToJoinWorkspaceMutation, RequestToJoinWorkspaceMutationVariables>;
|
||||
export const GetWorkspaceWithJoinRequestsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithJoinRequests"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"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":"AdminWorkspaceJoinRequestFilter"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"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":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]} as unknown as DocumentNode<GetWorkspaceWithJoinRequestsQuery, GetWorkspaceWithJoinRequestsQueryVariables>;
|
||||
export const GetWorkspaceWithSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}},{"kind":"Field","name":{"kind":"Name","value":"assigned"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"viewersCount"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]} as unknown as DocumentNode<GetWorkspaceWithSubscriptionQuery, GetWorkspaceWithSubscriptionQueryVariables>;
|
||||
export const UpdateWorkspaceProjectRoleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWorkspaceProjectRole"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectUpdateRoleInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateRole"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicProjectFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectFields"},"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<UpdateWorkspaceProjectRoleMutation, UpdateWorkspaceProjectRoleMutationVariables>;
|
||||
export const UpdateWorkspaceSeatTypeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWorkspaceSeatType"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceUpdateSeatTypeInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateSeatType"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"seatType"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<UpdateWorkspaceSeatTypeMutation, UpdateWorkspaceSeatTypeMutationVariables>;
|
||||
export const CreateStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequestCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode<CreateStreamAccessRequestMutation, CreateStreamAccessRequestMutationVariables>;
|
||||
|
||||
@@ -492,7 +492,9 @@ const getStream = () => {
|
||||
// REGIONS
|
||||
// https://app.speckle.systems/projects/16ce7b208c/models/1c14e37363@0614bb2957
|
||||
|
||||
// SUPER slow tree build time
|
||||
// 'https://app.speckle.systems/projects/7591c56179/models/82b94108a3'
|
||||
|
||||
// SUPER slow tree build time (LARGE N-GONS TRIANGULATION)
|
||||
// 'https://app.speckle.systems/projects/0edb6ef628/models/ff3d8480bc@cd83d90a2c'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import { computeOrthographicSize } from '../CameraController.js'
|
||||
import { ObjectLayers } from '../../../IViewer.js'
|
||||
import SpeckleBasicMaterial from '../../materials/SpeckleBasicMaterial.js'
|
||||
import SpeckleRenderer from '../../SpeckleRenderer.js'
|
||||
import { ExtendedMeshIntersection } from '../../objects/SpeckleRaycaster.js'
|
||||
|
||||
/**
|
||||
* @param {Number} value
|
||||
@@ -1076,6 +1077,12 @@ export class SmoothOrbitControls extends SpeckleControls {
|
||||
this.orbitSphere.visible = false
|
||||
}
|
||||
|
||||
/** By default hidden objects are ignored when picking for orbit around cursor */
|
||||
protected filterOrbitToCursorHits(hit: ExtendedMeshIntersection) {
|
||||
const material = this.renderer.getMaterial(hit.batchObject.renderView)
|
||||
return material?.visible ?? false
|
||||
}
|
||||
|
||||
protected onPointerDown = (event: PointerEvent) => {
|
||||
if (this._options.orbitAroundCursor) {
|
||||
/** Hope this is not slow */
|
||||
@@ -1083,7 +1090,7 @@ export class SmoothOrbitControls extends SpeckleControls {
|
||||
const x = ((event.clientX - rect.left) / rect.width) * 2 - 1
|
||||
const y = ((event.clientY - rect.top) / rect.height) * -2 + 1
|
||||
|
||||
const res = this.renderer.intersections.intersect(
|
||||
let res = this.renderer.intersections.intersect(
|
||||
this.renderer.scene,
|
||||
this._targetCamera as PerspectiveCamera,
|
||||
new Vector2(x, y),
|
||||
@@ -1091,7 +1098,9 @@ export class SmoothOrbitControls extends SpeckleControls {
|
||||
true,
|
||||
this.renderer.clippingVolume
|
||||
)
|
||||
if (res && res.length) {
|
||||
res = res?.filter(this.filterOrbitToCursorHits.bind(this)) ?? []
|
||||
|
||||
if (res.length) {
|
||||
this.pivotPoint.copy(res[0].point)
|
||||
this.usePivotal = true
|
||||
this.orbitSphere.visible = this._options.showOrbitPoint
|
||||
|
||||
Reference in New Issue
Block a user