Merge branch 'main' into viewer-redux-integration
This commit is contained in:
+7
-18
@@ -10,27 +10,16 @@ fi
|
||||
# enables building the test-deployment container with the same script
|
||||
# defaults to packages for minimal intervention in the ci config
|
||||
FOLDER="${FOLDER:-packages}"
|
||||
SHOULD_PUBLISH="${SHOULD_PUBLISH:-false}"
|
||||
|
||||
DOCKER_IMAGE_TAG="speckle/speckle-${SPECKLE_SERVER_PACKAGE}"
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
# shellcheck disable=SC1090,SC1091
|
||||
source "${SCRIPT_DIR}/common.sh"
|
||||
|
||||
# IMAGE_VERSION_TAG=$(./.circleci/get_version.sh)
|
||||
# if there is not image version tag, uses the SHA1 of the last git commit of the branch that triggered this build
|
||||
IMAGE_VERSION_TAG="${IMAGE_VERSION_TAG:-${CIRCLE_SHA1}}"
|
||||
echo "${IMAGE_VERSION_TAG}"
|
||||
echo "Building image: ${DOCKER_IMAGE_TAG}:${IMAGE_VERSION_TAG}"
|
||||
|
||||
export DOCKER_BUILDKIT=1
|
||||
|
||||
docker build --build-arg SPECKLE_SERVER_VERSION="${IMAGE_VERSION_TAG}" -t "${DOCKER_IMAGE_TAG}:${IMAGE_VERSION_TAG}" . --file "${FOLDER}/${SPECKLE_SERVER_PACKAGE}/Dockerfile"
|
||||
docker build --build-arg SPECKLE_SERVER_VERSION="${IMAGE_VERSION_TAG}" --tag "${DOCKER_IMAGE_TAG}:${IMAGE_VERSION_TAG}" --file "${FOLDER}/${SPECKLE_SERVER_PACKAGE}/Dockerfile" .
|
||||
|
||||
if [[ "${SHOULD_PUBLISH}" == "true" ]]; then
|
||||
echo "publishing images"
|
||||
docker tag "${DOCKER_IMAGE_TAG}:${IMAGE_VERSION_TAG}" "${DOCKER_IMAGE_TAG}:latest"
|
||||
|
||||
if [[ "${IMAGE_VERSION_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
docker tag "${DOCKER_IMAGE_TAG}:${IMAGE_VERSION_TAG}" "${DOCKER_IMAGE_TAG}:2"
|
||||
fi
|
||||
|
||||
echo "${DOCKER_REG_PASS}" | docker login -u "${DOCKER_REG_USER}" --password-stdin "${DOCKER_REG_URL}"
|
||||
docker push --all-tags "${DOCKER_IMAGE_TAG}"
|
||||
fi
|
||||
echo " Saving image: ${DOCKER_FILE_NAME}"
|
||||
docker save --output "/tmp/ci/workspace/${DOCKER_FILE_NAME}" "${DOCKER_IMAGE_TAG}:${IMAGE_VERSION_TAG}"
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
DOCKER_IMAGE_TAG="speckle/speckle-${SPECKLE_SERVER_PACKAGE}"
|
||||
IMAGE_VERSION_TAG="${IMAGE_VERSION_TAG:-${CIRCLE_SHA1}}"
|
||||
# shellcheck disable=SC2034,SC2086
|
||||
DOCKER_FILE_NAME="$(echo ${DOCKER_IMAGE_TAG}_${IMAGE_VERSION_TAG} | sed -e 's/[^A-Za-z0-9._-]/_/g')"
|
||||
+174
-61
@@ -23,9 +23,7 @@ workflows:
|
||||
- pre-commit:
|
||||
filters: *filters-everything
|
||||
|
||||
- docker-build-and-publish-server:
|
||||
context: &docker-hub-context
|
||||
- docker-hub
|
||||
- docker-build-server:
|
||||
filters: &filters-build
|
||||
tags:
|
||||
only: /.*/
|
||||
@@ -33,59 +31,114 @@ workflows:
|
||||
- test-server
|
||||
- get-version
|
||||
- should-build
|
||||
- should-publish
|
||||
|
||||
- docker-build-and-publish-frontend:
|
||||
context: *docker-hub-context
|
||||
- docker-build-frontend:
|
||||
filters: *filters-build
|
||||
requires:
|
||||
- get-version
|
||||
- should-build
|
||||
- should-publish
|
||||
|
||||
- docker-build-and-publish-webhooks:
|
||||
context: *docker-hub-context
|
||||
- docker-build-webhooks:
|
||||
filters: *filters-build
|
||||
requires:
|
||||
- get-version
|
||||
- test-server
|
||||
- should-build
|
||||
- should-publish
|
||||
|
||||
- docker-build-and-publish-file-imports:
|
||||
context: *docker-hub-context
|
||||
- docker-build-file-imports:
|
||||
filters: *filters-build
|
||||
requires:
|
||||
- get-version
|
||||
- test-server
|
||||
- should-build
|
||||
- should-publish
|
||||
|
||||
- docker-build-and-publish-previews:
|
||||
context: *docker-hub-context
|
||||
- docker-build-previews:
|
||||
filters: *filters-build
|
||||
requires:
|
||||
- get-version
|
||||
- test-server
|
||||
- should-build
|
||||
- should-publish
|
||||
|
||||
- docker-build-and-publish-test-container:
|
||||
context: *docker-hub-context
|
||||
- docker-build-test-container:
|
||||
filters: *filters-build
|
||||
requires:
|
||||
- get-version
|
||||
- test-server
|
||||
- should-build
|
||||
- should-publish
|
||||
|
||||
- docker-build-and-publish-monitor-container:
|
||||
context: *docker-hub-context
|
||||
- docker-build-monitor-container:
|
||||
filters: *filters-build
|
||||
requires:
|
||||
- get-version
|
||||
- should-build
|
||||
|
||||
- docker-publish-server:
|
||||
context: &docker-hub-context
|
||||
- docker-hub
|
||||
filters: &filters-publish
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
tags:
|
||||
only: /.*/
|
||||
requires:
|
||||
- get-version
|
||||
- should-publish
|
||||
- docker-build-server
|
||||
- pre-commit
|
||||
|
||||
- docker-publish-frontend:
|
||||
context: *docker-hub-context
|
||||
filters: *filters-publish
|
||||
requires:
|
||||
- get-version
|
||||
- should-publish
|
||||
- docker-build-frontend
|
||||
- pre-commit
|
||||
|
||||
- docker-publish-webhooks:
|
||||
context: *docker-hub-context
|
||||
filters: *filters-publish
|
||||
requires:
|
||||
- get-version
|
||||
- should-publish
|
||||
- docker-build-webhooks
|
||||
- pre-commit
|
||||
|
||||
- docker-publish-file-imports:
|
||||
context: *docker-hub-context
|
||||
filters: *filters-publish
|
||||
requires:
|
||||
- get-version
|
||||
- should-publish
|
||||
- docker-build-file-imports
|
||||
- pre-commit
|
||||
|
||||
- docker-publish-previews:
|
||||
context: *docker-hub-context
|
||||
filters: *filters-publish
|
||||
requires:
|
||||
- get-version
|
||||
- should-publish
|
||||
- docker-build-previews
|
||||
- pre-commit
|
||||
|
||||
- docker-publish-test-container:
|
||||
context: *docker-hub-context
|
||||
filters: *filters-publish
|
||||
requires:
|
||||
- get-version
|
||||
- should-publish
|
||||
- docker-build-test-container
|
||||
- pre-commit
|
||||
|
||||
- docker-publish-monitor-container:
|
||||
context: *docker-hub-context
|
||||
filters: *filters-publish
|
||||
requires:
|
||||
- get-version
|
||||
- should-publish
|
||||
- docker-build-monitor-container
|
||||
- pre-commit
|
||||
|
||||
- publish-helm-chart:
|
||||
filters: &filters-publish
|
||||
@@ -96,16 +149,15 @@ workflows:
|
||||
tags:
|
||||
only: &filters-tag /^[0-9]+\.[0-9]+\.[0-9]+$/
|
||||
requires:
|
||||
- test-server
|
||||
- get-version
|
||||
- should-publish
|
||||
- docker-build-and-publish-server
|
||||
- docker-build-and-publish-frontend
|
||||
- docker-build-and-publish-webhooks
|
||||
- docker-build-and-publish-file-imports
|
||||
- docker-build-and-publish-previews
|
||||
- docker-build-and-publish-monitor-container
|
||||
- docker-build-and-publish-test-container
|
||||
- docker-publish-server
|
||||
- docker-publish-frontend
|
||||
- docker-publish-webhooks
|
||||
- docker-publish-file-imports
|
||||
- docker-publish-previews
|
||||
- docker-publish-monitor-container
|
||||
- docker-publish-test-container
|
||||
|
||||
- publish-npm:
|
||||
filters:
|
||||
@@ -125,7 +177,6 @@ jobs:
|
||||
working_directory: &work-dir /tmp/ci
|
||||
steps:
|
||||
- checkout
|
||||
- run: pwd
|
||||
- run: mkdir -p workspace
|
||||
- run:
|
||||
name: set version
|
||||
@@ -142,13 +193,12 @@ jobs:
|
||||
docker:
|
||||
- image: cimg/base:2022.08
|
||||
working_directory: *work-dir
|
||||
environment:
|
||||
# £ delimited strings of regex for matches which should be published
|
||||
environment: &publishable-tags-branches
|
||||
PUBLISHABLE_TAGS: '^[0-9]+\.[0-9]+\.[0-9]+$'
|
||||
# £ delimited strings of regex for matches which should be published
|
||||
PUBLISHABLE_BRANCHES: '^main$£^hotfix.*£^alpha.*'
|
||||
steps:
|
||||
- checkout
|
||||
- run: pwd
|
||||
- run: mkdir -p workspace
|
||||
- run:
|
||||
name: determine whether to publish
|
||||
@@ -165,9 +215,9 @@ jobs:
|
||||
docker:
|
||||
- image: cimg/base:2022.08
|
||||
working_directory: *work-dir
|
||||
environment: *publishable-tags-branches
|
||||
steps:
|
||||
- checkout
|
||||
- run: pwd
|
||||
- run: mkdir -p workspace
|
||||
- run:
|
||||
name: determine whether to build
|
||||
@@ -197,7 +247,7 @@ jobs:
|
||||
type: string
|
||||
docker:
|
||||
- image: speckle/pre-commit-runner:latest
|
||||
resource_class: large
|
||||
resource_class: medium
|
||||
working_directory: *work-dir
|
||||
steps:
|
||||
- checkout
|
||||
@@ -259,6 +309,7 @@ jobs:
|
||||
S3_SECRET_KEY: 'minioadmin'
|
||||
S3_BUCKET: 'speckle-server'
|
||||
S3_CREATE_BUCKET: 'true'
|
||||
REDIS_URL: 'redis://localhost:6379'
|
||||
S3_REGION: '' # optional, defaults to 'us-east-1'
|
||||
steps:
|
||||
- checkout
|
||||
@@ -304,10 +355,10 @@ jobs:
|
||||
path: packages/server/coverage/lcov-report
|
||||
destination: package/server/coverage
|
||||
|
||||
docker-build-and-publish: &docker-job
|
||||
docker-build: &build-job
|
||||
docker: &docker-image
|
||||
- image: cimg/node:16.15
|
||||
resource_class: xlarge
|
||||
resource_class: medium
|
||||
working_directory: *work-dir
|
||||
steps:
|
||||
- checkout
|
||||
@@ -315,7 +366,11 @@ jobs:
|
||||
at: /tmp/ci/workspace
|
||||
- run: cat workspace/env-vars >> $BASH_ENV
|
||||
- run: cat workspace/should-build >> $BASH_ENV
|
||||
- run: cat workspace/should-publish >> $BASH_ENV
|
||||
- run:
|
||||
name: 'Check if should proceed'
|
||||
command: |
|
||||
[[ "${SHOULD_BUILD}" != true ]] && echo "Should not build, stopping" && circleci-agent step halt
|
||||
echo 'Proceeding with build'
|
||||
- setup_remote_docker:
|
||||
# a weird issue with yarn installing packages throwing EPERM errors
|
||||
# this fixes it
|
||||
@@ -324,40 +379,106 @@ jobs:
|
||||
- run:
|
||||
name: Build and Publish
|
||||
command: ./.circleci/build.sh
|
||||
- persist_to_workspace:
|
||||
root: workspace
|
||||
paths:
|
||||
- speckle*
|
||||
|
||||
docker-build-and-publish-server:
|
||||
<<: *docker-job
|
||||
docker-build-server:
|
||||
<<: *build-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: server
|
||||
|
||||
docker-build-and-publish-frontend:
|
||||
<<: *docker-job
|
||||
docker-build-frontend:
|
||||
<<: *build-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: frontend
|
||||
|
||||
docker-build-and-publish-previews:
|
||||
<<: *docker-job
|
||||
docker-build-previews:
|
||||
<<: *build-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: preview-service
|
||||
|
||||
docker-build-and-publish-webhooks:
|
||||
<<: *docker-job
|
||||
docker-build-webhooks:
|
||||
<<: *build-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: webhook-service
|
||||
|
||||
docker-build-and-publish-file-imports:
|
||||
<<: *docker-job
|
||||
docker-build-file-imports:
|
||||
<<: *build-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: fileimport-service
|
||||
|
||||
docker-build-and-publish-test-container:
|
||||
<<: *docker-job
|
||||
docker-build-test-container:
|
||||
<<: *build-job
|
||||
environment:
|
||||
FOLDER: utils
|
||||
SPECKLE_SERVER_PACKAGE: test-deployment
|
||||
|
||||
docker-build-and-publish-monitor-container:
|
||||
<<: *docker-job
|
||||
docker-build-monitor-container:
|
||||
<<: *build-job
|
||||
environment:
|
||||
FOLDER: utils
|
||||
SPECKLE_SERVER_PACKAGE: monitor-deployment
|
||||
|
||||
docker-publish: &publish-job
|
||||
docker: &base-image
|
||||
- image: cimg/base:2022.08
|
||||
resource_class: medium
|
||||
working_directory: *work-dir
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/ci/workspace
|
||||
- run: cat workspace/env-vars >> $BASH_ENV
|
||||
- run: cat workspace/should-publish >> $BASH_ENV
|
||||
- run:
|
||||
name: 'Check if should proceed'
|
||||
command: |
|
||||
[[ "${SHOULD_PUBLISH}" != true ]] && echo "Should not publish, stopping" && circleci-agent step halt
|
||||
echo 'Proceeding with publish'
|
||||
- setup_remote_docker:
|
||||
# a weird issue with yarn installing packages throwing EPERM errors
|
||||
# this fixes it
|
||||
version: 20.10.12
|
||||
docker_layer_caching: true
|
||||
- run:
|
||||
name: Publish
|
||||
command: ./.circleci/publish.sh
|
||||
|
||||
docker-publish-server:
|
||||
<<: *publish-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: server
|
||||
|
||||
docker-publish-frontend:
|
||||
<<: *publish-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: frontend
|
||||
|
||||
docker-publish-previews:
|
||||
<<: *publish-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: preview-service
|
||||
|
||||
docker-publish-webhooks:
|
||||
<<: *publish-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: webhook-service
|
||||
|
||||
docker-publish-file-imports:
|
||||
<<: *publish-job
|
||||
environment:
|
||||
SPECKLE_SERVER_PACKAGE: fileimport-service
|
||||
|
||||
docker-publish-test-container:
|
||||
<<: *publish-job
|
||||
environment:
|
||||
FOLDER: utils
|
||||
SPECKLE_SERVER_PACKAGE: test-deployment
|
||||
|
||||
docker-publish-monitor-container:
|
||||
<<: *publish-job
|
||||
environment:
|
||||
FOLDER: utils
|
||||
SPECKLE_SERVER_PACKAGE: monitor-deployment
|
||||
@@ -389,7 +510,7 @@ jobs:
|
||||
- run:
|
||||
name: auth to npm as Speckle
|
||||
command: |
|
||||
echo "npmRegistryServer: https://registry.npmjs.org/" >> .yarnrc.yml
|
||||
echo "npmRegistryServer: https://registry.npmjs.org/" >> .yarnrc.yml
|
||||
echo "npmAuthToken: ${NPM_TOKEN}" >> .yarnrc.yml
|
||||
- run:
|
||||
name: try login to npm
|
||||
@@ -407,14 +528,6 @@ jobs:
|
||||
name: publish to npm
|
||||
command: 'yarn workspaces foreach -pv --no-private npm publish --access public'
|
||||
|
||||
# - run:
|
||||
# name: commit changes
|
||||
# command: |
|
||||
# yarn prettier:fix
|
||||
# git add .
|
||||
# git commit -m '[ci skip] bump version to $IMAGE_VERSION_TAG'
|
||||
# git push
|
||||
|
||||
publish-helm-chart:
|
||||
docker: *docker-image
|
||||
working_directory: *work-dir
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
SHOULD_PUBLISH="${SHOULD_PUBLISH:-false}"
|
||||
|
||||
if [[ "${SHOULD_PUBLISH}" != "true" ]]; then
|
||||
echo "Not publishing as the SHOULD_PUBLISH environment variable is not 'true'."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
# shellcheck disable=SC1090,SC1091
|
||||
source "${SCRIPT_DIR}/common.sh"
|
||||
|
||||
echo "Publishing: ${DOCKER_IMAGE_TAG}:${IMAGE_VERSION_TAG}"
|
||||
|
||||
echo "💾 Loading image"
|
||||
docker load --input "/tmp/ci/workspace/${DOCKER_FILE_NAME}"
|
||||
|
||||
echo "🐳 Publishing image"
|
||||
docker tag "${DOCKER_IMAGE_TAG}:${IMAGE_VERSION_TAG}" "${DOCKER_IMAGE_TAG}:latest"
|
||||
|
||||
if [[ "${IMAGE_VERSION_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
docker tag "${DOCKER_IMAGE_TAG}:${IMAGE_VERSION_TAG}" "${DOCKER_IMAGE_TAG}:2"
|
||||
fi
|
||||
|
||||
echo "${DOCKER_REG_PASS}" | docker login -u "${DOCKER_REG_USER}" --password-stdin "${DOCKER_REG_URL}"
|
||||
docker push --all-tags "${DOCKER_IMAGE_TAG}"
|
||||
@@ -1,6 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
IFS='£' read -r -a PUB_TAGS <<< "${PUBLISHABLE_TAGS}"
|
||||
# shellcheck disable=SC2068
|
||||
for item in ${PUB_TAGS[@]}; do
|
||||
[[ "${CIRCLE_TAG}" =~ ${item} ]] && echo "true" && exit 0
|
||||
done
|
||||
|
||||
# it's on the main branch
|
||||
[[ "${CIRCLE_BRANCH}" == "main" ]] && echo "true" && exit 0
|
||||
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
matches-ignore:
|
||||
name: MIXPANEL_TOKEN
|
||||
match: acd87c5a50b56df91a795e999812a3a4
|
||||
- name: MIXPANEL_TOKEN
|
||||
match: acd87c5a50b56df91a795e999812a3a4
|
||||
|
||||
@@ -16,5 +16,10 @@ repos:
|
||||
hooks:
|
||||
- id: helmlint
|
||||
|
||||
- repo: https://github.com/syntaqx/git-hooks
|
||||
rev: 'v0.0.17'
|
||||
hooks:
|
||||
- id: circleci-config-validate
|
||||
|
||||
ci:
|
||||
autoupdate_schedule: quarterly
|
||||
|
||||
+2
-1
@@ -21,6 +21,7 @@
|
||||
"dev": "yarn workspaces foreach -piv -j unlimited run dev",
|
||||
"dev:no-server": "yarn workspaces foreach --exclude @speckle/server -piv -j unlimited run dev",
|
||||
"dev:minimal": "yarn workspaces foreach -piv -j unlimited --include '{@speckle/server,@speckle/frontend}' run dev",
|
||||
"gqlgen": "yarn workspaces foreach -piv -j unlimited --include '{@speckle/server,@speckle/frontend}' run gqlgen",
|
||||
"dev:server": "yarn workspace @speckle/server dev",
|
||||
"dev:frontend": "yarn workspace @speckle/frontend dev",
|
||||
"prepare": "husky install",
|
||||
@@ -40,7 +41,7 @@
|
||||
"resolutions": {
|
||||
"tslib": "^2.3.1",
|
||||
"core-js": "3.22.4",
|
||||
"vue-cli-plugin-apollo/graphql": "^15",
|
||||
"graphql": "^15",
|
||||
"typescript": "^4.5.4",
|
||||
"vue-loader": "^15.10.0"
|
||||
},
|
||||
|
||||
@@ -26,7 +26,8 @@ const config = {
|
||||
extends: ['plugin:vue/recommended', '@vue/eslint-config-typescript', 'prettier'],
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['error']
|
||||
'@typescript-eslint/no-unused-vars': ['error'],
|
||||
'vue/component-name-in-template-casing': ['warn', 'kebab-case']
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -15,3 +15,5 @@ generates:
|
||||
config:
|
||||
scalars:
|
||||
JSONObject: Record<string, unknown>
|
||||
DateTime: string
|
||||
dedupeFragments: true
|
||||
|
||||
@@ -110,6 +110,9 @@ function createCache(): InMemoryCache {
|
||||
},
|
||||
pendingCollaborators: {
|
||||
merge: incomingOverwritesExistingMergeFunction
|
||||
},
|
||||
pendingAccessRequests: {
|
||||
merge: incomingOverwritesExistingMergeFunction
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { basicStreamAccessRequestFieldsFragment } from '@/graphql/fragments/accessRequests'
|
||||
import { gql } from '@apollo/client/core'
|
||||
|
||||
export const getStreamAccessRequestQuery = gql`
|
||||
query GetStreamAccessRequest($streamId: String!) {
|
||||
streamAccessRequest(streamId: $streamId) {
|
||||
...BasicStreamAccessRequestFields
|
||||
}
|
||||
}
|
||||
|
||||
${basicStreamAccessRequestFieldsFragment}
|
||||
`
|
||||
|
||||
export const createStreamAccessRequestMutation = gql`
|
||||
mutation CreateStreamAccessRequest($streamId: String!) {
|
||||
streamAccessRequestCreate(streamId: $streamId) {
|
||||
...BasicStreamAccessRequestFields
|
||||
}
|
||||
}
|
||||
|
||||
${basicStreamAccessRequestFieldsFragment}
|
||||
`
|
||||
|
||||
export const useStreamAccessRequestMutation = gql`
|
||||
mutation UseStreamAccessRequest(
|
||||
$requestId: String!
|
||||
$accept: Boolean!
|
||||
$role: StreamRole = STREAM_CONTRIBUTOR
|
||||
) {
|
||||
streamAccessRequestUse(requestId: $requestId, accept: $accept, role: $role)
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,22 @@
|
||||
import { limitedUserFieldsFragment } from '@/graphql/fragments/user'
|
||||
import { gql } from '@apollo/client/core'
|
||||
|
||||
export const basicStreamAccessRequestFieldsFragment = gql`
|
||||
fragment BasicStreamAccessRequestFields on StreamAccessRequest {
|
||||
id
|
||||
streamId
|
||||
createdAt
|
||||
}
|
||||
`
|
||||
|
||||
export const fullStreamAccessRequestFieldsFragment = gql`
|
||||
fragment FullStreamAccessRequestFields on StreamAccessRequest {
|
||||
...BasicStreamAccessRequestFields
|
||||
requester {
|
||||
...LimitedUserFields
|
||||
}
|
||||
}
|
||||
|
||||
${limitedUserFieldsFragment}
|
||||
${basicStreamAccessRequestFieldsFragment}
|
||||
`
|
||||
@@ -0,0 +1,12 @@
|
||||
import { fullStreamAccessRequestFieldsFragment } from '@/graphql/fragments/accessRequests'
|
||||
import { gql } from '@apollo/client/core'
|
||||
|
||||
export const streamPendingAccessRequestsFragment = gql`
|
||||
fragment StreamPendingAccessRequests on Stream {
|
||||
pendingAccessRequests {
|
||||
...FullStreamAccessRequestFields
|
||||
}
|
||||
}
|
||||
|
||||
${fullStreamAccessRequestFieldsFragment}
|
||||
`
|
||||
@@ -15,7 +15,7 @@ export type Scalars = {
|
||||
/** The `BigInt` scalar type represents non-fractional signed whole numeric values. */
|
||||
BigInt: any;
|
||||
/** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */
|
||||
DateTime: any;
|
||||
DateTime: string;
|
||||
EmailAddress: any;
|
||||
/** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
|
||||
JSONObject: Record<string, unknown>;
|
||||
@@ -393,6 +393,16 @@ export type CommitUpdateInput = {
|
||||
streamId: Scalars['String'];
|
||||
};
|
||||
|
||||
export enum DiscoverableStreamsSortType {
|
||||
CreatedDate = 'CREATED_DATE',
|
||||
FavoritesCount = 'FAVORITES_COUNT'
|
||||
}
|
||||
|
||||
export type DiscoverableStreamsSortingInput = {
|
||||
direction: SortDirection;
|
||||
type: DiscoverableStreamsSortType;
|
||||
};
|
||||
|
||||
export type FileUpload = {
|
||||
__typename?: 'FileUpload';
|
||||
branchName?: Maybe<Scalars['String']>;
|
||||
@@ -467,10 +477,16 @@ export type Mutation = {
|
||||
/** Re-send a pending invite */
|
||||
inviteResend: Scalars['Boolean'];
|
||||
objectCreate: Array<Maybe<Scalars['String']>>;
|
||||
/** (Re-)send the account verification e-mail */
|
||||
requestVerification: Scalars['Boolean'];
|
||||
serverInfoUpdate?: Maybe<Scalars['Boolean']>;
|
||||
serverInviteBatchCreate: Scalars['Boolean'];
|
||||
/** Invite a new user to the speckle server and return the invite ID */
|
||||
serverInviteCreate: Scalars['Boolean'];
|
||||
/** Request access to a specific stream */
|
||||
streamAccessRequestCreate: StreamAccessRequest;
|
||||
/** Accept or decline a stream access request. Must be a stream owner to invoke this. */
|
||||
streamAccessRequestUse: Scalars['Boolean'];
|
||||
/** Creates a new stream. */
|
||||
streamCreate?: Maybe<Scalars['String']>;
|
||||
/** Deletes an existing stream. */
|
||||
@@ -639,6 +655,18 @@ export type MutationServerInviteCreateArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationStreamAccessRequestCreateArgs = {
|
||||
streamId: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationStreamAccessRequestUseArgs = {
|
||||
accept: Scalars['Boolean'];
|
||||
requestId: Scalars['String'];
|
||||
role?: StreamRole;
|
||||
};
|
||||
|
||||
|
||||
export type MutationStreamCreateArgs = {
|
||||
stream: StreamCreateInput;
|
||||
};
|
||||
@@ -797,6 +825,27 @@ export type ObjectCreateInput = {
|
||||
streamId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type PasswordStrengthCheckFeedback = {
|
||||
__typename?: 'PasswordStrengthCheckFeedback';
|
||||
suggestions: Array<Scalars['String']>;
|
||||
warning?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type PasswordStrengthCheckResults = {
|
||||
__typename?: 'PasswordStrengthCheckResults';
|
||||
/** Verbal feedback to help choose better passwords. set when score <= 2. */
|
||||
feedback: PasswordStrengthCheckFeedback;
|
||||
/**
|
||||
* Integer from 0-4 (useful for implementing a strength bar):
|
||||
* 0 too guessable: risky password. (guesses < 10^3)
|
||||
* 1 very guessable: protection from throttled online attacks. (guesses < 10^6)
|
||||
* 2 somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8)
|
||||
* 3 safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10)
|
||||
* 4 very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10)
|
||||
*/
|
||||
score: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type PendingStreamCollaborator = {
|
||||
__typename?: 'PendingStreamCollaborator';
|
||||
id: Scalars['String'];
|
||||
@@ -817,6 +866,7 @@ export type Query = {
|
||||
__typename?: 'Query';
|
||||
/** Stare into the void. */
|
||||
_?: Maybe<Scalars['String']>;
|
||||
/** All the streams of the server. Available to admins only. */
|
||||
adminStreams?: Maybe<StreamCollection>;
|
||||
/**
|
||||
* Get all (or search for specific) users, registered or invited, from the server in a paginated view.
|
||||
@@ -836,6 +886,8 @@ export type Query = {
|
||||
comments?: Maybe<CommentCollection>;
|
||||
/** Commit/Object viewer state (local-only) */
|
||||
commitObjectViewerState: CommitObjectViewerState;
|
||||
/** All of the discoverable streams of the server */
|
||||
discoverableStreams?: Maybe<StreamCollection>;
|
||||
serverInfo: ServerInfo;
|
||||
serverStats: ServerStats;
|
||||
/**
|
||||
@@ -843,6 +895,8 @@ export type Query = {
|
||||
* to see it.
|
||||
*/
|
||||
stream?: Maybe<Stream>;
|
||||
/** Get authed user's stream access request */
|
||||
streamAccessRequest?: Maybe<StreamAccessRequest>;
|
||||
/**
|
||||
* Look for an invitation to a stream, for the current user (authed or not). If token
|
||||
* isn't specified, the server will look for any valid invite.
|
||||
@@ -852,12 +906,10 @@ export type Query = {
|
||||
streamInvites: Array<PendingStreamCollaborator>;
|
||||
/** All the streams of the current user, pass in the `query` parameter to search by name, description or ID. */
|
||||
streams?: Maybe<StreamCollection>;
|
||||
/**
|
||||
* Gets the profile of a user. If no id argument is provided, will return the current authenticated user's profile (as extracted from the authorization header).
|
||||
* If ID is provided, admin access is required
|
||||
*/
|
||||
/** Gets the profile of a user. If no id argument is provided, will return the current authenticated user's profile (as extracted from the authorization header). */
|
||||
user?: Maybe<User>;
|
||||
userPwdStrength?: Maybe<Scalars['JSONObject']>;
|
||||
/** Validate password strength */
|
||||
userPwdStrength: PasswordStrengthCheckResults;
|
||||
/**
|
||||
* Search for users and return limited metadata about them, if you have the server:user role.
|
||||
* The query looks for matches in name & email
|
||||
@@ -902,11 +954,23 @@ export type QueryCommentsArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryDiscoverableStreamsArgs = {
|
||||
cursor?: InputMaybe<Scalars['String']>;
|
||||
limit?: Scalars['Int'];
|
||||
sort?: InputMaybe<DiscoverableStreamsSortingInput>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryStreamArgs = {
|
||||
id: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryStreamAccessRequestArgs = {
|
||||
streamId: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryStreamInviteArgs = {
|
||||
streamId: Scalars['String'];
|
||||
token?: InputMaybe<Scalars['String']>;
|
||||
@@ -1084,6 +1148,11 @@ export type SmartTextEditorValue = {
|
||||
version: Scalars['String'];
|
||||
};
|
||||
|
||||
export enum SortDirection {
|
||||
Asc = 'ASC',
|
||||
Desc = 'DESC'
|
||||
}
|
||||
|
||||
export type Stream = {
|
||||
__typename?: 'Stream';
|
||||
/** All the recent activity on this stream in chronological order */
|
||||
@@ -1119,9 +1188,17 @@ export type Stream = {
|
||||
/** Returns a list of all the file uploads for this stream. */
|
||||
fileUploads?: Maybe<Array<Maybe<FileUpload>>>;
|
||||
id: Scalars['String'];
|
||||
/**
|
||||
* Whether the stream (if public) can be found on public stream exploration pages
|
||||
* and searches
|
||||
*/
|
||||
isDiscoverable: Scalars['Boolean'];
|
||||
/** Whether the stream can be viewed by non-contributors */
|
||||
isPublic: Scalars['Boolean'];
|
||||
name: Scalars['String'];
|
||||
object?: Maybe<Object>;
|
||||
/** Pending stream access requests */
|
||||
pendingAccessRequests?: Maybe<Array<StreamAccessRequest>>;
|
||||
/** Collaborators who have been invited, but not yet accepted. */
|
||||
pendingCollaborators?: Maybe<Array<PendingStreamCollaborator>>;
|
||||
/** Your role for this stream. `null` if request is not authenticated, or the stream is not explicitly shared with you. */
|
||||
@@ -1189,6 +1266,18 @@ export type StreamWebhooksArgs = {
|
||||
id?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
/** Created when a user requests to become a contributor on a stream */
|
||||
export type StreamAccessRequest = {
|
||||
__typename?: 'StreamAccessRequest';
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['ID'];
|
||||
requester: LimitedUser;
|
||||
requesterId: Scalars['String'];
|
||||
/** Can only be selected if authed user has proper access */
|
||||
stream: Stream;
|
||||
streamId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type StreamCollaborator = {
|
||||
__typename?: 'StreamCollaborator';
|
||||
avatar?: Maybe<Scalars['String']>;
|
||||
@@ -1207,6 +1296,12 @@ export type StreamCollection = {
|
||||
|
||||
export type StreamCreateInput = {
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
/**
|
||||
* Whether the stream (if public) can be found on public stream exploration pages
|
||||
* and searches
|
||||
*/
|
||||
isDiscoverable?: InputMaybe<Scalars['Boolean']>;
|
||||
/** Whether the stream can be viewed by non-contributors */
|
||||
isPublic?: InputMaybe<Scalars['Boolean']>;
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
/** Optionally specify user IDs of users that you want to invite to be contributors to this stream */
|
||||
@@ -1237,6 +1332,12 @@ export type StreamUpdateInput = {
|
||||
allowPublicComments?: InputMaybe<Scalars['Boolean']>;
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
id: Scalars['String'];
|
||||
/**
|
||||
* Whether the stream (if public) can be found on public stream exploration pages
|
||||
* and searches
|
||||
*/
|
||||
isDiscoverable?: InputMaybe<Scalars['Boolean']>;
|
||||
/** Whether the stream can be viewed by non-contributors */
|
||||
isPublic?: InputMaybe<Scalars['Boolean']>;
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
@@ -1378,13 +1479,14 @@ export type User = {
|
||||
email?: Maybe<Scalars['String']>;
|
||||
/** All the streams that a user has favorited */
|
||||
favoriteStreams?: Maybe<StreamCollection>;
|
||||
/** Whether the user has a pending/active email verification token */
|
||||
hasPendingVerification?: Maybe<Scalars['Boolean']>;
|
||||
id: Scalars['String'];
|
||||
name?: Maybe<Scalars['String']>;
|
||||
profiles?: Maybe<Scalars['JSONObject']>;
|
||||
role?: Maybe<Scalars['String']>;
|
||||
/** All the streams that a user has access to. */
|
||||
streams?: Maybe<StreamCollection>;
|
||||
suuid?: Maybe<Scalars['String']>;
|
||||
timeline?: Maybe<ActivityCollection>;
|
||||
/** Total amount of favorites attached to streams owned by the user */
|
||||
totalOwnedStreamsFavorites: Scalars['Int'];
|
||||
@@ -1531,6 +1633,29 @@ export type WebhookUpdateInput = {
|
||||
url?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type GetStreamAccessRequestQueryVariables = Exact<{
|
||||
streamId: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetStreamAccessRequestQuery = { __typename?: 'Query', streamAccessRequest?: { __typename?: 'StreamAccessRequest', id: string, streamId: string, createdAt: string } | null };
|
||||
|
||||
export type CreateStreamAccessRequestMutationVariables = Exact<{
|
||||
streamId: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateStreamAccessRequestMutation = { __typename?: 'Mutation', streamAccessRequestCreate: { __typename?: 'StreamAccessRequest', id: string, streamId: string, createdAt: string } };
|
||||
|
||||
export type UseStreamAccessRequestMutationVariables = Exact<{
|
||||
requestId: Scalars['String'];
|
||||
accept: Scalars['Boolean'];
|
||||
role?: InputMaybe<StreamRole>;
|
||||
}>;
|
||||
|
||||
|
||||
export type UseStreamAccessRequestMutation = { __typename?: 'Mutation', streamAccessRequestUse: boolean };
|
||||
|
||||
export type StreamWithBranchQueryVariables = Exact<{
|
||||
streamId: Scalars['String'];
|
||||
branchName: Scalars['String'];
|
||||
@@ -1538,7 +1663,7 @@ export type StreamWithBranchQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type StreamWithBranchQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, branch?: { __typename?: 'Branch', id: string, name: string, description?: string | null, commits?: { __typename?: 'CommitCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Commit', id: string, authorName?: string | null, authorId?: string | null, authorAvatar?: string | null, sourceApplication?: string | null, message?: string | null, referencedObject: string, createdAt?: any | null, commentCount: number } | null> | null } | null } | null } | null };
|
||||
export type StreamWithBranchQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, branch?: { __typename?: 'Branch', id: string, name: string, description?: string | null, commits?: { __typename?: 'CommitCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Commit', id: string, authorName?: string | null, authorId?: string | null, authorAvatar?: string | null, sourceApplication?: string | null, message?: string | null, referencedObject: string, createdAt?: string | null, commentCount: number } | null> | null } | null } | null } | null };
|
||||
|
||||
export type BranchCreatedSubscriptionVariables = Exact<{
|
||||
streamId: Scalars['String'];
|
||||
@@ -1547,7 +1672,7 @@ export type BranchCreatedSubscriptionVariables = Exact<{
|
||||
|
||||
export type BranchCreatedSubscription = { __typename?: 'Subscription', branchCreated?: Record<string, unknown> | null };
|
||||
|
||||
export type CommentFullInfoFragment = { __typename?: 'Comment', id: string, archived: boolean, authorId: string, data?: Record<string, unknown> | null, screenshot?: string | null, createdAt?: any | null, updatedAt?: any | null, viewedAt?: any | null, text: { __typename?: 'SmartTextEditorValue', doc?: Record<string, unknown> | null, attachments?: Array<{ __typename?: 'BlobMetadata', id: string, fileName: string, streamId: string, fileType: string, fileSize?: number | null }> | null }, replies?: { __typename?: 'CommentCollection', totalCount: number } | null, resources: Array<{ __typename?: 'ResourceIdentifier', resourceId: string, resourceType: ResourceType } | null> };
|
||||
export type CommentFullInfoFragment = { __typename?: 'Comment', id: string, archived: boolean, authorId: string, data?: Record<string, unknown> | null, screenshot?: string | null, createdAt?: string | null, updatedAt?: string | null, viewedAt?: string | null, text: { __typename?: 'SmartTextEditorValue', doc?: Record<string, unknown> | null, attachments?: Array<{ __typename?: 'BlobMetadata', id: string, fileName: string, streamId: string, fileType: string, fileSize?: number | null }> | null }, replies?: { __typename?: 'CommentCollection', totalCount: number } | null, resources: Array<{ __typename?: 'ResourceIdentifier', resourceId: string, resourceType: ResourceType } | null> };
|
||||
|
||||
export type StreamCommitQueryQueryVariables = Exact<{
|
||||
streamId: Scalars['String'];
|
||||
@@ -1555,11 +1680,17 @@ export type StreamCommitQueryQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type StreamCommitQueryQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, role?: string | null, commit?: { __typename?: 'Commit', id: string, message?: string | null, referencedObject: string, authorName?: string | null, authorId?: string | null, authorAvatar?: string | null, createdAt?: any | null, branchName?: string | null, sourceApplication?: string | null } | null } | null };
|
||||
export type StreamCommitQueryQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, role?: string | null, commit?: { __typename?: 'Commit', id: string, message?: string | null, referencedObject: string, authorName?: string | null, authorId?: string | null, authorAvatar?: string | null, createdAt?: string | null, branchName?: string | null, sourceApplication?: string | null } | null } | null };
|
||||
|
||||
export type ActivityMainFieldsFragment = { __typename?: 'Activity', id: string, actionType: string, info: Record<string, unknown>, userId: string, streamId?: string | null, resourceId: string, resourceType: string, time: any, message: string };
|
||||
export type BasicStreamAccessRequestFieldsFragment = { __typename?: 'StreamAccessRequest', id: string, streamId: string, createdAt: string };
|
||||
|
||||
export type LimitedCommitActivityFieldsFragment = { __typename?: 'Activity', id: string, info: Record<string, unknown>, time: any, userId: string, message: string };
|
||||
export type FullStreamAccessRequestFieldsFragment = { __typename?: 'StreamAccessRequest', id: string, streamId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null } };
|
||||
|
||||
export type ActivityMainFieldsFragment = { __typename?: 'Activity', id: string, actionType: string, info: Record<string, unknown>, userId: string, streamId?: string | null, resourceId: string, resourceType: string, time: string, message: string };
|
||||
|
||||
export type LimitedCommitActivityFieldsFragment = { __typename?: 'Activity', id: string, info: Record<string, unknown>, time: string, userId: string, message: string };
|
||||
|
||||
export type StreamPendingAccessRequestsFragment = { __typename?: 'Stream', pendingAccessRequests?: Array<{ __typename?: 'StreamAccessRequest', id: string, streamId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null } }> | null };
|
||||
|
||||
export type LimitedUserFieldsFragment = { __typename?: 'LimitedUser', id: string, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null };
|
||||
|
||||
@@ -1669,30 +1800,30 @@ export type StreamCommitsQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type StreamCommitsQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, role?: string | null, commits?: { __typename?: 'CommitCollection', totalCount: number, items?: Array<{ __typename?: 'Commit', id: string, authorId?: string | null, authorName?: string | null, authorAvatar?: string | null, createdAt?: any | null, message?: string | null, referencedObject: string, branchName?: string | null, sourceApplication?: string | null } | null> | null } | null } | null };
|
||||
export type StreamCommitsQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, role?: string | null, commits?: { __typename?: 'CommitCollection', totalCount: number, items?: Array<{ __typename?: 'Commit', id: string, authorId?: string | null, authorName?: string | null, authorAvatar?: string | null, createdAt?: string | null, message?: string | null, referencedObject: string, branchName?: string | null, sourceApplication?: string | null } | null> | null } | null } | null };
|
||||
|
||||
export type StreamsQueryVariables = Exact<{
|
||||
cursor?: InputMaybe<Scalars['String']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type StreamsQuery = { __typename?: 'Query', streams?: { __typename?: 'StreamCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Stream', id: string, name: string, description?: string | null, role?: string | null, isPublic: boolean, createdAt: any, updatedAt: any, commentCount: number, favoritedDate?: any | null, favoritesCount: number, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, company?: string | null, avatar?: string | null, role: string }>, commits?: { __typename?: 'CommitCollection', totalCount: number, items?: Array<{ __typename?: 'Commit', id: string, createdAt?: any | null, message?: string | null, authorId?: string | null, branchName?: string | null, authorName?: string | null, authorAvatar?: string | null, referencedObject: string } | null> | null } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null }> | null } | null };
|
||||
export type StreamsQuery = { __typename?: 'Query', streams?: { __typename?: 'StreamCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Stream', id: string, name: string, description?: string | null, role?: string | null, isPublic: boolean, createdAt: string, updatedAt: string, commentCount: number, favoritedDate?: string | null, favoritesCount: number, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, company?: string | null, avatar?: string | null, role: string }>, commits?: { __typename?: 'CommitCollection', totalCount: number, items?: Array<{ __typename?: 'Commit', id: string, createdAt?: string | null, message?: string | null, authorId?: string | null, branchName?: string | null, authorName?: string | null, authorAvatar?: string | null, referencedObject: string } | null> | null } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null }> | null } | null };
|
||||
|
||||
export type CommonStreamFieldsFragment = { __typename?: 'Stream', id: string, name: string, description?: string | null, role?: string | null, isPublic: boolean, createdAt: any, updatedAt: any, commentCount: number, favoritedDate?: any | null, favoritesCount: number, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, company?: string | null, avatar?: string | null, role: string }>, commits?: { __typename?: 'CommitCollection', totalCount: number } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null };
|
||||
export type CommonStreamFieldsFragment = { __typename?: 'Stream', id: string, name: string, description?: string | null, role?: string | null, isPublic: boolean, createdAt: string, updatedAt: string, commentCount: number, favoritedDate?: string | null, favoritesCount: number, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, company?: string | null, avatar?: string | null, role: string }>, commits?: { __typename?: 'CommitCollection', totalCount: number } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null };
|
||||
|
||||
export type StreamQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StreamQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, description?: string | null, role?: string | null, isPublic: boolean, createdAt: any, updatedAt: any, commentCount: number, favoritedDate?: any | null, favoritesCount: number, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, company?: string | null, avatar?: string | null, role: string }>, commits?: { __typename?: 'CommitCollection', totalCount: number } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null } | null };
|
||||
export type StreamQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, description?: string | null, role?: string | null, isPublic: boolean, createdAt: string, updatedAt: string, commentCount: number, favoritedDate?: string | null, favoritesCount: number, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, company?: string | null, avatar?: string | null, role: string }>, commits?: { __typename?: 'CommitCollection', totalCount: number } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null } | null };
|
||||
|
||||
export type StreamWithCollaboratorsQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StreamWithCollaboratorsQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, isPublic: boolean, role?: string | null, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, role: string, company?: string | null, avatar?: string | null }>, pendingCollaborators?: Array<{ __typename?: 'PendingStreamCollaborator', title: string, inviteId: string, role: string, user?: { __typename?: 'LimitedUser', id: string, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null } | null }> | null } | null };
|
||||
export type StreamWithCollaboratorsQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, isPublic: boolean, role?: string | null, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, role: string, company?: string | null, avatar?: string | null }>, pendingCollaborators?: Array<{ __typename?: 'PendingStreamCollaborator', title: string, inviteId: string, role: string, user?: { __typename?: 'LimitedUser', id: string, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null } | null }> | null, pendingAccessRequests?: Array<{ __typename?: 'StreamAccessRequest', id: string, streamId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null } }> | null } | null };
|
||||
|
||||
export type StreamWithActivityQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -1700,7 +1831,7 @@ export type StreamWithActivityQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type StreamWithActivityQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, createdAt: any, commits?: { __typename?: 'CommitCollection', totalCount: number } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null, activity?: { __typename?: 'ActivityCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Activity', id: string, actionType: string, info: Record<string, unknown>, userId: string, streamId?: string | null, resourceId: string, resourceType: string, time: any, message: string } | null> | null } | null } | null };
|
||||
export type StreamWithActivityQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, createdAt: string, commits?: { __typename?: 'CommitCollection', totalCount: number } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null, activity?: { __typename?: 'ActivityCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Activity', id: string, actionType: string, info: Record<string, unknown>, userId: string, streamId?: string | null, resourceId: string, resourceType: string, time: string, message: string } | null> | null } | null } | null };
|
||||
|
||||
export type LeaveStreamMutationVariables = Exact<{
|
||||
streamId: Scalars['String'];
|
||||
@@ -1731,24 +1862,24 @@ export type StreamBranchFirstCommitQueryVariables = Exact<{
|
||||
|
||||
export type StreamBranchFirstCommitQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, branch?: { __typename?: 'Branch', commits?: { __typename?: 'CommitCollection', totalCount: number, items?: Array<{ __typename?: 'Commit', id: string, referencedObject: string } | null> | null } | null } | null } | null };
|
||||
|
||||
export type CommonUserFieldsFragment = { __typename?: 'User', id: string, suuid?: string | null, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, streams?: { __typename?: 'StreamCollection', totalCount: number } | null, commits?: { __typename?: 'CommitCollectionUser', totalCount: number, items?: Array<{ __typename?: 'CommitCollectionUserNode', id: string, createdAt?: any | null } | null> | null } | null };
|
||||
export type CommonUserFieldsFragment = { __typename?: 'User', id: string, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, hasPendingVerification?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, streams?: { __typename?: 'StreamCollection', totalCount: number } | null, commits?: { __typename?: 'CommitCollectionUser', totalCount: number, items?: Array<{ __typename?: 'CommitCollectionUserNode', id: string, createdAt?: string | null } | null> | null } | null };
|
||||
|
||||
export type UserFavoriteStreamsQueryVariables = Exact<{
|
||||
cursor?: InputMaybe<Scalars['String']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type UserFavoriteStreamsQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, suuid?: string | null, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, favoriteStreams?: { __typename?: 'StreamCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Stream', id: string, name: string, description?: string | null, role?: string | null, isPublic: boolean, createdAt: any, updatedAt: any, commentCount: number, favoritedDate?: any | null, favoritesCount: number, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, company?: string | null, avatar?: string | null, role: string }>, commits?: { __typename?: 'CommitCollection', totalCount: number } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null }> | null } | null, streams?: { __typename?: 'StreamCollection', totalCount: number } | null, commits?: { __typename?: 'CommitCollectionUser', totalCount: number, items?: Array<{ __typename?: 'CommitCollectionUserNode', id: string, createdAt?: any | null } | null> | null } | null } | null };
|
||||
export type UserFavoriteStreamsQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, hasPendingVerification?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, favoriteStreams?: { __typename?: 'StreamCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Stream', id: string, name: string, description?: string | null, role?: string | null, isPublic: boolean, createdAt: string, updatedAt: string, commentCount: number, favoritedDate?: string | null, favoritesCount: number, collaborators: Array<{ __typename?: 'StreamCollaborator', id: string, name: string, company?: string | null, avatar?: string | null, role: string }>, commits?: { __typename?: 'CommitCollection', totalCount: number } | null, branches?: { __typename?: 'BranchCollection', totalCount: number } | null }> | null } | null, streams?: { __typename?: 'StreamCollection', totalCount: number } | null, commits?: { __typename?: 'CommitCollectionUser', totalCount: number, items?: Array<{ __typename?: 'CommitCollectionUserNode', id: string, createdAt?: string | null } | null> | null } | null } | null };
|
||||
|
||||
export type MainUserDataQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type MainUserDataQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, suuid?: string | null, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, streams?: { __typename?: 'StreamCollection', totalCount: number } | null, commits?: { __typename?: 'CommitCollectionUser', totalCount: number, items?: Array<{ __typename?: 'CommitCollectionUserNode', id: string, createdAt?: any | null } | null> | null } | null } | null };
|
||||
export type MainUserDataQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, hasPendingVerification?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, streams?: { __typename?: 'StreamCollection', totalCount: number } | null, commits?: { __typename?: 'CommitCollectionUser', totalCount: number, items?: Array<{ __typename?: 'CommitCollectionUserNode', id: string, createdAt?: string | null } | null> | null } | null } | null };
|
||||
|
||||
export type ExtraUserDataQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ExtraUserDataQuery = { __typename?: 'Query', user?: { __typename?: 'User', totalOwnedStreamsFavorites: number, id: string, suuid?: string | null, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, streams?: { __typename?: 'StreamCollection', totalCount: number } | null, commits?: { __typename?: 'CommitCollectionUser', totalCount: number, items?: Array<{ __typename?: 'CommitCollectionUserNode', id: string, createdAt?: any | null } | null> | null } | null } | null };
|
||||
export type ExtraUserDataQuery = { __typename?: 'Query', user?: { __typename?: 'User', totalOwnedStreamsFavorites: number, id: string, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, hasPendingVerification?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, streams?: { __typename?: 'StreamCollection', totalCount: number } | null, commits?: { __typename?: 'CommitCollectionUser', totalCount: number, items?: Array<{ __typename?: 'CommitCollectionUserNode', id: string, createdAt?: string | null } | null> | null } | null } | null };
|
||||
|
||||
export type UserSearchQueryVariables = Exact<{
|
||||
query: Scalars['String'];
|
||||
@@ -1772,21 +1903,38 @@ export type AdminUsersListQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type AdminUsersListQuery = { __typename?: 'Query', adminUsers?: { __typename?: 'AdminUsersListCollection', totalCount: number, items: Array<{ __typename?: 'AdminUsersListItem', id: string, registeredUser?: { __typename?: 'User', id: string, suuid?: string | null, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, authorizedApps?: Array<{ __typename?: 'ServerAppListItem', name: string } | null> | null } | null, invitedUser?: { __typename?: 'ServerInvite', id: string, email: string, invitedBy: { __typename?: 'LimitedUser', id: string, name?: string | null } } | null }> } | null };
|
||||
export type AdminUsersListQuery = { __typename?: 'Query', adminUsers?: { __typename?: 'AdminUsersListCollection', totalCount: number, items: Array<{ __typename?: 'AdminUsersListItem', id: string, registeredUser?: { __typename?: 'User', id: string, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, authorizedApps?: Array<{ __typename?: 'ServerAppListItem', name: string } | null> | null } | null, invitedUser?: { __typename?: 'ServerInvite', id: string, email: string, invitedBy: { __typename?: 'LimitedUser', id: string, name?: string | null } } | null }> } | null };
|
||||
|
||||
export type UserTimelineQueryVariables = Exact<{
|
||||
cursor?: InputMaybe<Scalars['DateTime']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type UserTimelineQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, timeline?: { __typename?: 'ActivityCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Activity', id: string, actionType: string, info: Record<string, unknown>, userId: string, streamId?: string | null, resourceId: string, resourceType: string, time: any, message: string } | null> | null } | null } | null };
|
||||
export type UserTimelineQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, timeline?: { __typename?: 'ActivityCollection', totalCount: number, cursor?: string | null, items?: Array<{ __typename?: 'Activity', id: string, actionType: string, info: Record<string, unknown>, userId: string, streamId?: string | null, resourceId: string, resourceType: string, time: string, message: string } | null> | null } | null } | null };
|
||||
|
||||
export type UserQueryVariables = Exact<{
|
||||
export type ValidatePasswordStrengthQueryVariables = Exact<{
|
||||
pwd: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ValidatePasswordStrengthQuery = { __typename?: 'Query', userPwdStrength: { __typename?: 'PasswordStrengthCheckResults', score: number, feedback: { __typename?: 'PasswordStrengthCheckFeedback', warning?: string | null, suggestions: Array<string> } } };
|
||||
|
||||
export type EmailVerificationBannerStateQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type EmailVerificationBannerStateQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, email?: string | null, verified?: boolean | null, hasPendingVerification?: boolean | null } | null };
|
||||
|
||||
export type RequestVerificationMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type RequestVerificationMutation = { __typename?: 'Mutation', requestVerification: boolean };
|
||||
|
||||
export type UserByIdQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type UserQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null, suuid?: string | null } | null };
|
||||
export type UserByIdQuery = { __typename?: 'Query', user?: { __typename?: 'User', id: string, email?: string | null, name?: string | null, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, profiles?: Record<string, unknown> | null, role?: string | null } | null };
|
||||
|
||||
export type UserProfileQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -1808,7 +1956,7 @@ export type WebhooksQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type WebhooksQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, role?: string | null, webhooks?: { __typename?: 'WebhookCollection', items?: Array<{ __typename?: 'Webhook', id: string, streamId: string, url: string, description?: string | null, triggers: Array<string | null>, enabled?: boolean | null, history?: { __typename?: 'WebhookEventCollection', items?: Array<{ __typename?: 'WebhookEvent', status: number, statusInfo: string, lastUpdate: any } | null> | null } | null } | null> | null } | null } | null };
|
||||
export type WebhooksQuery = { __typename?: 'Query', stream?: { __typename?: 'Stream', id: string, name: string, role?: string | null, webhooks?: { __typename?: 'WebhookCollection', items?: Array<{ __typename?: 'Webhook', id: string, streamId: string, url: string, description?: string | null, triggers: Array<string | null>, enabled?: boolean | null, history?: { __typename?: 'WebhookEventCollection', items?: Array<{ __typename?: 'WebhookEvent', status: number, statusInfo: string, lastUpdate: string } | null> | null } | null } | null> | null } | null } | null };
|
||||
|
||||
export const CommentFullInfo = gql`
|
||||
fragment CommentFullInfo on Comment {
|
||||
@@ -1861,13 +2009,11 @@ export const LimitedCommitActivityFields = gql`
|
||||
message
|
||||
}
|
||||
`;
|
||||
export const StreamCollaboratorFields = gql`
|
||||
fragment StreamCollaboratorFields on StreamCollaborator {
|
||||
export const BasicStreamAccessRequestFields = gql`
|
||||
fragment BasicStreamAccessRequestFields on StreamAccessRequest {
|
||||
id
|
||||
name
|
||||
role
|
||||
company
|
||||
avatar
|
||||
streamId
|
||||
createdAt
|
||||
}
|
||||
`;
|
||||
export const LimitedUserFields = gql`
|
||||
@@ -1880,6 +2026,30 @@ export const LimitedUserFields = gql`
|
||||
verified
|
||||
}
|
||||
`;
|
||||
export const FullStreamAccessRequestFields = gql`
|
||||
fragment FullStreamAccessRequestFields on StreamAccessRequest {
|
||||
...BasicStreamAccessRequestFields
|
||||
requester {
|
||||
...LimitedUserFields
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const StreamPendingAccessRequests = gql`
|
||||
fragment StreamPendingAccessRequests on Stream {
|
||||
pendingAccessRequests {
|
||||
...FullStreamAccessRequestFields
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const StreamCollaboratorFields = gql`
|
||||
fragment StreamCollaboratorFields on StreamCollaborator {
|
||||
id
|
||||
name
|
||||
role
|
||||
company
|
||||
avatar
|
||||
}
|
||||
`;
|
||||
export const UsersOwnInviteFields = gql`
|
||||
fragment UsersOwnInviteFields on PendingStreamCollaborator {
|
||||
id
|
||||
@@ -1891,7 +2061,7 @@ export const UsersOwnInviteFields = gql`
|
||||
...LimitedUserFields
|
||||
}
|
||||
}
|
||||
${LimitedUserFields}`;
|
||||
`;
|
||||
export const ServerInfoBlobSizeFields = gql`
|
||||
fragment ServerInfoBlobSizeFields on ServerInfo {
|
||||
blobSizeLimitBytes
|
||||
@@ -1956,16 +2126,15 @@ export const CommonStreamFields = gql`
|
||||
export const CommonUserFields = gql`
|
||||
fragment CommonUserFields on User {
|
||||
id
|
||||
suuid
|
||||
email
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
hasPendingVerification
|
||||
profiles
|
||||
role
|
||||
suuid
|
||||
streams {
|
||||
totalCount
|
||||
}
|
||||
@@ -1978,6 +2147,25 @@ export const CommonUserFields = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const GetStreamAccessRequest = gql`
|
||||
query GetStreamAccessRequest($streamId: String!) {
|
||||
streamAccessRequest(streamId: $streamId) {
|
||||
...BasicStreamAccessRequestFields
|
||||
}
|
||||
}
|
||||
${BasicStreamAccessRequestFields}`;
|
||||
export const CreateStreamAccessRequest = gql`
|
||||
mutation CreateStreamAccessRequest($streamId: String!) {
|
||||
streamAccessRequestCreate(streamId: $streamId) {
|
||||
...BasicStreamAccessRequestFields
|
||||
}
|
||||
}
|
||||
${BasicStreamAccessRequestFields}`;
|
||||
export const UseStreamAccessRequest = gql`
|
||||
mutation UseStreamAccessRequest($requestId: String!, $accept: Boolean!, $role: StreamRole = STREAM_CONTRIBUTOR) {
|
||||
streamAccessRequestUse(requestId: $requestId, accept: $accept, role: $role)
|
||||
}
|
||||
`;
|
||||
export const StreamWithBranch = gql`
|
||||
query StreamWithBranch($streamId: String!, $branchName: String!, $cursor: String) {
|
||||
stream(id: $streamId) {
|
||||
@@ -2037,14 +2225,16 @@ export const StreamInvite = gql`
|
||||
...UsersOwnInviteFields
|
||||
}
|
||||
}
|
||||
${UsersOwnInviteFields}`;
|
||||
${UsersOwnInviteFields}
|
||||
${LimitedUserFields}`;
|
||||
export const UserStreamInvites = gql`
|
||||
query UserStreamInvites {
|
||||
streamInvites {
|
||||
...UsersOwnInviteFields
|
||||
}
|
||||
}
|
||||
${UsersOwnInviteFields}`;
|
||||
${UsersOwnInviteFields}
|
||||
${LimitedUserFields}`;
|
||||
export const UseStreamInvite = gql`
|
||||
mutation UseStreamInvite($accept: Boolean!, $streamId: String!, $token: String!) {
|
||||
streamInviteUse(accept: $accept, streamId: $streamId, token: $token)
|
||||
@@ -2218,10 +2408,15 @@ export const StreamWithCollaborators = gql`
|
||||
...LimitedUserFields
|
||||
}
|
||||
}
|
||||
pendingAccessRequests {
|
||||
...FullStreamAccessRequestFields
|
||||
}
|
||||
}
|
||||
}
|
||||
${StreamCollaboratorFields}
|
||||
${LimitedUserFields}`;
|
||||
${LimitedUserFields}
|
||||
${FullStreamAccessRequestFields}
|
||||
${BasicStreamAccessRequestFields}`;
|
||||
export const StreamWithActivity = gql`
|
||||
query StreamWithActivity($id: String!, $cursor: DateTime) {
|
||||
stream(id: $id) {
|
||||
@@ -2339,7 +2534,6 @@ export const AdminUsersList = gql`
|
||||
id
|
||||
registeredUser {
|
||||
id
|
||||
suuid
|
||||
email
|
||||
name
|
||||
bio
|
||||
@@ -2378,8 +2572,34 @@ export const UserTimeline = gql`
|
||||
}
|
||||
}
|
||||
${ActivityMainFields}`;
|
||||
export const User = gql`
|
||||
query User($id: String!) {
|
||||
export const ValidatePasswordStrength = gql`
|
||||
query ValidatePasswordStrength($pwd: String!) {
|
||||
userPwdStrength(pwd: $pwd) {
|
||||
score
|
||||
feedback {
|
||||
warning
|
||||
suggestions
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const EmailVerificationBannerState = gql`
|
||||
query EmailVerificationBannerState {
|
||||
user {
|
||||
id
|
||||
email
|
||||
verified
|
||||
hasPendingVerification
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const RequestVerification = gql`
|
||||
mutation RequestVerification {
|
||||
requestVerification
|
||||
}
|
||||
`;
|
||||
export const UserById = gql`
|
||||
query UserById($id: String!) {
|
||||
user(id: $id) {
|
||||
id
|
||||
email
|
||||
@@ -2390,7 +2610,6 @@ export const User = gql`
|
||||
verified
|
||||
profiles
|
||||
role
|
||||
suuid
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -2459,20 +2678,26 @@ export const Webhooks = gql`
|
||||
export const CommentFullInfoFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFullInfo"},"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":"archived"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"text"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"doc"}},{"kind":"Field","name":{"kind":"Name","value":"attachments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileSize"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"screenshot"}},{"kind":"Field","name":{"kind":"Name","value":"replies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"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"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"viewedAt"}}]}}]} as unknown as DocumentNode<CommentFullInfoFragment, unknown>;
|
||||
export const ActivityMainFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ActivityMainFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Activity"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"actionType"}},{"kind":"Field","name":{"kind":"Name","value":"info"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"resourceId"}},{"kind":"Field","name":{"kind":"Name","value":"resourceType"}},{"kind":"Field","name":{"kind":"Name","value":"time"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]} as unknown as DocumentNode<ActivityMainFieldsFragment, unknown>;
|
||||
export const LimitedCommitActivityFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedCommitActivityFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Activity"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"info"}},{"kind":"Field","name":{"kind":"Name","value":"time"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]} as unknown as DocumentNode<LimitedCommitActivityFieldsFragment, unknown>;
|
||||
export const StreamCollaboratorFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StreamCollaboratorFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamCollaborator"}},"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":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]} as unknown as DocumentNode<StreamCollaboratorFieldsFragment, unknown>;
|
||||
export const BasicStreamAccessRequestFieldsFragmentDoc = {"kind":"Document","definitions":[{"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":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode<BasicStreamAccessRequestFieldsFragment, unknown>;
|
||||
export const LimitedUserFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}}]}}]} as unknown as DocumentNode<LimitedUserFieldsFragment, unknown>;
|
||||
export const UsersOwnInviteFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UsersOwnInviteFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingStreamCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"streamName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserFields"}}]}}]}},...LimitedUserFieldsFragmentDoc.definitions]} as unknown as DocumentNode<UsersOwnInviteFieldsFragment, unknown>;
|
||||
export const FullStreamAccessRequestFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FullStreamAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserFields"}}]}}]}}]} as unknown as DocumentNode<FullStreamAccessRequestFieldsFragment, unknown>;
|
||||
export const StreamPendingAccessRequestsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StreamPendingAccessRequests"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Stream"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pendingAccessRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FullStreamAccessRequestFields"}}]}}]}}]} as unknown as DocumentNode<StreamPendingAccessRequestsFragment, unknown>;
|
||||
export const StreamCollaboratorFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StreamCollaboratorFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamCollaborator"}},"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":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]} as unknown as DocumentNode<StreamCollaboratorFieldsFragment, unknown>;
|
||||
export const UsersOwnInviteFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UsersOwnInviteFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingStreamCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"streamName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserFields"}}]}}]}}]} as unknown as DocumentNode<UsersOwnInviteFieldsFragment, unknown>;
|
||||
export const ServerInfoBlobSizeFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerInfoBlobSizeFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blobSizeLimitBytes"}}]}}]} as unknown as DocumentNode<ServerInfoBlobSizeFieldsFragment, unknown>;
|
||||
export const MainServerInfoFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MainServerInfoFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"adminContact"}},{"kind":"Field","name":{"kind":"Name","value":"canonicalUrl"}},{"kind":"Field","name":{"kind":"Name","value":"termsOfService"}},{"kind":"Field","name":{"kind":"Name","value":"inviteOnly"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}}]} as unknown as DocumentNode<MainServerInfoFieldsFragment, unknown>;
|
||||
export const ServerInfoRolesFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerInfoRolesFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"roles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"resourceTarget"}}]}}]}}]} as unknown as DocumentNode<ServerInfoRolesFieldsFragment, unknown>;
|
||||
export const ServerInfoScopesFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerInfoScopesFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scopes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]} as unknown as DocumentNode<ServerInfoScopesFieldsFragment, unknown>;
|
||||
export const CommonStreamFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommonStreamFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Stream"}},"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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"isPublic"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"commentCount"}},{"kind":"Field","name":{"kind":"Name","value":"collaborators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"Field","name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"branches"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"favoritedDate"}},{"kind":"Field","name":{"kind":"Name","value":"favoritesCount"}}]}}]} as unknown as DocumentNode<CommonStreamFieldsFragment, unknown>;
|
||||
export const CommonUserFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommonUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"suuid"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"profiles"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"suuid"}},{"kind":"Field","name":{"kind":"Name","value":"streams"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode<CommonUserFieldsFragment, unknown>;
|
||||
export const CommonUserFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommonUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"hasPendingVerification"}},{"kind":"Field","name":{"kind":"Name","value":"profiles"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"streams"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode<CommonUserFieldsFragment, unknown>;
|
||||
export const GetStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetStreamAccessRequest"},"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":"streamAccessRequest"},"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"}}]}}]}},...BasicStreamAccessRequestFieldsFragmentDoc.definitions]} as unknown as DocumentNode<GetStreamAccessRequestQuery, GetStreamAccessRequestQueryVariables>;
|
||||
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"}}]}}]}},...BasicStreamAccessRequestFieldsFragmentDoc.definitions]} as unknown as DocumentNode<CreateStreamAccessRequestMutation, CreateStreamAccessRequestMutationVariables>;
|
||||
export const UseStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UseStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"requestId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"accept"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"role"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StreamRole"}},"defaultValue":{"kind":"EnumValue","value":"STREAM_CONTRIBUTOR"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequestUse"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"requestId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"requestId"}}},{"kind":"Argument","name":{"kind":"Name","value":"accept"},"value":{"kind":"Variable","name":{"kind":"Name","value":"accept"}}},{"kind":"Argument","name":{"kind":"Name","value":"role"},"value":{"kind":"Variable","name":{"kind":"Name","value":"role"}}}]}]}}]} as unknown as DocumentNode<UseStreamAccessRequestMutation, UseStreamAccessRequestMutationVariables>;
|
||||
export const StreamWithBranchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamWithBranch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}},"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"branch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}}}],"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":"commits"},"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":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"authorAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"commentCount"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<StreamWithBranchQuery, StreamWithBranchQueryVariables>;
|
||||
export const BranchCreatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"BranchCreated"},"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":"branchCreated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}]}]}}]} as unknown as DocumentNode<BranchCreatedSubscription, BranchCreatedSubscriptionVariables>;
|
||||
export const StreamCommitQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamCommitQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"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":"commit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"authorAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"branchName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}}]}}]}}]}}]} as unknown as DocumentNode<StreamCommitQueryQuery, StreamCommitQueryQueryVariables>;
|
||||
export const StreamInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"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":"streamInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}},{"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":"UsersOwnInviteFields"}}]}}]}},...UsersOwnInviteFieldsFragmentDoc.definitions]} as unknown as DocumentNode<StreamInviteQuery, StreamInviteQueryVariables>;
|
||||
export const UserStreamInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserStreamInvites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamInvites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UsersOwnInviteFields"}}]}}]}},...UsersOwnInviteFieldsFragmentDoc.definitions]} as unknown as DocumentNode<UserStreamInvitesQuery, UserStreamInvitesQueryVariables>;
|
||||
export const StreamInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"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":"streamInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}},{"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":"UsersOwnInviteFields"}}]}}]}},...UsersOwnInviteFieldsFragmentDoc.definitions,...LimitedUserFieldsFragmentDoc.definitions]} as unknown as DocumentNode<StreamInviteQuery, StreamInviteQueryVariables>;
|
||||
export const UserStreamInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserStreamInvites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamInvites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UsersOwnInviteFields"}}]}}]}},...UsersOwnInviteFieldsFragmentDoc.definitions,...LimitedUserFieldsFragmentDoc.definitions]} as unknown as DocumentNode<UserStreamInvitesQuery, UserStreamInvitesQueryVariables>;
|
||||
export const UseStreamInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UseStreamInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"accept"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamInviteUse"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"accept"},"value":{"kind":"Variable","name":{"kind":"Name","value":"accept"}}},{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}]}]}}]} as unknown as DocumentNode<UseStreamInviteMutation, UseStreamInviteMutationVariables>;
|
||||
export const CancelStreamInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CancelStreamInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamInviteCancel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}},{"kind":"Argument","name":{"kind":"Name","value":"inviteId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}}}]}]}}]} as unknown as DocumentNode<CancelStreamInviteMutation, CancelStreamInviteMutationVariables>;
|
||||
export const DeleteInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inviteDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"inviteId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}}}]}]}}]} as unknown as DocumentNode<DeleteInviteMutation, DeleteInviteMutationVariables>;
|
||||
@@ -2487,7 +2712,7 @@ export const ServerInfoBlobSizeLimitDocument = {"kind":"Document","definitions":
|
||||
export const StreamCommitsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamCommits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"commits"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"authorAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"branchName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}}]}}]}}]}}]}}]} as unknown as DocumentNode<StreamCommitsQuery, StreamCommitsQueryVariables>;
|
||||
export const StreamsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Streams"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streams"},"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":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"isPublic"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"commentCount"}},{"kind":"Field","name":{"kind":"Name","value":"collaborators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"Field","name":{"kind":"Name","value":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"branchName"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"authorAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"branches"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"favoritedDate"}},{"kind":"Field","name":{"kind":"Name","value":"favoritesCount"}}]}}]}}]}}]} as unknown as DocumentNode<StreamsQuery, StreamsQueryVariables>;
|
||||
export const StreamDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Stream"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommonStreamFields"}}]}}]}},...CommonStreamFieldsFragmentDoc.definitions]} as unknown as DocumentNode<StreamQuery, StreamQueryVariables>;
|
||||
export const StreamWithCollaboratorsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamWithCollaborators"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isPublic"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"collaborators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StreamCollaboratorFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingCollaborators"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"LimitedUserFields"}}]}}]}}]}}]}},...StreamCollaboratorFieldsFragmentDoc.definitions,...LimitedUserFieldsFragmentDoc.definitions]} as unknown as DocumentNode<StreamWithCollaboratorsQuery, StreamWithCollaboratorsQueryVariables>;
|
||||
export const StreamWithCollaboratorsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamWithCollaborators"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isPublic"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"collaborators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StreamCollaboratorFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingCollaborators"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"LimitedUserFields"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingAccessRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FullStreamAccessRequestFields"}}]}}]}}]}},...StreamCollaboratorFieldsFragmentDoc.definitions,...LimitedUserFieldsFragmentDoc.definitions,...FullStreamAccessRequestFieldsFragmentDoc.definitions,...BasicStreamAccessRequestFieldsFragmentDoc.definitions]} as unknown as DocumentNode<StreamWithCollaboratorsQuery, StreamWithCollaboratorsQueryVariables>;
|
||||
export const StreamWithActivityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"StreamWithActivity"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"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":"DateTime"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"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":"commits"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"branches"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"activity"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}}],"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":"ActivityMainFields"}}]}}]}}]}}]}},...ActivityMainFieldsFragmentDoc.definitions]} as unknown as DocumentNode<StreamWithActivityQuery, StreamWithActivityQueryVariables>;
|
||||
export const LeaveStreamDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"LeaveStream"},"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":"streamLeave"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}]}]}}]} as unknown as DocumentNode<LeaveStreamMutation, LeaveStreamMutationVariables>;
|
||||
export const UpdateStreamPermissionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateStreamPermission"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"params"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"StreamUpdatePermissionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamUpdatePermission"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"permissionParams"},"value":{"kind":"Variable","name":{"kind":"Name","value":"params"}}}]}]}}]} as unknown as DocumentNode<UpdateStreamPermissionMutation, UpdateStreamPermissionMutationVariables>;
|
||||
@@ -2498,9 +2723,12 @@ export const MainUserDataDocument = {"kind":"Document","definitions":[{"kind":"O
|
||||
export const ExtraUserDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExtraUserData"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommonUserFields"}},{"kind":"Field","name":{"kind":"Name","value":"totalOwnedStreamsFavorites"}}]}}]}},...CommonUserFieldsFragmentDoc.definitions]} as unknown as DocumentNode<ExtraUserDataQuery, ExtraUserDataQueryVariables>;
|
||||
export const UserSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"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":"archived"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userSearch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"archived"},"value":{"kind":"Variable","name":{"kind":"Name","value":"archived"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserFields"}}]}}]}}]}},...LimitedUserFieldsFragmentDoc.definitions]} as unknown as DocumentNode<UserSearchQuery, UserSearchQueryVariables>;
|
||||
export const IsLoggedInDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"IsLoggedIn"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<IsLoggedInQuery, IsLoggedInQueryVariables>;
|
||||
export const AdminUsersListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AdminUsersList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"adminUsers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}},{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"registeredUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"suuid"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"profiles"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"authorizedApps"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<AdminUsersListQuery, AdminUsersListQueryVariables>;
|
||||
export const AdminUsersListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AdminUsersList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"adminUsers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}},{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"registeredUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"profiles"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"authorizedApps"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<AdminUsersListQuery, AdminUsersListQueryVariables>;
|
||||
export const UserTimelineDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserTimeline"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"DateTime"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"timeline"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}}],"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":"ActivityMainFields"}}]}}]}}]}}]}},...ActivityMainFieldsFragmentDoc.definitions]} as unknown as DocumentNode<UserTimelineQuery, UserTimelineQueryVariables>;
|
||||
export const UserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"User"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"profiles"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"suuid"}}]}}]}}]} as unknown as DocumentNode<UserQuery, UserQueryVariables>;
|
||||
export const ValidatePasswordStrengthDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ValidatePasswordStrength"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pwd"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userPwdStrength"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pwd"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pwd"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"score"}},{"kind":"Field","name":{"kind":"Name","value":"feedback"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"warning"}},{"kind":"Field","name":{"kind":"Name","value":"suggestions"}}]}}]}}]}}]} as unknown as DocumentNode<ValidatePasswordStrengthQuery, ValidatePasswordStrengthQueryVariables>;
|
||||
export const EmailVerificationBannerStateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"EmailVerificationBannerState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"hasPendingVerification"}}]}}]}}]} as unknown as DocumentNode<EmailVerificationBannerStateQuery, EmailVerificationBannerStateQueryVariables>;
|
||||
export const RequestVerificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RequestVerification"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"requestVerification"}}]}}]} as unknown as DocumentNode<RequestVerificationMutation, RequestVerificationMutationVariables>;
|
||||
export const UserByIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserById"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"profiles"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]} as unknown as DocumentNode<UserByIdQuery, UserByIdQueryVariables>;
|
||||
export const UserProfileDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserProfile"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}}]}}]}}]} as unknown as DocumentNode<UserProfileQuery, UserProfileQueryVariables>;
|
||||
export const WebhookDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"webhook"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"webhookId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"webhooks"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"webhookId"}}}],"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":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"triggers"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"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":"statusInfo"}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<WebhookQuery, WebhookQueryVariables>;
|
||||
export const WebhooksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"webhooks"},"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":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"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":"webhooks"},"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":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"triggers"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"50"}}],"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":"statusInfo"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdate"}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<WebhooksQuery, WebhooksQueryVariables>;
|
||||
@@ -1,3 +1,4 @@
|
||||
import { fullStreamAccessRequestFieldsFragment } from '@/graphql/fragments/accessRequests'
|
||||
import { activityMainFieldsFragment } from '@/graphql/fragments/activity'
|
||||
import {
|
||||
limitedUserFieldsFragment,
|
||||
@@ -70,10 +71,14 @@ export const streamWithCollaboratorsQuery = gql`
|
||||
...LimitedUserFields
|
||||
}
|
||||
}
|
||||
pendingAccessRequests {
|
||||
...FullStreamAccessRequestFields
|
||||
}
|
||||
}
|
||||
}
|
||||
${limitedUserFieldsFragment}
|
||||
${streamCollaboratorFieldsFragment}
|
||||
${fullStreamAccessRequestFieldsFragment}
|
||||
`
|
||||
|
||||
export const streamWithActivityQuery = gql`
|
||||
|
||||
@@ -6,16 +6,15 @@ import { gql } from '@apollo/client/core'
|
||||
export const commonUserFieldsFragment = gql`
|
||||
fragment CommonUserFields on User {
|
||||
id
|
||||
suuid
|
||||
email
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
hasPendingVerification
|
||||
profiles
|
||||
role
|
||||
suuid
|
||||
streams {
|
||||
totalCount
|
||||
}
|
||||
@@ -115,7 +114,6 @@ export const adminUsersListQuery = gql`
|
||||
id
|
||||
registeredUser {
|
||||
id
|
||||
suuid
|
||||
email
|
||||
name
|
||||
bio
|
||||
@@ -157,3 +155,32 @@ export const userTimelineQuery = gql`
|
||||
|
||||
${activityMainFieldsFragment}
|
||||
`
|
||||
|
||||
export const validatePasswordStrengthQuery = gql`
|
||||
query ValidatePasswordStrength($pwd: String!) {
|
||||
userPwdStrength(pwd: $pwd) {
|
||||
score
|
||||
feedback {
|
||||
warning
|
||||
suggestions
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const emailVerificationBannerStateQuery = gql`
|
||||
query EmailVerificationBannerState {
|
||||
user {
|
||||
id
|
||||
email
|
||||
verified
|
||||
hasPendingVerification
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const requestVerificationMutation = gql`
|
||||
mutation RequestVerification {
|
||||
requestVerification
|
||||
}
|
||||
`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
query User($id: String!) {
|
||||
query UserById($id: String!) {
|
||||
user(id: $id) {
|
||||
id
|
||||
email
|
||||
@@ -9,6 +9,5 @@ query User($id: String!) {
|
||||
verified
|
||||
profiles
|
||||
role
|
||||
suuid
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Speckle role constants
|
||||
*/
|
||||
export const Roles = Object.freeze({
|
||||
Stream: {
|
||||
Owner: 'stream:owner',
|
||||
Contributor: 'stream:contributor',
|
||||
Reviewer: 'stream:reviewer'
|
||||
},
|
||||
Server: {
|
||||
Admin: 'server:admin',
|
||||
User: 'server:user',
|
||||
ArchivedUser: 'server:archived-user'
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Keys for values stored in localStorage
|
||||
*/
|
||||
export const LocalStorageKeys = Object.freeze({
|
||||
AuthToken: 'AuthToken',
|
||||
RefreshToken: 'RefreshToken',
|
||||
Uuid: 'uuid',
|
||||
ShouldRedirectTo: 'shouldRedirectTo'
|
||||
})
|
||||
@@ -0,0 +1,46 @@
|
||||
import { StreamRole } from '@/graphql/generated/graphql'
|
||||
|
||||
/**
|
||||
* Speckle role constants
|
||||
*/
|
||||
export const Roles = Object.freeze(<const>{
|
||||
Stream: {
|
||||
Owner: 'stream:owner',
|
||||
Contributor: 'stream:contributor',
|
||||
Reviewer: 'stream:reviewer'
|
||||
},
|
||||
Server: {
|
||||
Admin: 'server:admin',
|
||||
User: 'server:user',
|
||||
ArchivedUser: 'server:archived-user'
|
||||
}
|
||||
})
|
||||
|
||||
export type ServerRoles = typeof Roles['Server'][keyof typeof Roles['Server']]
|
||||
export type StreamRoles = typeof Roles['Stream'][keyof typeof Roles['Stream']]
|
||||
|
||||
/**
|
||||
* Keys for values stored in localStorage
|
||||
*/
|
||||
export const LocalStorageKeys = Object.freeze({
|
||||
AuthToken: 'AuthToken',
|
||||
RefreshToken: 'RefreshToken',
|
||||
Uuid: 'uuid',
|
||||
ShouldRedirectTo: 'shouldRedirectTo'
|
||||
})
|
||||
|
||||
/**
|
||||
* Our GQL schema has a StreamRoles enum that unfortunately can't have the same exact values as our roles constants, because
|
||||
* we can't use colons (:) there. So you can use this function to map from our constant value to the GQL one.
|
||||
*/
|
||||
export function streamRoleToGraphQLEnum(role: StreamRoles): StreamRole {
|
||||
switch (role) {
|
||||
case Roles.Stream.Owner:
|
||||
return StreamRole.StreamOwner
|
||||
case Roles.Stream.Reviewer:
|
||||
return StreamRole.StreamReviewer
|
||||
case Roles.Stream.Contributor:
|
||||
default:
|
||||
return StreamRole.StreamContributor
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ReactiveVar } from '@apollo/client/core'
|
||||
import { isUndefined } from 'lodash'
|
||||
import Vue, { VueConstructor } from 'vue'
|
||||
|
||||
export type Nullable<T> = T | null
|
||||
@@ -7,6 +8,16 @@ export type Optional<T> = T | undefined
|
||||
|
||||
export type MaybeFalsy<T> = T | null | undefined | false | '' | 0
|
||||
|
||||
export type MaybeNullOrUndefined<T> = T | null | undefined
|
||||
|
||||
export type MaybeAsync<T> = T | Promise<T>
|
||||
|
||||
/**
|
||||
* In TS undefined !== void, so use this type guard to check for both
|
||||
*/
|
||||
export const isUndefinedOrVoid = (val: unknown): val is void | undefined =>
|
||||
isUndefined(val)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type GetReactiveVarType<V extends ReactiveVar<any>> = V extends ReactiveVar<
|
||||
infer T
|
||||
|
||||
@@ -17,5 +17,6 @@ export type VFormInstance = CombinedVueInstance<
|
||||
validate(): boolean
|
||||
},
|
||||
unknown,
|
||||
unknown,
|
||||
unknown
|
||||
>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { DefaultApolloClient } from '@vue/apollo-composable'
|
||||
import { createProvider, installVueApollo } from '@/config/apolloConfig'
|
||||
import {
|
||||
checkAccessCodeAndGetTokens,
|
||||
prefetchUserAndSetSuuid
|
||||
prefetchUserAndSetID
|
||||
} from '@/plugins/authHelpers'
|
||||
|
||||
import router from '@/main/router/index'
|
||||
@@ -58,7 +58,7 @@ installVueApollo(apolloProvider)
|
||||
|
||||
// TODO: Sort out error handling here, if something goes wrong it just goes into an infinite loop
|
||||
if (AuthToken) {
|
||||
prefetchUserAndSetSuuid(apolloProvider.defaultClient)
|
||||
prefetchUserAndSetID(apolloProvider.defaultClient)
|
||||
.then(() => {
|
||||
postAuthInit()
|
||||
})
|
||||
@@ -72,7 +72,7 @@ if (AuthToken) {
|
||||
} else {
|
||||
checkAccessCodeAndGetTokens()
|
||||
.then(() => {
|
||||
return prefetchUserAndSetSuuid(apolloProvider.defaultClient)
|
||||
return prefetchUserAndSetID(apolloProvider.defaultClient)
|
||||
})
|
||||
.then(() => {
|
||||
postAuthInit()
|
||||
|
||||
@@ -46,6 +46,12 @@
|
||||
{{ val ? 'public' : 'private' }}
|
||||
</i>
|
||||
</template>
|
||||
<template v-else-if="key === UpdatedInfoKeys.IsDiscoverable">
|
||||
📺 Stream is now
|
||||
<i>
|
||||
{{ val ? 'discoverable' : 'not discoverable' }}
|
||||
</i>
|
||||
</template>
|
||||
</p>
|
||||
</template>
|
||||
</template>
|
||||
@@ -61,7 +67,8 @@ const UpdatedInfoKeys = {
|
||||
Name: 'name',
|
||||
Description: 'description',
|
||||
Message: 'message',
|
||||
IsPublic: 'isPublic'
|
||||
IsPublic: 'isPublic',
|
||||
IsDiscoverable: 'isDiscoverable'
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
block
|
||||
:color="s.color"
|
||||
:href="`${s.url}?appId=${appId}&challenge=${challenge}${
|
||||
suuid ? '&suuid=' + suuid : ''
|
||||
}${token ? '&token=' + token : ''}`"
|
||||
token ? '&token=' + token : ''
|
||||
}`"
|
||||
>
|
||||
<v-icon small class="mr-5">{{ s.icon }}</v-icon>
|
||||
{{ s.name }}
|
||||
@@ -45,10 +45,6 @@ export default {
|
||||
challenge: {
|
||||
type: String,
|
||||
default: () => null
|
||||
},
|
||||
suuid: {
|
||||
type: String,
|
||||
default: () => null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="text-caption d-flex flex-column">
|
||||
<a
|
||||
v-for="attachment in attachments"
|
||||
:key="attachment.url"
|
||||
:key="attachment.id"
|
||||
v-tooltip="attachment.fileName"
|
||||
href="javascript:;"
|
||||
:class="`my-1 ${primary ? '' : 'blue--text'}`"
|
||||
@@ -34,6 +34,7 @@
|
||||
import { BlobMetadata } from '@/graphql/generated/graphql'
|
||||
import Vue, { PropType } from 'vue'
|
||||
import CommentThreadAttachmentPreview from '@/main/components/comments/CommentThreadAttachmentPreview.vue'
|
||||
import { Nullable } from '@/helpers/typeHelpers'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'CommentThreadReplyAttachments',
|
||||
@@ -53,7 +54,7 @@ export default Vue.extend({
|
||||
data: () => {
|
||||
return {
|
||||
showAttachmentPreview: false,
|
||||
selectedAttachment: null
|
||||
selectedAttachment: null as Nullable<BlobMetadata>
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -12,47 +12,16 @@
|
||||
</v-snackbar>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Nullable } from '@/helpers/typeHelpers'
|
||||
import Vue from 'vue'
|
||||
import {
|
||||
GlobalEvents,
|
||||
NotificationEventPayload,
|
||||
ToastNotificationType
|
||||
} from '@/main/lib/core/helpers/eventHubHelper'
|
||||
import { defineComponent } from 'vue'
|
||||
import { setupGlobalToast } from '@/main/lib/core/composables/notifications'
|
||||
|
||||
export default Vue.extend({
|
||||
export default defineComponent({
|
||||
name: 'GlobalToast',
|
||||
data() {
|
||||
setup() {
|
||||
const globalToastData = setupGlobalToast()
|
||||
return {
|
||||
snack: false,
|
||||
text: null as Nullable<string>,
|
||||
actionName: null as Nullable<string>,
|
||||
to: null as Nullable<string>,
|
||||
type: 'primary' as ToastNotificationType
|
||||
...globalToastData
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
color(): ToastNotificationType {
|
||||
return this.type || 'primary'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
snack(newVal) {
|
||||
if (!newVal) {
|
||||
this.text = null
|
||||
this.actionName = null
|
||||
this.to = null
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventHub.$on(GlobalEvents.Notification, (args: NotificationEventPayload) => {
|
||||
this.snack = true
|
||||
this.text = args.text
|
||||
this.actionName = args.action ? args.action.name : null
|
||||
this.to = args.action ? args.action.to : null
|
||||
this.type = args.type || 'primary'
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
link
|
||||
class="primary mb-4"
|
||||
dark
|
||||
@click="downloadManager"
|
||||
href="https://releases.speckle.systems/"
|
||||
target="_blank"
|
||||
>
|
||||
<v-list-item-icon>
|
||||
<v-icon class="pt-4">mdi-download</v-icon>
|
||||
@@ -183,23 +184,6 @@ export default {
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.checkAccountTimer)
|
||||
},
|
||||
methods: {
|
||||
async downloadManager() {
|
||||
this.$mixpanel.track('Manager Download', {
|
||||
type: 'action'
|
||||
})
|
||||
|
||||
const url = `https://releases.speckle.dev/manager/SpeckleManager Setup.exe`
|
||||
|
||||
const a = document.createElement('a')
|
||||
document.body.appendChild(a)
|
||||
a.style = 'display: none'
|
||||
a.href = url
|
||||
a.download = 'SpeckleManager Setup.exe'
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -49,7 +49,7 @@ export default {
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: () => ''
|
||||
},
|
||||
color: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -69,18 +69,27 @@
|
||||
></user-avatar-icon>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import userByIdQuery from '@/graphql/userById.gql'
|
||||
import UserAvatarIcon from '@/main/components/common/UserAvatarIcon'
|
||||
<script lang="ts">
|
||||
import UserAvatarIcon from '@/main/components/common/UserAvatarIcon.vue'
|
||||
import { AppLocalStorage } from '@/utils/localStorage'
|
||||
import { LocalStorageKeys } from '@/helpers/mainConstants'
|
||||
import { useIsLoggedIn } from '@/main/lib/core/composables/core'
|
||||
import { computed, defineComponent, PropType } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { UserByIdDocument } from '@/graphql/generated/graphql'
|
||||
import { MaybeNullOrUndefined } from '@/helpers/typeHelpers'
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
components: { UserAvatarIcon },
|
||||
props: {
|
||||
avatar: { type: String, default: null },
|
||||
name: { type: String, default: null },
|
||||
avatar: {
|
||||
type: String as PropType<MaybeNullOrUndefined<string>>,
|
||||
default: null
|
||||
},
|
||||
name: {
|
||||
type: String as PropType<MaybeNullOrUndefined<string>>,
|
||||
default: null
|
||||
},
|
||||
showHover: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
@@ -102,30 +111,21 @@ export default {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
setup(props) {
|
||||
const { isLoggedIn } = useIsLoggedIn()
|
||||
return { isLoggedIn }
|
||||
const { result: userByIdResult } = useQuery(
|
||||
UserByIdDocument,
|
||||
() => ({ id: props.id }),
|
||||
() => ({ enabled: isLoggedIn.value })
|
||||
)
|
||||
const userById = computed(() => userByIdResult.value?.user)
|
||||
|
||||
return { isLoggedIn, userById }
|
||||
},
|
||||
computed: {
|
||||
isSelf() {
|
||||
isSelf(): boolean {
|
||||
return this.id === AppLocalStorage.get(LocalStorageKeys.Uuid)
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
userById: {
|
||||
query: userByIdQuery,
|
||||
variables() {
|
||||
return {
|
||||
id: this.id
|
||||
}
|
||||
},
|
||||
skip() {
|
||||
return !this.isLoggedIn
|
||||
},
|
||||
update: (data) => {
|
||||
return data.user
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -17,6 +17,9 @@ export default {
|
||||
required: true
|
||||
},
|
||||
avatar: {
|
||||
/**
|
||||
* @type {import('vue').PropType<string | null | undefined>}
|
||||
*/
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<v-alert rounded="lg" elevation="4" dense class="mb-0"><slot /></v-alert>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<v-list rounded class="transparent">
|
||||
<slot />
|
||||
</v-list>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
export default defineComponent({
|
||||
name: 'RoundedButtonList'
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.v-list-item:not(:last-of-type)) {
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
</style>
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<v-list-item
|
||||
link
|
||||
:href="to"
|
||||
:class="itemClass"
|
||||
:dark="isPrimary"
|
||||
@click="$emit('click', $event)"
|
||||
>
|
||||
<v-list-item-icon v-if="icon">
|
||||
<v-icon>{{ icon }}</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
<slot />
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle class="caption">
|
||||
<slot name="subtitle" />
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Optional } from '@/helpers/typeHelpers'
|
||||
import { useVuetify } from '@/main/lib/core/composables/core'
|
||||
import { computed, defineComponent, PropType } from 'vue'
|
||||
|
||||
type ItemType = 'primary' | 'secondary'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RoundedButtonListItem',
|
||||
props: {
|
||||
type: {
|
||||
type: String as PropType<ItemType>,
|
||||
default: () => 'primary'
|
||||
},
|
||||
icon: {
|
||||
type: String as PropType<Optional<string>>,
|
||||
default: () => undefined
|
||||
},
|
||||
to: {
|
||||
type: String as PropType<Optional<string>>,
|
||||
default: () => undefined
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const vuetify = useVuetify()
|
||||
const itemClass = computed(() => {
|
||||
const classes = ['']
|
||||
switch (props.type) {
|
||||
case 'primary':
|
||||
classes.push('primary')
|
||||
break
|
||||
case 'secondary':
|
||||
classes.push(`grey ${vuetify.theme.dark ? 'darken-4' : 'lighten-4'}`)
|
||||
break
|
||||
}
|
||||
|
||||
return classes
|
||||
})
|
||||
|
||||
const isPrimary = computed(() => props.type === 'primary')
|
||||
|
||||
return {
|
||||
itemClass,
|
||||
isPrimary
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -82,6 +82,7 @@ import { UserTimelineDocument } from '@/graphql/generated/graphql'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { computed } from 'vue'
|
||||
import { AppLocalStorage } from '@/utils/localStorage'
|
||||
import { SKIPPABLE_ACTION_TYPES } from '@/main/lib/feed/helpers/activityStream'
|
||||
|
||||
export default {
|
||||
name: 'FeedTimeline',
|
||||
@@ -112,7 +113,7 @@ export default {
|
||||
const data = timelineResult.value
|
||||
if (!data) return []
|
||||
|
||||
const skippableActionTypes = ['stream_invite_sent', 'stream_invite_declined']
|
||||
const skippableActionTypes = SKIPPABLE_ACTION_TYPES
|
||||
const groupedTimeline = data.user.timeline.items.reduce(function (prev, curr) {
|
||||
if (skippableActionTypes.includes(curr.actionType)) {
|
||||
return prev
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
<template>
|
||||
<v-navigation-drawer app right fixed class="transparent overflow-auto" floating>
|
||||
<v-card
|
||||
rounded="lg"
|
||||
style="overflow: hidden"
|
||||
class="transparent elevation-0 pl-1 pr-3 pb-4"
|
||||
>
|
||||
<v-toolbar class="mt-3" rounded="lg" dense>
|
||||
<div class="latest-blogposts">
|
||||
<v-card rounded="lg" style="overflow: hidden" class="transparent elevation-0 pb-4">
|
||||
<v-toolbar class="mt-0" rounded="lg" dense>
|
||||
<v-toolbar-title class="body-2 font-weight-bold">
|
||||
<a
|
||||
class="text-decoration-none"
|
||||
@@ -113,7 +109,7 @@
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-card>
|
||||
</v-navigation-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import GhostContentAPI from '@tryghost/content-api'
|
||||
@@ -147,3 +143,8 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.latest-blogposts {
|
||||
width: 240px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<basic-panel class="stream-access-request-banner">
|
||||
<div class="d-flex flex-column flex-md-row align-center">
|
||||
<div class="flex-grow-1 d-flex align-center">
|
||||
<user-avatar
|
||||
:id="requester.id"
|
||||
:name="requester.name"
|
||||
:avatar="requester.avatar"
|
||||
:size="25"
|
||||
class="mr-1"
|
||||
/>
|
||||
<div>
|
||||
<strong>{{ requester.name }}</strong>
|
||||
has requested access to this stream
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-center mt-2 mt-md-0">
|
||||
<v-select
|
||||
v-model="selectedRole"
|
||||
class="mr-2"
|
||||
filled
|
||||
rounded
|
||||
dense
|
||||
hide-details
|
||||
:items="availableRolesSelectItems"
|
||||
style="max-width: 200px"
|
||||
></v-select>
|
||||
<v-btn
|
||||
small
|
||||
color="primary"
|
||||
class="mr-2 flex-grow-1 flex-md-grow-0"
|
||||
@click="approveRequest"
|
||||
>
|
||||
Add
|
||||
</v-btn>
|
||||
<v-btn
|
||||
small
|
||||
color="error"
|
||||
dark
|
||||
class="flex-grow-1 flex-md-grow-0"
|
||||
@click="declineRequest"
|
||||
>
|
||||
Ignore
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</basic-panel>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, PropType, ref } from 'vue'
|
||||
import { Get } from 'type-fest'
|
||||
import {
|
||||
StreamRole,
|
||||
StreamWithCollaboratorsQuery,
|
||||
UseStreamAccessRequestDocument,
|
||||
StreamPendingAccessRequestsFragment
|
||||
} from '@/graphql/generated/graphql'
|
||||
import BasicPanel from '@/main/components/common/layout/BasicPanel.vue'
|
||||
import UserAvatar from '@/main/components/common/UserAvatar.vue'
|
||||
import { Roles, streamRoleToGraphQLEnum } from '@/helpers/mainConstants'
|
||||
import { useApolloClient } from '@vue/apollo-composable'
|
||||
import {
|
||||
convertThrowIntoFetchResult,
|
||||
getFirstErrorMessage,
|
||||
updateCacheByFilter,
|
||||
getCacheId
|
||||
} from '@/main/lib/common/apollo/helpers/apolloOperationHelper'
|
||||
import { useEventHub } from '@/main/lib/core/composables/core'
|
||||
import { StreamEvents } from '@/main/lib/core/helpers/eventHubHelper'
|
||||
import { useGlobalToast } from '@/main/lib/core/composables/notifications'
|
||||
import { streamPendingAccessRequestsFragment } from '@/graphql/fragments/streams'
|
||||
|
||||
type StreamAccessRequest = NonNullable<
|
||||
Get<StreamWithCollaboratorsQuery, 'stream.pendingAccessRequests.0'>
|
||||
>
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* Request from the StreamWithCollaborators query
|
||||
*/
|
||||
request: {
|
||||
type: Object as PropType<StreamAccessRequest>,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const availableRolesSelectItems: { text: string; value: StreamRole }[] = Object.entries(
|
||||
Roles.Stream
|
||||
).map(([text, value]) => ({
|
||||
text,
|
||||
value: streamRoleToGraphQLEnum(value)
|
||||
}))
|
||||
const selectedRole = ref<StreamRole>(streamRoleToGraphQLEnum(Roles.Stream.Contributor))
|
||||
|
||||
const requester = computed(() => props.request.requester)
|
||||
|
||||
const apollo = useApolloClient().client
|
||||
const eventHub = useEventHub()
|
||||
const { triggerNotification } = useGlobalToast()
|
||||
|
||||
/**
|
||||
* Accept or decline the access request
|
||||
*/
|
||||
const processRequest = async (accept: boolean) => {
|
||||
const reqId = props.request.id
|
||||
|
||||
const res = await apollo
|
||||
.mutate({
|
||||
mutation: UseStreamAccessRequestDocument,
|
||||
variables: {
|
||||
requestId: reqId,
|
||||
accept,
|
||||
role: accept ? selectedRole.value : undefined
|
||||
},
|
||||
update: (cache, res) => {
|
||||
const reqId = props.request.id
|
||||
const streamId = props.request.streamId
|
||||
|
||||
const { data } = res
|
||||
if (!data?.streamAccessRequestUse) return
|
||||
|
||||
// Remove request from cache
|
||||
updateCacheByFilter<StreamPendingAccessRequestsFragment>(
|
||||
cache,
|
||||
{
|
||||
fragment: {
|
||||
id: getCacheId('Stream', streamId),
|
||||
// (not using typed doc, cause of nested fragments which don't get converted to typed doc correctly)
|
||||
fragment: streamPendingAccessRequestsFragment,
|
||||
fragmentName: 'StreamPendingAccessRequests'
|
||||
}
|
||||
},
|
||||
(data) => {
|
||||
if (!data.pendingAccessRequests?.length) return
|
||||
return {
|
||||
...data,
|
||||
pendingAccessRequests: data.pendingAccessRequests.filter(
|
||||
(r) => r.id !== reqId
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
|
||||
const { data, errors } = res
|
||||
if (data?.streamAccessRequestUse) {
|
||||
triggerNotification({
|
||||
text: accept ? 'Access request approved' : 'Access request declined'
|
||||
})
|
||||
} else {
|
||||
triggerNotification({
|
||||
type: 'error',
|
||||
text: getFirstErrorMessage(errors)
|
||||
})
|
||||
}
|
||||
|
||||
if (accept) {
|
||||
// reload stream collaborators
|
||||
eventHub.$emit(StreamEvents.RefetchCollaborators)
|
||||
}
|
||||
}
|
||||
|
||||
const declineRequest = async () => {
|
||||
await processRequest(false)
|
||||
}
|
||||
const approveRequest = async () => {
|
||||
await processRequest(true)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.stream-access-request-banner:not(:last-child) {
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -41,6 +41,7 @@ import { StreamWithActivityDocument } from '@/graphql/generated/graphql'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { useRoute } from '@/main/lib/core/composables/router'
|
||||
import { computed } from 'vue'
|
||||
import { SKIPPABLE_ACTION_TYPES } from '@/main/lib/feed/helpers/activityStream'
|
||||
|
||||
export default {
|
||||
name: 'StreamActivity',
|
||||
@@ -61,7 +62,7 @@ export default {
|
||||
}))
|
||||
const stream = computed(() => result.value?.stream || null)
|
||||
|
||||
const skippableActionTypes = ['stream_invite_sent', 'stream_invite_declined']
|
||||
const skippableActionTypes = SKIPPABLE_ACTION_TYPES
|
||||
const groupedActivity = computed(() =>
|
||||
(stream.value?.activity?.items || []).reduce(function (prev, curr) {
|
||||
if (skippableActionTypes.includes(curr.actionType)) {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="d-flex flex-column">
|
||||
<v-switch
|
||||
v-model="isPublicModel"
|
||||
inset
|
||||
:label="isPublicModel ? 'Link Sharing On' : 'Link Sharing Off'"
|
||||
:hint="
|
||||
isPublicModel
|
||||
? 'Anyone with the link can view this stream. It is also visible on your profile page. Only collaborators can push data to it.'
|
||||
: 'Only collaborators can access this stream.'
|
||||
"
|
||||
persistent-hint
|
||||
:disabled="disabled"
|
||||
class="visibility-toggle"
|
||||
/>
|
||||
<v-switch
|
||||
v-model="isDiscoverableModel"
|
||||
inset
|
||||
:label="isDiscoverableModel ? 'Discoverable' : 'Not Discoverable'"
|
||||
:hint="
|
||||
isDiscoverableModel
|
||||
? 'This stream can be found on public stream discovery pages'
|
||||
: 'This stream is not shown on any public stream discovery pages'
|
||||
"
|
||||
persistent-hint
|
||||
:disabled="disabled || !isPublicModel"
|
||||
class="visibility-toggle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import Vue, { computed } from 'vue'
|
||||
export default Vue.extend({
|
||||
name: 'StreamVisibilityToggle',
|
||||
props: {
|
||||
isPublic: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
isDiscoverable: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const isPublicModel = computed({
|
||||
get: () => props.isPublic,
|
||||
set: (val) => {
|
||||
emit('update:isPublic', val)
|
||||
if (!val) {
|
||||
isDiscoverableModel.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const isDiscoverableModel = computed({
|
||||
get: () => props.isDiscoverable && isPublicModel.value,
|
||||
set: (val) => {
|
||||
if (!isPublicModel.value) {
|
||||
val = false
|
||||
}
|
||||
|
||||
emit('update:isDiscoverable', val)
|
||||
}
|
||||
})
|
||||
|
||||
return { isPublicModel, isDiscoverableModel }
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.visibility-toggle {
|
||||
// incase hint breaks up in 2 lines
|
||||
min-height: 60px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<transition name="component-fade" mode="out-in">
|
||||
<transition v-if="shouldShowBanner" name="component-fade" mode="out-in">
|
||||
<v-alert
|
||||
v-if="!success & !errors"
|
||||
v-if="!success && !errors"
|
||||
type="warning"
|
||||
dismissible
|
||||
rounded="lg"
|
||||
@@ -10,16 +10,16 @@
|
||||
dense
|
||||
>
|
||||
<v-row align="center">
|
||||
<v-col class="grow">Your email {{ user.email }} is not verified.</v-col>
|
||||
<v-col class="grow">{{ verifyBannerText }}</v-col>
|
||||
<v-col class="shrink">
|
||||
<v-btn plain small :loading="loading" @click="requestVerification">
|
||||
Send verification
|
||||
{{ verifyBannerCtaText }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-alert>
|
||||
<v-alert
|
||||
v-if="success & !errors"
|
||||
v-if="success && !errors"
|
||||
type="success"
|
||||
color="primary"
|
||||
dismissible
|
||||
@@ -28,7 +28,7 @@
|
||||
height="44"
|
||||
dense
|
||||
>
|
||||
Verification email sent, please check you inbox.
|
||||
Verification e-mail sent, please check you inbox.
|
||||
</v-alert>
|
||||
<v-alert
|
||||
v-if="errors"
|
||||
@@ -39,18 +39,49 @@
|
||||
elevation="8"
|
||||
dense
|
||||
>
|
||||
Email verification failed.{{ errorMessage ? ` Reason: ${errorMessage}` : '' }}
|
||||
E-mail verification failed.{{ errorMessage ? ` Reason: ${errorMessage}` : '' }}
|
||||
</v-alert>
|
||||
</transition>
|
||||
</template>
|
||||
<script>
|
||||
import { AppLocalStorage } from '@/utils/localStorage'
|
||||
<script lang="ts">
|
||||
import {
|
||||
EmailVerificationBannerStateDocument,
|
||||
RequestVerificationDocument
|
||||
} from '@/graphql/generated/graphql'
|
||||
import { Nullable } from '@/helpers/typeHelpers'
|
||||
import { convertThrowIntoFetchResult } from '@/main/lib/common/apollo/helpers/apolloOperationHelper'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { computed, defineComponent } from 'vue'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
default: () => null
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { result } = useQuery(EmailVerificationBannerStateDocument)
|
||||
|
||||
const user = computed(() => result.value?.user || null)
|
||||
const shouldShowBanner = computed(() => {
|
||||
if (!user.value) return false
|
||||
if (user.value.verified) return false
|
||||
|
||||
return true
|
||||
})
|
||||
const hasPendingVerification = computed(() => !!user.value?.hasPendingVerification)
|
||||
|
||||
const verifyBannerText = computed(() =>
|
||||
hasPendingVerification.value
|
||||
? `Please check your inbox (${user.value?.email}) for the verification e-mail`
|
||||
: `Please verify your e-mail address (${user.value?.email})`
|
||||
)
|
||||
|
||||
const verifyBannerCtaText = computed(() =>
|
||||
hasPendingVerification.value ? `Re-send verification` : `Send verification`
|
||||
)
|
||||
|
||||
return {
|
||||
user,
|
||||
shouldShowBanner,
|
||||
hasPendingVerification,
|
||||
verifyBannerText,
|
||||
verifyBannerCtaText
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -58,30 +89,44 @@ export default {
|
||||
errors: false,
|
||||
success: false,
|
||||
loading: false,
|
||||
errorMessage: null
|
||||
errorMessage: null as Nullable<string>
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async requestVerification() {
|
||||
this.loading = true
|
||||
const res = await fetch(`/auth/emailverification/request`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: AppLocalStorage.get('AuthToken')
|
||||
},
|
||||
body: JSON.stringify({ email: this.user.email })
|
||||
})
|
||||
if (res.status !== 200) {
|
||||
this.errors = true
|
||||
this.errorMessage = await res.text()
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
const user = this.user
|
||||
if (!user) return
|
||||
|
||||
this.success = true
|
||||
this.loading = false
|
||||
this.loading = true
|
||||
const { data, errors } = await this.$apollo
|
||||
.mutate({
|
||||
mutation: RequestVerificationDocument,
|
||||
update: (cache, { data }) => {
|
||||
const isSuccess = !!data?.requestVerification
|
||||
if (!isSuccess) return
|
||||
|
||||
// Switch hasPendingVerification to true
|
||||
cache.modify({
|
||||
id: cache.identify(user),
|
||||
fields: {
|
||||
hasPendingVerification: () => true
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
.finally(() => (this.loading = false))
|
||||
|
||||
if (errors?.length || !data?.requestVerification) {
|
||||
const errMsg = errors?.[0].message || 'An unexpected issue occurred!'
|
||||
|
||||
this.errors = true
|
||||
this.errorMessage = errMsg
|
||||
this.loading = false
|
||||
} else {
|
||||
this.success = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -64,8 +64,6 @@
|
||||
<span v-if="isSelf" class="caption">
|
||||
id:
|
||||
<code>{{ user.id }}</code>
|
||||
, suuid:
|
||||
<code>{{ user.suuid }}</code>
|
||||
</span>
|
||||
<br />
|
||||
</v-col>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<v-alert v-show="error" dismissible type="error">
|
||||
{{ error }}
|
||||
</v-alert>
|
||||
<v-form ref="form" v-model="valid" lazy-validation>
|
||||
<v-form ref="form" v-model="valid" lazy-validation @submit.prevent="updateBranch">
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
v-model="editableBranch.name"
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
:disabled="isLoading"
|
||||
:rules="nameRules"
|
||||
validate-on-blur
|
||||
autofocus
|
||||
@@ -25,27 +26,22 @@
|
||||
/>
|
||||
<v-textarea
|
||||
v-model="description"
|
||||
:disabled="isLoading"
|
||||
rows="1"
|
||||
row-height="15"
|
||||
label="Description (optional)"
|
||||
/>
|
||||
<v-switch
|
||||
v-model="isPublic"
|
||||
v-tooltip="
|
||||
isPublic
|
||||
? `Anyone with the link can view this stream. It is also visible on your profile page. Only collaborators
|
||||
can edit it.`
|
||||
: `Only collaborators can access this stream.`
|
||||
"
|
||||
inset
|
||||
:label="`${isPublic ? 'Link Sharing On' : 'Link Sharing Off'}`"
|
||||
<stream-visibility-toggle
|
||||
:disabled="isLoading"
|
||||
:is-public.sync="isPublic"
|
||||
:is-discoverable.sync="isDiscoverable"
|
||||
/>
|
||||
|
||||
<p class="mt-5">
|
||||
<b>Invite collaborators</b>
|
||||
</p>
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
:disabled="isLoading"
|
||||
label="Search users..."
|
||||
placeholder="Search by name or by email"
|
||||
/>
|
||||
@@ -100,7 +96,7 @@
|
||||
color="primary"
|
||||
block
|
||||
large
|
||||
:disabled="!valid"
|
||||
:disabled="!valid || isLoading"
|
||||
:loading="isLoading"
|
||||
elevation="0"
|
||||
type="submit"
|
||||
@@ -115,10 +111,13 @@
|
||||
import { gql } from '@apollo/client/core'
|
||||
import { userSearchQuery } from '@/graphql/user'
|
||||
import { AppLocalStorage } from '@/utils/localStorage'
|
||||
import StreamVisibilityToggle from '@/main/components/stream/editor/StreamVisibilityToggle.vue'
|
||||
import UserAvatar from '@/main/components/common/UserAvatar.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UserAvatar: () => import('@/main/components/common/UserAvatar')
|
||||
UserAvatar,
|
||||
StreamVisibilityToggle
|
||||
},
|
||||
props: {
|
||||
open: {
|
||||
@@ -156,6 +155,7 @@ export default {
|
||||
search: null,
|
||||
nameRules: [],
|
||||
isPublic: true,
|
||||
isDiscoverable: false,
|
||||
collabs: [],
|
||||
isLoading: false,
|
||||
users: null
|
||||
@@ -210,6 +210,7 @@ export default {
|
||||
myStream: {
|
||||
name: this.name,
|
||||
isPublic: this.isPublic,
|
||||
isDiscoverable: this.isDiscoverable,
|
||||
description: this.description,
|
||||
withContributors: collabIds
|
||||
}
|
||||
@@ -223,8 +224,9 @@ export default {
|
||||
this.$eventHub.$emit('notification', {
|
||||
text: e.message
|
||||
})
|
||||
} finally {
|
||||
this.isLoading = false
|
||||
}
|
||||
this.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,7 @@
|
||||
</v-app-bar>
|
||||
<v-main class="background">
|
||||
<email-verification-banner
|
||||
v-if="!hideEmailBanner && user && !user.verified"
|
||||
:user="user"
|
||||
v-if="!hideEmailBanner"
|
||||
class="my-2 mx-4 email-banner"
|
||||
></email-verification-banner>
|
||||
<v-container fluid class="px-4">
|
||||
@@ -62,9 +61,9 @@
|
||||
</template>
|
||||
<script>
|
||||
import { gql } from '@apollo/client/core'
|
||||
import { mainUserDataQuery } from '@/graphql/user'
|
||||
import { useNavigationDrawerAutoResize } from '../lib/core/composables/dom'
|
||||
import { ref } from 'vue'
|
||||
import { useIsLoggedIn } from '../lib/core/composables/core'
|
||||
|
||||
export default {
|
||||
name: 'TheMain',
|
||||
@@ -78,12 +77,6 @@ export default {
|
||||
import('@/main/components/user/EmailVerificationBanner')
|
||||
},
|
||||
apollo: {
|
||||
user: {
|
||||
query: mainUserDataQuery,
|
||||
skip() {
|
||||
return !this.$loggedIn()
|
||||
}
|
||||
},
|
||||
$subscribe: {
|
||||
userStreamAdded: {
|
||||
query: gql`
|
||||
@@ -103,7 +96,7 @@ export default {
|
||||
})
|
||||
},
|
||||
skip() {
|
||||
return !this.user
|
||||
return !this.isLoggedIn
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,10 +108,13 @@ export default {
|
||||
drawerRef: navDrawer
|
||||
})
|
||||
|
||||
const { isLoggedIn } = useIsLoggedIn()
|
||||
|
||||
// drawer ref must be returned, for it to be filled
|
||||
return {
|
||||
navDrawer,
|
||||
navWidth
|
||||
navWidth,
|
||||
isLoggedIn
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -134,15 +130,37 @@ export default {
|
||||
this.hideEmailBanner = !!to.meta.hideEmailBanner
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
'$route.query.emailverifiedstatus': {
|
||||
handler(emailVerifiedStatus, oldStatus) {
|
||||
if (!oldStatus && emailVerifiedStatus === 'true') {
|
||||
this.$triggerNotification({
|
||||
text: '✉️ Email successfully verified!',
|
||||
type: 'success'
|
||||
})
|
||||
|
||||
this.cleanQuery()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
'$route.query.emailverifiederror': {
|
||||
handler(emailVerifiedError, oldError) {
|
||||
if (!oldError && emailVerifiedError) {
|
||||
this.$triggerNotification({
|
||||
text: `✉️ ${emailVerifiedError}`,
|
||||
type: 'error'
|
||||
})
|
||||
|
||||
this.cleanQuery()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.$route.query.emailverfiedstatus) {
|
||||
setTimeout(() => {
|
||||
this.$eventHub.$emit('notification', {
|
||||
text: '✉️ Email successfully verfied!'
|
||||
})
|
||||
}, 1000) // todo: ask fabian if there's a better way, feels icky
|
||||
methods: {
|
||||
cleanQuery() {
|
||||
this.$router.replace({ ...this.$router.currentRoute, query: '' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { ValidatePasswordStrengthDocument } from '@/graphql/generated/graphql'
|
||||
import { Nullable } from '@/helpers/typeHelpers'
|
||||
import { useApolloClient } from '@vue/apollo-composable'
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
export function useValidatablePasswordEntry() {
|
||||
const password = ref<Nullable<string>>(null)
|
||||
const passwordConfirmation = ref<Nullable<string>>(null)
|
||||
/**
|
||||
* Strength value from 1 to 100. Its PasswordStrengthCheckResults.score times 25.
|
||||
*/
|
||||
const passwordStrength = ref<Nullable<number>>(null)
|
||||
const passwordSuggestion = ref<Nullable<string>>(null)
|
||||
|
||||
const apollo = useApolloClient()
|
||||
|
||||
/**
|
||||
* Re-check password strength
|
||||
*/
|
||||
const updatePasswordStrength = async () => {
|
||||
if (!password.value) {
|
||||
passwordStrength.value = 1
|
||||
passwordSuggestion.value = null
|
||||
return
|
||||
}
|
||||
|
||||
const result = await apollo.client.query({
|
||||
query: ValidatePasswordStrengthDocument,
|
||||
variables: { pwd: password.value }
|
||||
})
|
||||
|
||||
passwordStrength.value = result.data.userPwdStrength.score * 25
|
||||
passwordSuggestion.value =
|
||||
result.data.userPwdStrength.feedback.suggestions[0] || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Do basic validation
|
||||
*/
|
||||
const validatePassword = () => {
|
||||
if (!password.value) {
|
||||
throw new Error('Password is empty')
|
||||
}
|
||||
|
||||
if (password.value !== passwordConfirmation.value) {
|
||||
throw new Error('Passwords do not match')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously validate that the password is strong enough
|
||||
*/
|
||||
const validatePasswordStrength = async () => {
|
||||
await updatePasswordStrength()
|
||||
if ((passwordStrength.value || 0) < 50) {
|
||||
throw new Error('Password too weak')
|
||||
}
|
||||
}
|
||||
|
||||
// Wipe old suggestion, if password is changed
|
||||
watch(password, () => {
|
||||
passwordStrength.value = 0
|
||||
passwordSuggestion.value = null
|
||||
})
|
||||
|
||||
return {
|
||||
password,
|
||||
passwordConfirmation,
|
||||
passwordStrength,
|
||||
passwordSuggestion,
|
||||
updatePasswordStrength,
|
||||
validatePassword,
|
||||
validatePasswordStrength
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,30 @@
|
||||
import { ApolloError, FetchResult } from '@apollo/client/core'
|
||||
import { isUndefinedOrVoid } from '@/helpers/typeHelpers'
|
||||
import {
|
||||
ApolloError,
|
||||
FetchResult,
|
||||
DataProxy,
|
||||
ApolloCache,
|
||||
defaultDataIdFromObject
|
||||
} from '@apollo/client/core'
|
||||
import { GraphQLError } from 'graphql'
|
||||
|
||||
/**
|
||||
* Get a cached object's identifier
|
||||
*/
|
||||
export function getCacheId(typeName: string, id: string) {
|
||||
const cachedId = defaultDataIdFromObject({
|
||||
__typename: typeName,
|
||||
id
|
||||
})
|
||||
if (!cachedId) throw new Error('Unable to build Apollo cache ID')
|
||||
|
||||
return cachedId
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an error thrown during $apollo.mutate() into a fetch result
|
||||
*/
|
||||
export function convertThrowIntoFetchResult(err: unknown): FetchResult {
|
||||
export function convertThrowIntoFetchResult(err: unknown): FetchResult<undefined> {
|
||||
let gqlErrors: readonly GraphQLError[]
|
||||
if (err instanceof ApolloError) {
|
||||
gqlErrors = err.graphQLErrors
|
||||
@@ -19,3 +39,87 @@ export function convertThrowIntoFetchResult(err: unknown): FetchResult {
|
||||
errors: gqlErrors
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first error message from a GQL errors array
|
||||
*/
|
||||
export function getFirstErrorMessage(
|
||||
errs: readonly GraphQLError[] | undefined | null,
|
||||
fallbackMessage = 'An unexpected issue occurred'
|
||||
): string {
|
||||
return errs?.[0]?.message || fallbackMessage
|
||||
}
|
||||
|
||||
/**
|
||||
* Find some cached Apollo data through a fragment/query and use the updater function
|
||||
* to return the replacement for the data that the fragment initially found
|
||||
* @returns Whether an update was made
|
||||
*/
|
||||
export function updateCacheByFilter<TData, TVariables = unknown>(
|
||||
cache: ApolloCache<unknown>,
|
||||
filter: {
|
||||
fragment?: DataProxy.Fragment<TVariables, TData>
|
||||
query?: DataProxy.Query<TVariables, TData>
|
||||
},
|
||||
/**
|
||||
* If returns undefined/void, then updating is essentially canceled. Be careful not to
|
||||
* mutate anything being passed into this function! E.g. if you want to mutate arrays,
|
||||
* create new arrays through slice()/filter() instead
|
||||
*/
|
||||
updater: (data: TData) => TData | undefined | void,
|
||||
options: Partial<{
|
||||
/**
|
||||
* Whether to suppress errors that occur when the fragment being queried
|
||||
* doesn't find anything
|
||||
* Default: true
|
||||
*/
|
||||
ignoreCacheErrors: boolean
|
||||
}> = {}
|
||||
): boolean {
|
||||
const { fragment, query } = filter
|
||||
const { ignoreCacheErrors = true } = options
|
||||
|
||||
if (!fragment && !query) {
|
||||
throw new Error(
|
||||
'Either fragment or query must be specified to be able to find cached data to update'
|
||||
)
|
||||
}
|
||||
|
||||
const readData = (): TData | null => {
|
||||
if (fragment) {
|
||||
return cache.readFragment(fragment)
|
||||
} else if (query) {
|
||||
return cache.readQuery(query)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const writeData = (data: TData): boolean => {
|
||||
if (fragment) {
|
||||
cache.writeFragment({ ...fragment, data })
|
||||
return true
|
||||
} else if (query) {
|
||||
cache.writeQuery({ ...query, data })
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const currentData = readData()
|
||||
if (!currentData) return false
|
||||
|
||||
const newData = updater(currentData)
|
||||
if (isUndefinedOrVoid(newData)) return false
|
||||
|
||||
return writeData(newData)
|
||||
} catch (e: unknown) {
|
||||
if (ignoreCacheErrors) {
|
||||
console.warn('Failed Apollo cache update', e)
|
||||
return false
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,3 +32,13 @@ export function useIsLoggedIn() {
|
||||
const isLoggedIn = computed(() => !!result.value?.user?.id)
|
||||
return { isLoggedIn }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Vuetify
|
||||
*/
|
||||
export function useVuetify() {
|
||||
const vm = getCurrentInstance()
|
||||
if (!vm) throw new ComposableInvokedOutOfScopeError()
|
||||
|
||||
return vm.proxy.$vuetify
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import { Nullable } from '@/helpers/typeHelpers'
|
||||
import { useEventHub } from '@/main/lib/core/composables/core'
|
||||
import {
|
||||
GlobalEvents,
|
||||
NotificationEventPayload,
|
||||
ToastNotificationType
|
||||
} from '@/main/lib/core/helpers/eventHubHelper'
|
||||
import Vue, { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
|
||||
const globalToastState = Vue.observable({
|
||||
isInitialized: false,
|
||||
queuedNotifications: [] as NotificationEventPayload[]
|
||||
})
|
||||
|
||||
const isInitialized = () => !!globalToastState.isInitialized
|
||||
const queuedNotifications = () => globalToastState.queuedNotifications
|
||||
const resetQueue = () => (globalToastState.queuedNotifications = [])
|
||||
const queueNotification = (e: NotificationEventPayload) => {
|
||||
const notifications = queuedNotifications().slice()
|
||||
notifications.push(e)
|
||||
Vue.set(globalToastState, 'queuedNotifications', notifications)
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke this only in GlobalToast.vue to properly initialize it
|
||||
*/
|
||||
export function setupGlobalToast() {
|
||||
const eventHub = useEventHub()
|
||||
|
||||
const snack = ref(false)
|
||||
const text = ref(null as Nullable<string>)
|
||||
const actionName = ref(null as Nullable<string>)
|
||||
const to = ref(null as Nullable<string>)
|
||||
const type = ref('primary' as ToastNotificationType)
|
||||
|
||||
const color = computed((): ToastNotificationType => type.value || 'primary')
|
||||
|
||||
watch(snack, (newVal) => {
|
||||
if (!newVal) {
|
||||
text.value = null
|
||||
actionName.value = null
|
||||
to.value = null
|
||||
}
|
||||
})
|
||||
|
||||
const handleEvent = (e: NotificationEventPayload) => {
|
||||
snack.value = true
|
||||
text.value = e.text
|
||||
actionName.value = e.action ? e.action.name : null
|
||||
to.value = e.action ? e.action.to : null
|
||||
type.value = e.type || 'primary'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
Vue.set(globalToastState, 'isInitialized', true)
|
||||
|
||||
eventHub.$on(GlobalEvents.Notification, handleEvent)
|
||||
|
||||
const queue = queuedNotifications()
|
||||
for (const queueItem of queue) {
|
||||
handleEvent(queueItem)
|
||||
}
|
||||
resetQueue()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
Vue.set(globalToastState, 'isInitialized', false)
|
||||
|
||||
eventHub.$off(GlobalEvents.Notification, handleEvent)
|
||||
})
|
||||
|
||||
return {
|
||||
snack,
|
||||
text,
|
||||
actionName,
|
||||
to,
|
||||
type,
|
||||
color
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger notification or queue it up to be triggered when GlobalToast.vue is ready
|
||||
*/
|
||||
export async function triggerToastNotification(
|
||||
eventHub: Vue,
|
||||
e: NotificationEventPayload
|
||||
) {
|
||||
if (isInitialized()) {
|
||||
eventHub.$emit(GlobalEvents.Notification, e)
|
||||
} else {
|
||||
queueNotification(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to emit toast notifications
|
||||
*/
|
||||
export function useGlobalToast() {
|
||||
const eventHub = useEventHub()
|
||||
|
||||
return {
|
||||
/**
|
||||
* Trigger a toast notification
|
||||
*/
|
||||
triggerNotification: (args: NotificationEventPayload) => {
|
||||
triggerToastNotification(eventHub, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Activity stream item action types that shouldn't be displayed in the FE
|
||||
*/
|
||||
export const SKIPPABLE_ACTION_TYPES = <const>[
|
||||
'stream_invite_sent',
|
||||
'stream_invite_declined',
|
||||
'stream_access_request_sent',
|
||||
'stream_access_request_declined'
|
||||
]
|
||||
@@ -43,7 +43,7 @@ export const UsersStreamInviteMixin = vueWithMixins(IsLoggedInMixin).extend({
|
||||
token(): Nullable<string> {
|
||||
return this.streamInvite.token || this.inviteToken || null
|
||||
},
|
||||
streamInviter(): Nullable<Get<StreamInviteQuery, 'streamInvite.invitedBy'>> {
|
||||
streamInviter(): NonNullable<Get<StreamInviteQuery, 'streamInvite.invitedBy'>> {
|
||||
return this.streamInvite.invitedBy
|
||||
},
|
||||
hasInvite(): boolean {
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
<div class="font-weight-bold">Feed</div>
|
||||
</portal>
|
||||
<v-row>
|
||||
<v-col cols="12" lg="12">
|
||||
<feed-timeline />
|
||||
<v-col cols="12" class="d-flex">
|
||||
<feed-timeline class="flex-grow-1" />
|
||||
<latest-blogposts class="d-none d-lg-block ml-6" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<latest-blogposts />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
@@ -147,7 +147,7 @@ export default {
|
||||
window.location.replace(
|
||||
`${window.location.origin}/auth/accesscode?appId=${this.app.id}&challenge=${
|
||||
this.$route.params.challenge
|
||||
}&token=${AppLocalStorage.get('AuthToken')}&suuid=${this.$route.query.suuid}`
|
||||
}&token=${AppLocalStorage.get('AuthToken')}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,23 +20,24 @@
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
id="new-password"
|
||||
v-model="form.password"
|
||||
v-model="password"
|
||||
label="new password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
:rules="validation.passwordRules"
|
||||
:readonly="loading"
|
||||
filled
|
||||
single-line
|
||||
style="margin-top: -12px"
|
||||
@keydown="debouncedPwdTest"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="form.passwordConf"
|
||||
v-model="passwordConfirmation"
|
||||
label="confirm new password"
|
||||
type="password"
|
||||
:rules="validation.passwordRules"
|
||||
:readonly="loading"
|
||||
filled
|
||||
single-line
|
||||
style="margin-top: -12px"
|
||||
@@ -44,7 +45,7 @@
|
||||
</v-col>
|
||||
<v-col cols="12" class="py-2" style="margin-top: -18px">
|
||||
<v-row
|
||||
v-show="passwordStrength !== 1 && form.password"
|
||||
v-show="passwordStrength !== 1 && password"
|
||||
no-gutters
|
||||
align="center"
|
||||
>
|
||||
@@ -68,16 +69,27 @@
|
||||
</v-col>
|
||||
<v-col cols="12" class="caption text-center mt-3">
|
||||
{{
|
||||
pwdSuggestions ? pwdSuggestions : form.password ? 'Looks good.' : null
|
||||
passwordSuggestion
|
||||
? passwordSuggestion
|
||||
: password && password === passwordConfirmation
|
||||
? 'Looks good.'
|
||||
: null
|
||||
}}
|
||||
<span v-if="form.password !== form.passwordConf">
|
||||
<div v-if="password !== passwordConfirmation">
|
||||
<b>Passwords do not match.</b>
|
||||
</span>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-btn type="submit" block large color="primary" @click="resetPassword()">
|
||||
<v-btn
|
||||
type="submit"
|
||||
block
|
||||
large
|
||||
color="primary"
|
||||
:disabled="loading"
|
||||
@click="resetPassword()"
|
||||
>
|
||||
Save new password
|
||||
</v-btn>
|
||||
</v-col>
|
||||
@@ -88,7 +100,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import { gql } from '@apollo/client/core'
|
||||
import debounce from 'lodash/debounce'
|
||||
import { useValidatablePasswordEntry } from '@/main/lib/auth/composables/useValidatablePasswordEntry'
|
||||
|
||||
export default {
|
||||
name: 'ResetPasswordRequest',
|
||||
@@ -117,22 +129,22 @@ export default {
|
||||
`
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const validatablePasswordEntry = useValidatablePasswordEntry()
|
||||
return {
|
||||
...validatablePasswordEntry
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
passwordStrength: 1,
|
||||
pwdSuggestions: null,
|
||||
form: {
|
||||
password: null,
|
||||
passwordConf: null
|
||||
},
|
||||
|
||||
validation: {
|
||||
passwordRules: [(v) => !!v || 'Required']
|
||||
},
|
||||
tokenId: null,
|
||||
errors: false,
|
||||
errorMessage: null,
|
||||
success: false
|
||||
success: false,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -147,28 +159,24 @@ export default {
|
||||
this.tokenId = this.$route.query.t
|
||||
},
|
||||
methods: {
|
||||
debouncedPwdTest: debounce(async function () {
|
||||
const result = await this.$apollo.query({
|
||||
query: gql` query{ userPwdStrength(pwd:"${this.form.password}")}`
|
||||
})
|
||||
this.passwordStrength = result.data.userPwdStrength.score * 25
|
||||
this.pwdSuggestions = result.data.userPwdStrength.feedback.suggestions[0]
|
||||
}, 1000),
|
||||
async resetPassword() {
|
||||
if (this.loading) return
|
||||
|
||||
try {
|
||||
this.success = false
|
||||
this.errors = false
|
||||
this.errorMessage = null
|
||||
const valid = this.$refs.form.validate()
|
||||
if (!valid) return
|
||||
if (this.form.password !== this.form.passwordConf)
|
||||
throw new Error('Passwords do not match')
|
||||
if (this.passwordStrength < 3) throw new Error('Password too weak.')
|
||||
this.validatePassword()
|
||||
|
||||
this.loading = true
|
||||
await this.validatePasswordStrength()
|
||||
|
||||
const res = await fetch(`/auth/pwdreset/finalize`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ tokenId: this.tokenId, password: this.form.password })
|
||||
body: JSON.stringify({ tokenId: this.tokenId, password: this.password })
|
||||
})
|
||||
|
||||
if (res.status !== 200) {
|
||||
@@ -181,6 +189,8 @@ export default {
|
||||
} catch (err) {
|
||||
this.errorMessage = err.message
|
||||
this.errors = true
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,7 @@
|
||||
<span class="hidden-md-and-up mr-2 primary--text">Speckle:</span>
|
||||
Interoperability in seconds
|
||||
</v-card-title>
|
||||
<auth-strategies
|
||||
:strategies="strategies"
|
||||
:app-id="appId"
|
||||
:challenge="challenge"
|
||||
:suuid="suuid"
|
||||
/>
|
||||
<auth-strategies :strategies="strategies" :app-id="appId" :challenge="challenge" />
|
||||
<div v-if="hasLocalStrategy">
|
||||
<v-card-title class="justify-center pb-5 pt-0 body-1 text--secondary">
|
||||
<v-divider class="mx-4"></v-divider>
|
||||
@@ -155,7 +150,6 @@ export default {
|
||||
errorMessage: '',
|
||||
serverApp: null,
|
||||
appId: null,
|
||||
suuid: null,
|
||||
challenge: null
|
||||
}),
|
||||
computed: {
|
||||
@@ -174,7 +168,6 @@ export default {
|
||||
query: {
|
||||
appId: this.$route.query.appId,
|
||||
challenge: this.$route.query.challenge,
|
||||
suuid: this.$route.query.suuid,
|
||||
token: this.token
|
||||
}
|
||||
}
|
||||
@@ -184,8 +177,6 @@ export default {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const appId = urlParams.get('appId')
|
||||
const challenge = urlParams.get('challenge')
|
||||
const suuid = urlParams.get('suuid')
|
||||
this.suuid = suuid
|
||||
|
||||
this.$mixpanel.track('Visit Log In')
|
||||
|
||||
@@ -210,8 +201,6 @@ export default {
|
||||
password: this.form.password
|
||||
}
|
||||
|
||||
if (this.suuid) user.suuid = this.suuid
|
||||
|
||||
const res = await fetch(`/auth/local/login?challenge=${this.challenge}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
:strategies="strategies"
|
||||
:app-id="appId"
|
||||
:challenge="challenge"
|
||||
:suuid="suuid"
|
||||
/>
|
||||
<v-card-title class="justify-center pb-5 pt-0 body-1 text--secondary">
|
||||
<v-divider class="mx-4"></v-divider>
|
||||
@@ -64,6 +63,7 @@
|
||||
v-model="form.email"
|
||||
label="your email"
|
||||
:rules="validation.emailRules"
|
||||
:readonly="loading"
|
||||
filled
|
||||
single-line
|
||||
prepend-icon="mdi-email"
|
||||
@@ -77,6 +77,7 @@
|
||||
v-model="form.firstName"
|
||||
label="name"
|
||||
:rules="validation.nameRules"
|
||||
:readonly="loading"
|
||||
filled
|
||||
single-line
|
||||
style="margin-top: -12px"
|
||||
@@ -88,6 +89,7 @@
|
||||
v-model="form.company"
|
||||
label="company/team"
|
||||
:rules="validation.companyRules"
|
||||
:readonly="loading"
|
||||
filled
|
||||
single-line
|
||||
style="margin-top: -12px"
|
||||
@@ -97,11 +99,12 @@
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field
|
||||
id="new-password"
|
||||
v-model="form.password"
|
||||
v-model="password"
|
||||
label="password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
:rules="validation.passwordRules"
|
||||
:readonly="loading"
|
||||
filled
|
||||
single-line
|
||||
style="margin-top: -12px"
|
||||
@@ -111,11 +114,12 @@
|
||||
<v-col cols="12" sm="6">
|
||||
<v-text-field
|
||||
id="confirm-password"
|
||||
v-model="form.passwordConf"
|
||||
v-model="passwordConfirmation"
|
||||
label="confirm password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
:rules="validation.passwordRules"
|
||||
:readonly="loading"
|
||||
filled
|
||||
single-line
|
||||
style="margin-top: -12px"
|
||||
@@ -123,7 +127,7 @@
|
||||
</v-col>
|
||||
<v-col cols="12" class="py-2 pl-9" style="margin-top: -18px">
|
||||
<v-row
|
||||
v-show="passwordStrength !== 1 && form.password"
|
||||
v-show="passwordStrength !== 1 && password"
|
||||
no-gutters
|
||||
align="center"
|
||||
>
|
||||
@@ -137,9 +141,9 @@
|
||||
height="5"
|
||||
class="mt-1 mb-0"
|
||||
:color="`${
|
||||
passwordStrength >= 75 && form.password === form.passwordConf
|
||||
passwordStrength >= 75 && password === passwordConfirmation
|
||||
? 'green'
|
||||
: passwordStrength >= 50 && form.password === form.passwordConf
|
||||
: passwordStrength >= 50 && password === passwordConfirmation
|
||||
? 'orange'
|
||||
: 'red'
|
||||
}`"
|
||||
@@ -147,15 +151,15 @@
|
||||
</v-col>
|
||||
<v-col cols="12" class="caption text-center mt-3">
|
||||
{{
|
||||
pwdSuggestions
|
||||
? pwdSuggestions
|
||||
: form.password && form.password === form.passwordConf
|
||||
passwordSuggestion
|
||||
? passwordSuggestion
|
||||
: password && password === passwordConfirmation
|
||||
? 'Looks good.'
|
||||
: null
|
||||
}}
|
||||
<span v-if="form.password !== form.passwordConf">
|
||||
<div v-if="password !== passwordConfirmation">
|
||||
<b>Passwords do not match.</b>
|
||||
</span>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
@@ -196,6 +200,7 @@ import {
|
||||
processSuccessfulAuth
|
||||
} from '@/main/lib/auth/services/authService'
|
||||
import { AppLocalStorage } from '@/utils/localStorage'
|
||||
import { useValidatablePasswordEntry } from '@/main/lib/auth/composables/useValidatablePasswordEntry'
|
||||
|
||||
export default {
|
||||
name: 'TheRegistration',
|
||||
@@ -226,6 +231,12 @@ export default {
|
||||
`
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const validatablePasswordEntry = useValidatablePasswordEntry()
|
||||
return {
|
||||
...validatablePasswordEntry
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
serverInfo: { authStrategies: [] },
|
||||
@@ -233,9 +244,7 @@ export default {
|
||||
email: null,
|
||||
firstName: null,
|
||||
lastName: null,
|
||||
company: null,
|
||||
password: null,
|
||||
passwordConf: null
|
||||
company: null
|
||||
},
|
||||
registrationError: false,
|
||||
errorMessage: '',
|
||||
@@ -251,11 +260,8 @@ export default {
|
||||
(v) => isEmailValid(v) || 'E-mail must be valid'
|
||||
]
|
||||
},
|
||||
passwordStrength: 1,
|
||||
pwdSuggestions: null,
|
||||
appId: null,
|
||||
challenge: null,
|
||||
suuid: null,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
@@ -269,7 +275,6 @@ export default {
|
||||
query: {
|
||||
appId: this.$route.query.appId,
|
||||
challenge: this.$route.query.challenge,
|
||||
suuid: this.$route.query.suuid,
|
||||
token: this.token
|
||||
}
|
||||
}
|
||||
@@ -285,8 +290,6 @@ export default {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const appId = urlParams.get('appId')
|
||||
const challenge = urlParams.get('challenge')
|
||||
const suuid = urlParams.get('suuid')
|
||||
this.suuid = suuid
|
||||
|
||||
this.$mixpanel.track('Visit Sign Up')
|
||||
|
||||
@@ -301,42 +304,25 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async validatePasswordStrength() {
|
||||
const result = await this.$apollo.query({
|
||||
query: gql`
|
||||
query ($pwd: String!) {
|
||||
userPwdStrength(pwd: $pwd)
|
||||
}
|
||||
`,
|
||||
variables: { pwd: this.form.password }
|
||||
})
|
||||
this.passwordStrength = result.data.userPwdStrength.score * 25
|
||||
this.pwdSuggestions = result.data.userPwdStrength.feedback.suggestions[0]
|
||||
},
|
||||
async registerUser() {
|
||||
if (this.loading) return
|
||||
|
||||
try {
|
||||
const valid = this.$refs.form.validate()
|
||||
if (!valid) return
|
||||
if (this.form.password !== this.form.passwordConf)
|
||||
throw new Error('Passwords do not match')
|
||||
this.validatePassword()
|
||||
|
||||
this.loading = true
|
||||
|
||||
// Validate password strength
|
||||
await this.validatePasswordStrength()
|
||||
if (this.passwordStrength < 3) throw new Error('Password too weak')
|
||||
|
||||
const user = {
|
||||
email: this.form.email,
|
||||
company: this.form.company,
|
||||
password: this.form.password,
|
||||
password: this.password,
|
||||
name: `${this.form.firstName}`
|
||||
}
|
||||
|
||||
if (this.suuid) user.suuid = this.suuid
|
||||
|
||||
const res = await fetch(
|
||||
`/auth/local/register?challenge=${this.challenge}${
|
||||
this.token ? '&token=' + this.token : ''
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
<template>
|
||||
<div class="commit-object-viewer">
|
||||
<div v-if="(isMultiple || isCommit || isObject) && !singleResourceError">
|
||||
<div
|
||||
v-if="
|
||||
firstResource && (isMultiple || isCommit || isObject) && !singleResourceError
|
||||
"
|
||||
>
|
||||
<commit-toolbar
|
||||
v-if="isCommit"
|
||||
:stream="resources[0].data"
|
||||
v-if="isCommitResource(firstResource)"
|
||||
:stream="firstResource.data"
|
||||
@edit-commit="showCommitEditDialog = true"
|
||||
/>
|
||||
<object-toolbar v-if="isObject" :stream="resources[0].data" />
|
||||
<object-toolbar
|
||||
v-if="isObjectResource(firstResource)"
|
||||
:stream="firstResource.data"
|
||||
/>
|
||||
<multiple-resources-toolbar
|
||||
v-if="isMultiple"
|
||||
:stream="{ name: resources[0].data.name, id: streamId }"
|
||||
v-if="isMultiple && isNotErrorResource(firstResource)"
|
||||
:stream="{ name: firstResource.data.name, id: streamId }"
|
||||
:resources="resources"
|
||||
/>
|
||||
|
||||
@@ -27,9 +34,9 @@
|
||||
</div>
|
||||
<v-list nav dense class="mt-0 pt-0">
|
||||
<v-list-item
|
||||
v-if="isCommit"
|
||||
v-if="isCommitResource(firstResource)"
|
||||
link
|
||||
:to="`/streams/${streamId}/branches/${resources[0].data.commit.branchName}`"
|
||||
:to="`/streams/${streamId}/branches/${firstResource.data.commit?.branchName}`"
|
||||
class=""
|
||||
>
|
||||
<v-list-item-icon>
|
||||
@@ -38,7 +45,7 @@
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="font-weight-bold">
|
||||
<v-icon small class="mr-1 caption">mdi-source-branch</v-icon>
|
||||
{{ resources[0].data.commit.branchName }}
|
||||
{{ firstResource.data.commit?.branchName }}
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
@@ -81,8 +88,8 @@
|
||||
class="mt-4"
|
||||
:source-application="
|
||||
resources
|
||||
.filter((r) => r.type === 'commit')
|
||||
.map((r) => r.data.commit.sourceApplication)
|
||||
.filter(isCommitResource)
|
||||
.map((r) => r.data.commit?.sourceApplication)
|
||||
.join(',')
|
||||
"
|
||||
/>
|
||||
@@ -92,7 +99,10 @@
|
||||
<!-- Preview image -->
|
||||
<v-fade-transition>
|
||||
<preview-image
|
||||
v-if="!loadedModel && (isCommit || isObject)"
|
||||
v-if="
|
||||
!loadedModel &&
|
||||
(isCommitResource(firstResource) || isObjectResource(firstResource))
|
||||
"
|
||||
:style="`
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
@@ -104,9 +114,9 @@
|
||||
`"
|
||||
:height="420"
|
||||
:url="`/preview/${streamId}/objects/${
|
||||
isCommit
|
||||
? resources[0].data.commit.referencedObject
|
||||
: resources[0].data.object.id
|
||||
isCommitResource(firstResource)
|
||||
? firstResource.data.commit?.referencedObject
|
||||
: firstResource.data.object?.id
|
||||
}`"
|
||||
></preview-image>
|
||||
</v-fade-transition>
|
||||
@@ -141,10 +151,11 @@
|
||||
:style="`width: 100%; bottom: 12px; left: 0px; position: ${
|
||||
$isMobile() ? 'fixed' : 'absolute'
|
||||
}; z-index: 20`"
|
||||
:class="`d-flex justify-center`"
|
||||
:class="`d-flex justify-center no-mouse`"
|
||||
>
|
||||
<viewer-controls
|
||||
v-show="!hideControls"
|
||||
class="mouse"
|
||||
@show-add-overlay="showAddOverlay = true"
|
||||
/>
|
||||
</div>
|
||||
@@ -229,12 +240,12 @@
|
||||
/>
|
||||
</v-dialog>
|
||||
<v-dialog
|
||||
v-if="isCommit"
|
||||
v-if="firstResource && isCommitResource(firstResource)"
|
||||
v-model="showCommitEditDialog"
|
||||
width="500"
|
||||
:fullscreen="$vuetify.breakpoint.smAndDown"
|
||||
>
|
||||
<commit-edit :stream="resources[0].data" @close="showCommitEditDialog = false" />
|
||||
<commit-edit :stream="firstResource.data" @close="showCommitEditDialog = false" />
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
@@ -277,10 +288,9 @@ type CommitResourceData = NonNullable<Get<StreamCommitQueryQuery, 'stream'>>
|
||||
|
||||
type ObjectResourceData = NonNullable<Get<StreamObjectNoDataQuery, 'stream'>>
|
||||
|
||||
type AllSupportedDataTypes =
|
||||
| ErroredResourceData
|
||||
| CommitResourceData
|
||||
| ObjectResourceData
|
||||
type NonErrorDataTypes = CommitResourceData | ObjectResourceData
|
||||
|
||||
type AllSupportedDataTypes = ErroredResourceData | NonErrorDataTypes
|
||||
|
||||
type ResourceTypeValue = 'commit' | 'object'
|
||||
|
||||
@@ -294,6 +304,10 @@ const isErrorResource = (
|
||||
resource: ResourceObjectType<unknown>
|
||||
): resource is ResourceObjectType<ErroredResourceData> => has(resource.data, 'error')
|
||||
|
||||
const isNotErrorResource = (
|
||||
resource: ResourceObjectType<unknown>
|
||||
): resource is ResourceObjectType<NonErrorDataTypes> => !isErrorResource(resource)
|
||||
|
||||
const isCommitResource = (
|
||||
resource: ResourceObjectType<unknown>
|
||||
): resource is ResourceObjectType<CommitResourceData> => resource.type === 'commit'
|
||||
@@ -375,7 +389,11 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
viewer,
|
||||
viewerState
|
||||
viewerState,
|
||||
isCommitResource,
|
||||
isObjectResource,
|
||||
isErrorResource,
|
||||
isNotErrorResource
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
@@ -397,22 +415,21 @@ export default defineComponent({
|
||||
return !this.$vuetify.breakpoint.smAndDown ? 'top: -64px;' : 'top: -56px;'
|
||||
},
|
||||
isCommit(): boolean {
|
||||
if (this.resources.length === 0) return false
|
||||
if (this.resources.length === 1 && this.resources[0].type === 'commit')
|
||||
return true
|
||||
return false
|
||||
if (this.isMultiple) return false
|
||||
return this.firstResource?.type === 'commit'
|
||||
},
|
||||
isObject(): boolean {
|
||||
if (this.resources.length === 0) return false
|
||||
if (this.resources.length === 1 && this.resources[0].type === 'object')
|
||||
return true
|
||||
return false
|
||||
if (this.isMultiple) return false
|
||||
return this.firstResource?.type === 'object'
|
||||
},
|
||||
isMultiple(): boolean {
|
||||
if (this.resources.length === 0) return false
|
||||
if (this.resources.length > 1) return true
|
||||
return false
|
||||
},
|
||||
firstResource(): Nullable<ResourceObjectType<AllSupportedDataTypes>> {
|
||||
return this.resources[0] || null
|
||||
},
|
||||
singleResourceError(): boolean {
|
||||
if (this.resources.length !== 1) return false
|
||||
const resource = this.resources[0]
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
</portal>
|
||||
<v-row v-if="stream" justify="center">
|
||||
<v-col v-if="serverInfo && stream" cols="12">
|
||||
<!-- Add contributors panel -->
|
||||
<v-row>
|
||||
<!-- Add contributors panel -->
|
||||
<v-col v-if="isStreamOwner" cols="12">
|
||||
<section-card :elevation="4">
|
||||
<v-progress-linear v-show="loading" indeterminate></v-progress-linear>
|
||||
@@ -97,6 +97,20 @@
|
||||
</v-alert>
|
||||
</v-col>
|
||||
|
||||
<!-- Stream access requests -->
|
||||
<v-col
|
||||
v-if="
|
||||
isStreamOwner && pendingAccessRequests && pendingAccessRequests.length
|
||||
"
|
||||
cols="12"
|
||||
>
|
||||
<stream-access-request-banner
|
||||
v-for="req in pendingAccessRequests"
|
||||
:key="req.id"
|
||||
:request="req"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Current users/invites for each role - owner, contributor, reviewer -->
|
||||
<v-col v-for="role in roles" :key="role.name" cols="12" md="4">
|
||||
<stream-role-collaborators
|
||||
@@ -146,6 +160,7 @@ import { IsLoggedInMixin } from '@/main/lib/core/mixins/isLoggedInMixin'
|
||||
import { vueWithMixins } from '@/helpers/typeHelpers'
|
||||
import { convertThrowIntoFetchResult } from '@/main/lib/common/apollo/helpers/apolloOperationHelper'
|
||||
import { AppLocalStorage } from '@/utils/localStorage'
|
||||
import StreamAccessRequestBanner from '@/main/components/stream/StreamAccessRequestBanner.vue'
|
||||
|
||||
export default vueWithMixins(IsLoggedInMixin).extend({
|
||||
// @vue/component
|
||||
@@ -155,7 +170,8 @@ export default vueWithMixins(IsLoggedInMixin).extend({
|
||||
InviteDialog,
|
||||
BasicUserInfoRow,
|
||||
StreamRoleCollaborators,
|
||||
LeaveStreamPanel
|
||||
LeaveStreamPanel,
|
||||
StreamAccessRequestBanner
|
||||
},
|
||||
mixins: [
|
||||
buildPortalStateMixin([STANDARD_PORTAL_KEYS.Toolbar], 'stream-collaborators', 1)
|
||||
@@ -255,6 +271,9 @@ export default vueWithMixins(IsLoggedInMixin).extend({
|
||||
if (!this.stream) return []
|
||||
return this.stream.collaborators.filter((u) => u.role === 'stream:owner')
|
||||
},
|
||||
pendingAccessRequests() {
|
||||
return this.stream?.pendingAccessRequests
|
||||
},
|
||||
filteredSearchResults() {
|
||||
if (!this.userSearch) return null
|
||||
const users = []
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
>
|
||||
<!-- BG image -->
|
||||
<div
|
||||
v-if="resourceMetadata && !isModelLoaded"
|
||||
v-if="previewUrl && resourceMetadata && !isModelLoaded"
|
||||
style="position: fixed; top: 0; width: 100%; height: 100%; cursor: pointer"
|
||||
class="embed-bg"
|
||||
@click="load()"
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
<!-- This should always be conditionally and asynchronously loaded so that heavy viewer deps are lazy loaded -->
|
||||
<embedded-commit-object-viewer
|
||||
v-if="shouldLoadHeavyDeps"
|
||||
v-if="resourceMetadata && shouldLoadHeavyDeps"
|
||||
:stream-id="streamId"
|
||||
:resource-id="resourceMetadata.resourceId"
|
||||
@models-loaded="onModelsLoaded"
|
||||
|
||||
@@ -51,18 +51,10 @@
|
||||
:disabled="stream.role !== 'stream:owner'"
|
||||
/>
|
||||
<h2>Privacy</h2>
|
||||
<v-switch
|
||||
v-model="isPublic"
|
||||
inset
|
||||
class="mt-5"
|
||||
:label="isPublic ? 'Link Sharing On' : 'Link Sharing Off'"
|
||||
:hint="
|
||||
isPublic
|
||||
? 'Anyone with the link can view this stream. It is also visible on your profile page. Only collaborators can push data to it.'
|
||||
: 'Only collaborators can access this stream.'
|
||||
"
|
||||
persistent-hint
|
||||
:disabled="stream.role !== 'stream:owner'"
|
||||
<stream-visibility-toggle
|
||||
:disabled="isEditDisabled"
|
||||
:is-public.sync="isPublic"
|
||||
:is-discoverable.sync="isDiscoverable"
|
||||
/>
|
||||
<br />
|
||||
<h2>Comments</h2>
|
||||
@@ -179,11 +171,14 @@ import {
|
||||
STANDARD_PORTAL_KEYS,
|
||||
buildPortalStateMixin
|
||||
} from '@/main/utils/portalStateManager'
|
||||
import SectionCard from '@/main/components/common/SectionCard.vue'
|
||||
import StreamVisibilityToggle from '@/main/components/stream/editor/StreamVisibilityToggle.vue'
|
||||
|
||||
export default {
|
||||
name: 'TheSettings',
|
||||
components: {
|
||||
SectionCard: () => import('@/main/components/common/SectionCard')
|
||||
SectionCard,
|
||||
StreamVisibilityToggle
|
||||
},
|
||||
mixins: [buildPortalStateMixin([STANDARD_PORTAL_KEYS.Toolbar], 'stream-settings', 1)],
|
||||
apollo: {
|
||||
@@ -195,6 +190,7 @@ export default {
|
||||
name
|
||||
description
|
||||
isPublic
|
||||
isDiscoverable
|
||||
allowPublicComments
|
||||
role
|
||||
}
|
||||
@@ -213,6 +209,7 @@ export default {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
isPublic: this.isPublic,
|
||||
isDiscoverable: this.isDiscoverable,
|
||||
allowPublicComments: this.allowPublicComments
|
||||
} = stream)
|
||||
|
||||
@@ -230,6 +227,7 @@ export default {
|
||||
streamNameConfirm: '',
|
||||
description: null,
|
||||
isPublic: true,
|
||||
isDiscoverable: false,
|
||||
allowPublicComments: true,
|
||||
validation: {
|
||||
nameRules: [(v) => !!v || 'A stream must have a name!']
|
||||
@@ -238,13 +236,17 @@ export default {
|
||||
computed: {
|
||||
canSave() {
|
||||
return (
|
||||
this.stream.role === 'stream:owner' &&
|
||||
!this.isEditDisabled &&
|
||||
this.valid &&
|
||||
(this.name !== this.stream.name ||
|
||||
this.description !== this.stream.description ||
|
||||
this.isPublic !== this.stream.isPublic ||
|
||||
this.allowPublicComments !== this.stream.allowPublicComments)
|
||||
this.allowPublicComments !== this.stream.allowPublicComments ||
|
||||
this.isDiscoverable !== this.stream.isDiscoverable)
|
||||
)
|
||||
},
|
||||
isEditDisabled() {
|
||||
return this.stream?.role !== 'stream:owner'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -272,7 +274,8 @@ export default {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
isPublic: this.isPublic,
|
||||
allowPublicComments: this.allowPublicComments
|
||||
allowPublicComments: this.allowPublicComments,
|
||||
isDiscoverable: this.isDiscoverable
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -22,15 +22,43 @@
|
||||
</transition>
|
||||
</div>
|
||||
<div v-else style="width: 100%">
|
||||
<error-placeholder v-if="!showInvitePlaceholder" :error-type="errorType">
|
||||
<h2>{{ errorMsg }}</h2>
|
||||
</error-placeholder>
|
||||
<stream-invite-placeholder
|
||||
v-else
|
||||
v-if="showInvitePlaceholder"
|
||||
:stream-invite="streamInvite"
|
||||
:invite-token="inviteToken"
|
||||
@invite-used="onInviteClosed"
|
||||
/>
|
||||
<error-placeholder v-else :error-type="errorType">
|
||||
<template #default>
|
||||
<h2>{{ errorMsg }}</h2>
|
||||
</template>
|
||||
<template v-if="allowRequestAccess" #actions>
|
||||
<rounded-button-list>
|
||||
<rounded-button-list-item
|
||||
type="primary"
|
||||
icon="mdi-lock-outline"
|
||||
@click="onRequestAccess"
|
||||
>
|
||||
<span v-if="hasStreamAccessRequest">Access Request sent</span>
|
||||
<span v-else>Request Access to Stream</span>
|
||||
|
||||
<template #subtitle>
|
||||
<span v-if="hasStreamAccessRequest">
|
||||
You will get a confirmation email once it's been approved
|
||||
</span>
|
||||
<span v-else>Request Access from the stream owners</span>
|
||||
</template>
|
||||
</rounded-button-list-item>
|
||||
<rounded-button-list-item
|
||||
type="secondary"
|
||||
icon="mdi-home-outline"
|
||||
@click="onBackToStreams"
|
||||
>
|
||||
Back to your Streams
|
||||
</rounded-button-list-item>
|
||||
</rounded-button-list>
|
||||
</template>
|
||||
</error-placeholder>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -40,7 +68,7 @@
|
||||
import { gql } from '@apollo/client/core'
|
||||
import StreamInviteBanner from '@/main/components/stream/StreamInviteBanner.vue'
|
||||
import { StreamEvents } from '@/main/lib/core/helpers/eventHubHelper'
|
||||
import Vue from 'vue'
|
||||
import Vue, { defineComponent, computed } from 'vue'
|
||||
import { Nullable, MaybeFalsy } from '@/helpers/typeHelpers'
|
||||
import {
|
||||
StreamInviteDocument,
|
||||
@@ -48,13 +76,24 @@ import {
|
||||
MainUserDataQuery,
|
||||
StreamQuery,
|
||||
StreamQueryVariables,
|
||||
StreamDocument
|
||||
StreamDocument,
|
||||
CreateStreamAccessRequestDocument,
|
||||
GetStreamAccessRequestDocument
|
||||
} from '@/graphql/generated/graphql'
|
||||
import type { ApolloQueryResult, ApolloError } from '@apollo/client/core'
|
||||
import type { Get } from 'type-fest'
|
||||
import StreamInvitePlaceholder from '@/main/components/stream/StreamInvitePlaceholder.vue'
|
||||
import { StreamInviteType } from '@/main/lib/stream/mixins/streamInviteMixin'
|
||||
import { getInviteTokenFromRoute } from '@/main/lib/auth/services/authService'
|
||||
import RoundedButtonList from '@/main/components/common/layout/RoundedButtonList.vue'
|
||||
import RoundedButtonListItem from '@/main/components/common/layout/rounded-button-list/RoundedButtonListItem.vue'
|
||||
import {
|
||||
convertThrowIntoFetchResult,
|
||||
getFirstErrorMessage
|
||||
} from '@/main/lib/common/apollo/helpers/apolloOperationHelper'
|
||||
import { useIsLoggedIn } from '@/main/lib/core/composables/core'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { useRoute } from '@/main/lib/core/composables/router'
|
||||
|
||||
// Cause of a limitation of Vue Apollo Options API TS types, this needs to be duplicated
|
||||
// (the better option is to just use the Composition API)
|
||||
@@ -62,16 +101,39 @@ type VueThis = Vue & {
|
||||
streamId: string
|
||||
inviteToken: Nullable<string>
|
||||
error: Nullable<Error>
|
||||
isLoggedIn: boolean
|
||||
}
|
||||
|
||||
export default Vue.extend({
|
||||
export default defineComponent({
|
||||
name: 'TheStream',
|
||||
components: {
|
||||
ErrorPlaceholder: () => import('@/main/components/common/ErrorPlaceholder.vue'),
|
||||
StreamNav: () => import('@/main/navigation/StreamNav.vue'),
|
||||
StreamToolbar: () => import('@/main/toolbars/StreamToolbar.vue'),
|
||||
StreamInviteBanner,
|
||||
StreamInvitePlaceholder
|
||||
StreamInvitePlaceholder,
|
||||
RoundedButtonList,
|
||||
RoundedButtonListItem
|
||||
},
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const streamId = computed(() => route.params.streamId as string)
|
||||
|
||||
const { isLoggedIn } = useIsLoggedIn()
|
||||
|
||||
const { result: streamAccessRequestResult } = useQuery(
|
||||
GetStreamAccessRequestDocument,
|
||||
() => ({ streamId: streamId.value })
|
||||
)
|
||||
const hasStreamAccessRequest = computed(
|
||||
() => !!streamAccessRequestResult.value?.streamAccessRequest?.id
|
||||
)
|
||||
|
||||
return {
|
||||
isLoggedIn,
|
||||
streamId,
|
||||
hasStreamAccessRequest
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -80,16 +142,14 @@ export default Vue.extend({
|
||||
streamInvite: null as Nullable<StreamInviteType>,
|
||||
shareStream: false,
|
||||
branchMenuOpen: false,
|
||||
inviteClosed: false
|
||||
inviteClosed: false,
|
||||
stream: null as Nullable<StreamQuery>
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
inviteToken(): Nullable<string> {
|
||||
return getInviteTokenFromRoute(this.$route)
|
||||
},
|
||||
streamId(): string {
|
||||
return this.$route.params.streamId
|
||||
},
|
||||
errorMsg(): MaybeFalsy<string> {
|
||||
return this.error?.message.replace('GraphQL error: ', '')
|
||||
},
|
||||
@@ -113,6 +173,9 @@ export default Vue.extend({
|
||||
showInvitePlaceholder(): boolean {
|
||||
return !!(this.hasInvite && this.isAccessError)
|
||||
},
|
||||
allowRequestAccess(): boolean {
|
||||
return !!(this.isAccessError && this.isLoggedIn)
|
||||
},
|
||||
hasInvite(): boolean {
|
||||
return !!(this.streamInvite && !this.inviteClosed)
|
||||
}
|
||||
@@ -172,7 +235,7 @@ export default Vue.extend({
|
||||
})
|
||||
},
|
||||
skip(this: VueThis): boolean {
|
||||
return !this.$loggedIn()
|
||||
return !this.isLoggedIn
|
||||
}
|
||||
},
|
||||
commitCreated: {
|
||||
@@ -200,7 +263,7 @@ export default Vue.extend({
|
||||
})
|
||||
},
|
||||
skip(this: VueThis): boolean {
|
||||
return !this.$loggedIn()
|
||||
return !this.isLoggedIn
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,6 +277,43 @@ export default Vue.extend({
|
||||
onInviteClosed() {
|
||||
this.inviteClosed = true
|
||||
this.error = null
|
||||
},
|
||||
onBackToStreams() {
|
||||
this.$router.push('/streams')
|
||||
},
|
||||
async onRequestAccess() {
|
||||
if (this.hasStreamAccessRequest) return
|
||||
|
||||
const { data, errors } = await this.$apollo
|
||||
.mutate({
|
||||
mutation: CreateStreamAccessRequestDocument,
|
||||
variables: {
|
||||
streamId: this.streamId
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
if (!data?.streamAccessRequestCreate.id) return
|
||||
|
||||
// Update GetStreamAccessRequest query
|
||||
const newReq = data.streamAccessRequestCreate
|
||||
cache.writeQuery({
|
||||
query: GetStreamAccessRequestDocument,
|
||||
variables: { streamId: this.streamId },
|
||||
data: { streamAccessRequest: { ...newReq } }
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
|
||||
if (data?.streamAccessRequestCreate.id) {
|
||||
this.$triggerNotification({
|
||||
text: 'A stream access request has been submitted'
|
||||
})
|
||||
} else {
|
||||
this.$triggerNotification({
|
||||
text: getFirstErrorMessage(errors),
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -70,7 +70,6 @@ export default {
|
||||
verified
|
||||
profiles
|
||||
role
|
||||
suuid
|
||||
totalOwnedStreamsFavorites
|
||||
streams {
|
||||
totalCount
|
||||
|
||||
@@ -33,7 +33,7 @@ const routes = [
|
||||
path: 'resetpassword',
|
||||
name: 'Reset Password',
|
||||
meta: {
|
||||
title: 'Register | Speckle'
|
||||
title: 'Reset Password | Speckle'
|
||||
},
|
||||
component: () => import('@/main/pages/auth/ResetPasswordRequest.vue')
|
||||
},
|
||||
@@ -41,7 +41,7 @@ const routes = [
|
||||
path: 'resetpassword/finalize',
|
||||
name: 'Reset Password Finalization',
|
||||
meta: {
|
||||
title: 'Register | Speckle'
|
||||
title: 'Reset Password | Speckle'
|
||||
},
|
||||
component: () => import('@/main/pages/auth/ResetPasswordFinalization.vue')
|
||||
},
|
||||
|
||||
@@ -31,11 +31,11 @@ export async function checkAccessCodeAndGetTokens() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user id and suuid, sets them in local storage
|
||||
* Gets the user id and sets it in localStorage
|
||||
* @param {import('@apollo/client/core').ApolloClient} apolloClient
|
||||
* @return {Object} The full graphql response.
|
||||
*/
|
||||
export async function prefetchUserAndSetSuuid(apolloClient) {
|
||||
export async function prefetchUserAndSetID(apolloClient) {
|
||||
const token = AppLocalStorage.get(LocalStorageKeys.AuthToken)
|
||||
if (!token) return
|
||||
|
||||
@@ -45,11 +45,8 @@ export async function prefetchUserAndSetSuuid(apolloClient) {
|
||||
})
|
||||
|
||||
if (data.user) {
|
||||
// eslint-disable-next-line camelcase
|
||||
const distinct_id = '@' + md5(data.user.email.toLowerCase()).toUpperCase()
|
||||
|
||||
AppLocalStorage.set('suuid', data.user.suuid)
|
||||
AppLocalStorage.set('distinct_id', distinct_id)
|
||||
const distinctId = '@' + md5(data.user.email.toLowerCase()).toUpperCase()
|
||||
AppLocalStorage.set('distinct_id', distinctId)
|
||||
AppLocalStorage.set('uuid', data.user.id)
|
||||
AppLocalStorage.set('stcount', data.user.streams.totalCount)
|
||||
return data
|
||||
@@ -95,7 +92,6 @@ export async function signOut(mixpanelInstance) {
|
||||
|
||||
AppLocalStorage.remove(LocalStorageKeys.AuthToken)
|
||||
AppLocalStorage.remove(LocalStorageKeys.RefreshToken)
|
||||
AppLocalStorage.remove('suuid')
|
||||
AppLocalStorage.remove('uuid')
|
||||
AppLocalStorage.remove('distinct_id')
|
||||
AppLocalStorage.remove('stcount')
|
||||
@@ -131,7 +127,7 @@ export async function refreshToken() {
|
||||
if (data.hasOwnProperty('token')) {
|
||||
AppLocalStorage.set(LocalStorageKeys.AuthToken, data.token)
|
||||
AppLocalStorage.set(LocalStorageKeys.RefreshToken, data.refreshToken)
|
||||
await prefetchUserAndSetSuuid()
|
||||
await prefetchUserAndSetID()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { NotificationEventPayload } from '@/main/lib/core/helpers/eventHubHelper
|
||||
import { AppLocalStorage } from '@/utils/localStorage'
|
||||
import { LocalStorageKeys } from '@/helpers/mainConstants'
|
||||
import { getInviteTokenFromURL } from '@/main/lib/auth/services/authService'
|
||||
import { triggerToastNotification } from '@/main/lib/core/composables/notifications'
|
||||
|
||||
Vue.prototype.$userId = function () {
|
||||
return AppLocalStorage.get(LocalStorageKeys.Uuid)
|
||||
@@ -46,5 +47,5 @@ Vue.prototype.$loginAndSetRedirect = function () {
|
||||
* Trigger a toast notification from anywhere
|
||||
*/
|
||||
Vue.prototype.$triggerNotification = function (args: NotificationEventPayload) {
|
||||
this.$eventHub.$emit('notification', args)
|
||||
triggerToastNotification(this.$eventHub, args)
|
||||
}
|
||||
|
||||
@@ -267,3 +267,10 @@ html {
|
||||
opacity: 0.5;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.no-mouse {
|
||||
pointer-events: none;
|
||||
}
|
||||
.mouse {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue'
|
||||
export default Vue
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
declare module 'vue/types/vue' {
|
||||
import { Nullable } from '@/helpers/typeHelpers'
|
||||
|
||||
export interface Vue {
|
||||
/**
|
||||
* Mixpanel instance
|
||||
@@ -16,6 +18,14 @@ declare module 'vue/types/vue' {
|
||||
args: import('@/main/lib/core/helpers/eventHubHelper').NotificationEventPayload
|
||||
): void
|
||||
|
||||
$userId: () => Nullable<string>
|
||||
|
||||
/**
|
||||
* Whether the client seems to be a mobile device. Note that this doesn't check for screen size, only
|
||||
* whether this is a mobile device.
|
||||
*/
|
||||
$isMobile: () => boolean
|
||||
|
||||
/**
|
||||
* Redirect to log in and redirect back to current page post-login
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
"@types/dompurify"
|
||||
]
|
||||
},
|
||||
"vueCompilerOptions": {
|
||||
"target": 2.7
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.js",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"directory": "packages/objectloader"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0"
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint . --ext .js,.ts",
|
||||
|
||||
@@ -59,6 +59,14 @@ EMAIL_PORT="1025"
|
||||
# EMAIL_PASSWORD="-> FILL IN <-"
|
||||
# EMAIL_FROM="-> FILL IN <-"
|
||||
|
||||
############################################################
|
||||
# Notifications
|
||||
# Settings related to the MQ based notifications module
|
||||
############################################################
|
||||
|
||||
# If set to true, will prevent the server from consuming notification jobs
|
||||
DISABLE_NOTIFICATIONS_CONSUMPTION=false
|
||||
|
||||
############################################################
|
||||
# Auth strategies
|
||||
# At least one needs to be enabled!
|
||||
|
||||
@@ -14,12 +14,6 @@ const config = {
|
||||
},
|
||||
ignorePatterns: ['node_modules', 'dist', 'generated/**/*'],
|
||||
overrides: [
|
||||
{
|
||||
files: '*.spec.{js,ts}',
|
||||
env: {
|
||||
mocha: true
|
||||
}
|
||||
},
|
||||
{
|
||||
files: '*.ts',
|
||||
plugins: ['@typescript-eslint'],
|
||||
@@ -32,6 +26,21 @@ const config = {
|
||||
'prettier'
|
||||
],
|
||||
parser: '@typescript-eslint/parser'
|
||||
},
|
||||
{
|
||||
files: '*.d.ts',
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: '*.spec.{js,ts}',
|
||||
env: {
|
||||
mocha: true
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-non-null-assertion': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import { buildErrorFormatter } from '@/modules/core/graph/setup'
|
||||
import { isDevEnv, isTestEnv } from '@/modules/shared/helpers/envHelper'
|
||||
import * as ModulesSetup from '@/modules'
|
||||
import { Optional } from '@/modules/shared/helpers/typeHelper'
|
||||
import apolloPlugin from '@/logging/apolloPlugin'
|
||||
|
||||
import { get, has, isString, toNumber } from 'lodash'
|
||||
|
||||
@@ -100,7 +99,7 @@ export function buildApolloServer(
|
||||
metricConnectedClients.dec()
|
||||
}
|
||||
},
|
||||
plugins: [apolloPlugin],
|
||||
plugins: [require('@/logging/apolloPlugin')],
|
||||
tracing: debug,
|
||||
introspection: true,
|
||||
playground: true,
|
||||
@@ -159,6 +158,10 @@ export async function init() {
|
||||
return { app, graphqlServer }
|
||||
}
|
||||
|
||||
export async function shutdown(): Promise<void> {
|
||||
await ModulesSetup.shutdown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a http server, hoisting the express app to it.
|
||||
*/
|
||||
@@ -211,7 +214,7 @@ export async function startHttp(app: Express, customPortOverride?: number) {
|
||||
debug('speckle:shutdown')('Shutting down (signal received)...')
|
||||
},
|
||||
onSignal: async () => {
|
||||
// Other custom cleanup after connections are finished
|
||||
await shutdown()
|
||||
},
|
||||
onShutdown: () => {
|
||||
debug('speckle:shutdown')('Shutdown completed')
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
extend type Query {
|
||||
"""
|
||||
Get authed user's stream access request
|
||||
"""
|
||||
streamAccessRequest(streamId: String!): StreamAccessRequest
|
||||
@hasRole(role: "server:user")
|
||||
}
|
||||
|
||||
extend type Stream {
|
||||
"""
|
||||
Pending stream access requests
|
||||
"""
|
||||
pendingAccessRequests: [StreamAccessRequest!] @hasStreamRole(role: STREAM_OWNER)
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
"""
|
||||
Accept or decline a stream access request. Must be a stream owner to invoke this.
|
||||
"""
|
||||
streamAccessRequestUse(
|
||||
requestId: String!
|
||||
accept: Boolean!
|
||||
role: StreamRole! = STREAM_CONTRIBUTOR
|
||||
): Boolean! @hasRole(role: "server:user") @hasScope(scope: "users:invite")
|
||||
|
||||
"""
|
||||
Request access to a specific stream
|
||||
"""
|
||||
streamAccessRequestCreate(streamId: String!): StreamAccessRequest!
|
||||
@hasRole(role: "server:user")
|
||||
@hasScope(scope: "users:invite")
|
||||
}
|
||||
|
||||
"""
|
||||
Created when a user requests to become a contributor on a stream
|
||||
"""
|
||||
type StreamAccessRequest {
|
||||
id: ID!
|
||||
requester: LimitedUser!
|
||||
requesterId: String!
|
||||
streamId: String!
|
||||
"""
|
||||
Can only be selected if authed user has proper access
|
||||
"""
|
||||
stream: Stream!
|
||||
createdAt: DateTime!
|
||||
}
|
||||
@@ -26,3 +26,8 @@ type SmartTextEditorValue {
|
||||
"""
|
||||
attachments: [BlobMetadata!]
|
||||
}
|
||||
|
||||
enum SortDirection {
|
||||
ASC
|
||||
DESC
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ extend type Query {
|
||||
streams(query: String, limit: Int = 25, cursor: String): StreamCollection
|
||||
@hasScope(scope: "streams:read")
|
||||
|
||||
"""
|
||||
All the streams of the server. Available to admins only.
|
||||
"""
|
||||
adminStreams(
|
||||
offset: Int = 0
|
||||
query: String
|
||||
@@ -18,13 +21,33 @@ extend type Query {
|
||||
visibility: String
|
||||
limit: Int = 25
|
||||
): StreamCollection @hasRole(role: "server:admin")
|
||||
|
||||
"""
|
||||
All of the discoverable streams of the server
|
||||
"""
|
||||
discoverableStreams(
|
||||
limit: Int! = 25
|
||||
cursor: String
|
||||
"""
|
||||
Defaults to sorting by creation date in a descending order
|
||||
"""
|
||||
sort: DiscoverableStreamsSortingInput
|
||||
): StreamCollection
|
||||
}
|
||||
|
||||
type Stream {
|
||||
id: String!
|
||||
name: String!
|
||||
description: String
|
||||
"""
|
||||
Whether the stream can be viewed by non-contributors
|
||||
"""
|
||||
isPublic: Boolean!
|
||||
"""
|
||||
Whether the stream (if public) can be found on public stream exploration pages
|
||||
and searches
|
||||
"""
|
||||
isDiscoverable: Boolean!
|
||||
allowPublicComments: Boolean!
|
||||
"""
|
||||
Your role for this stream. `null` if request is not authenticated, or the stream is not explicitly shared with you.
|
||||
@@ -189,8 +212,16 @@ extend type Subscription {
|
||||
input StreamCreateInput {
|
||||
name: String
|
||||
description: String
|
||||
"""
|
||||
Whether the stream can be viewed by non-contributors
|
||||
"""
|
||||
isPublic: Boolean
|
||||
"""
|
||||
Whether the stream (if public) can be found on public stream exploration pages
|
||||
and searches
|
||||
"""
|
||||
isDiscoverable: Boolean
|
||||
"""
|
||||
Optionally specify user IDs of users that you want to invite to be contributors to this stream
|
||||
"""
|
||||
withContributors: [String!]
|
||||
@@ -200,7 +231,15 @@ input StreamUpdateInput {
|
||||
id: String!
|
||||
name: String
|
||||
description: String
|
||||
"""
|
||||
Whether the stream can be viewed by non-contributors
|
||||
"""
|
||||
isPublic: Boolean
|
||||
"""
|
||||
Whether the stream (if public) can be found on public stream exploration pages
|
||||
and searches
|
||||
"""
|
||||
isDiscoverable: Boolean
|
||||
allowPublicComments: Boolean
|
||||
}
|
||||
|
||||
@@ -214,3 +253,13 @@ input StreamRevokePermissionInput {
|
||||
streamId: String!
|
||||
userId: String!
|
||||
}
|
||||
|
||||
input DiscoverableStreamsSortingInput {
|
||||
type: DiscoverableStreamsSortType!
|
||||
direction: SortDirection!
|
||||
}
|
||||
|
||||
enum DiscoverableStreamsSortType {
|
||||
FAVORITES_COUNT
|
||||
CREATED_DATE
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
extend type Query {
|
||||
"""
|
||||
Gets the profile of a user. If no id argument is provided, will return the current authenticated user's profile (as extracted from the authorization header).
|
||||
If ID is provided, admin access is required
|
||||
"""
|
||||
user(id: String): User
|
||||
|
||||
@@ -27,7 +26,33 @@ extend type Query {
|
||||
cursor: String
|
||||
archived: Boolean = false
|
||||
): UserSearchResultCollection
|
||||
userPwdStrength(pwd: String!): JSONObject
|
||||
|
||||
"""
|
||||
Validate password strength
|
||||
"""
|
||||
userPwdStrength(pwd: String!): PasswordStrengthCheckResults!
|
||||
}
|
||||
|
||||
type PasswordStrengthCheckResults {
|
||||
"""
|
||||
Integer from 0-4 (useful for implementing a strength bar):
|
||||
0 too guessable: risky password. (guesses < 10^3)
|
||||
1 very guessable: protection from throttled online attacks. (guesses < 10^6)
|
||||
2 somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8)
|
||||
3 safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10)
|
||||
4 very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10)
|
||||
"""
|
||||
score: Int!
|
||||
|
||||
"""
|
||||
Verbal feedback to help choose better passwords. set when score <= 2.
|
||||
"""
|
||||
feedback: PasswordStrengthCheckFeedback!
|
||||
}
|
||||
|
||||
type PasswordStrengthCheckFeedback {
|
||||
warning: String
|
||||
suggestions: [String!]!
|
||||
}
|
||||
|
||||
"""
|
||||
@@ -36,7 +61,6 @@ when a user is reading/writing info about himself
|
||||
"""
|
||||
type User {
|
||||
id: String!
|
||||
suuid: String
|
||||
"""
|
||||
E-mail can be null, if it's requested for a user other than the authenticated one
|
||||
and the user isn't an admin
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
</head>
|
||||
|
||||
<body style="word-spacing: normal">
|
||||
<div style="">
|
||||
<div>
|
||||
<!-- Header -->
|
||||
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="linear-gradient(90deg, rgba(0,143,233,1) 0%, rgba(0,76,235,1) 100%)" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div
|
||||
@@ -265,7 +265,7 @@
|
||||
style="
|
||||
font-family: Helvetica;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
line-height: 1;
|
||||
text-align: left;
|
||||
color: #000000;
|
||||
"
|
||||
@@ -375,7 +375,7 @@
|
||||
font-family: Helvetica;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
line-height: 120%;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
text-transform: none;
|
||||
@@ -462,7 +462,7 @@
|
||||
style="
|
||||
font-family: Helvetica;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
line-height: 1;
|
||||
text-align: left;
|
||||
color: #000000;
|
||||
"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
extend type User {
|
||||
"""
|
||||
Whether the user has a pending/active email verification token
|
||||
"""
|
||||
hasPendingVerification: Boolean @isOwner
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
"""
|
||||
(Re-)send the account verification e-mail
|
||||
"""
|
||||
requestVerification: Boolean! @hasRole(role: "server:user")
|
||||
}
|
||||
@@ -2,18 +2,32 @@ overwrite: true
|
||||
schema:
|
||||
- 'modules/core/graph/schema/baseTypeDefs.js'
|
||||
- 'assets/**/*.graphql'
|
||||
- 'assets/**/*.gql'
|
||||
documents: null
|
||||
generates:
|
||||
modules/core/graph/generated/graphql.ts:
|
||||
plugins:
|
||||
- 'typescript'
|
||||
- 'typescript-resolvers'
|
||||
config:
|
||||
contextType: '@/modules/shared/helpers/typeHelper#GraphQLContext'
|
||||
mappers:
|
||||
Stream: '@/modules/core/helpers/graphTypes#StreamGraphQLReturn'
|
||||
StreamAccessRequest: '@/modules/accessrequests/helpers/graphTypes#StreamAccessRequestGraphQLReturn'
|
||||
test/graphql/generated/graphql.ts:
|
||||
plugins:
|
||||
- 'typescript'
|
||||
- 'typescript-operations'
|
||||
documents:
|
||||
- 'test/graphql/*.{js,ts}'
|
||||
config:
|
||||
scalars:
|
||||
JSONObject: Record<string, unknown>
|
||||
DateTime: string
|
||||
config:
|
||||
scalars:
|
||||
JSONObject: Record<string, unknown>
|
||||
DateTime: Date
|
||||
require:
|
||||
- ts-node/register
|
||||
- tsconfig-paths/register
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { BaseError } from '@/modules/shared/errors'
|
||||
|
||||
export class AccessRequestProcessingError extends BaseError {
|
||||
static defaultMessage =
|
||||
'An unexpected error occurred while processing an access request'
|
||||
static code = 'ACCESS_REQUEST_PROCESSING_ERROR'
|
||||
}
|
||||
|
||||
export class AccessRequestCreationError extends BaseError {
|
||||
static defaultMessage =
|
||||
'An unexpected error occurred while creating an access request'
|
||||
static code = 'ACCESS_REQUEST_CREATION_ERROR'
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ServerAccessRequestRecord } from '@/modules/accessrequests/repositories'
|
||||
import { StreamRoles } from '@/modules/core/helpers/mainConstants'
|
||||
import { initializeModuleEventEmitter } from '@/modules/shared/services/moduleEventEmitterSetup'
|
||||
|
||||
export enum AccessRequestsEvents {
|
||||
Created = 'created',
|
||||
Finalized = 'finalized'
|
||||
}
|
||||
|
||||
export type AccessRequestsEventsPayloads = {
|
||||
[AccessRequestsEvents.Created]: { request: ServerAccessRequestRecord }
|
||||
[AccessRequestsEvents.Finalized]: {
|
||||
request: ServerAccessRequestRecord
|
||||
/**
|
||||
* ID of the user that finalized this request
|
||||
*/
|
||||
finalizedBy: string
|
||||
/**
|
||||
* If this object is set, request was approved
|
||||
*/
|
||||
approved?: {
|
||||
role: StreamRoles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { emit, listen } = initializeModuleEventEmitter<AccessRequestsEventsPayloads>({
|
||||
moduleName: 'accessrequests'
|
||||
})
|
||||
|
||||
export const AccessRequestsEmitter = { emit, listen, events: AccessRequestsEvents }
|
||||
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
getPendingStreamRequests,
|
||||
getUserStreamAccessRequest,
|
||||
processPendingStreamRequest,
|
||||
requestStreamAccess
|
||||
} from '@/modules/accessrequests/services/stream'
|
||||
import { Resolvers } from '@/modules/core/graph/generated/graphql'
|
||||
import { mapStreamRoleToValue } from '@/modules/core/helpers/graphTypes'
|
||||
import { LogicError } from '@/modules/shared/errors'
|
||||
|
||||
const resolvers: Resolvers = {
|
||||
Mutation: {
|
||||
async streamAccessRequestUse(_parent, args, ctx) {
|
||||
const { userId } = ctx
|
||||
const { requestId, accept, role } = args
|
||||
|
||||
if (!userId) throw new LogicError('User ID unexpectedly false')
|
||||
|
||||
await processPendingStreamRequest(
|
||||
userId,
|
||||
requestId,
|
||||
accept,
|
||||
mapStreamRoleToValue(role)
|
||||
)
|
||||
return true
|
||||
},
|
||||
async streamAccessRequestCreate(_parent, args, ctx) {
|
||||
const { userId } = ctx
|
||||
if (!userId) throw new LogicError('User ID unexpectedly false')
|
||||
|
||||
const { streamId } = args
|
||||
return await requestStreamAccess(userId, streamId)
|
||||
}
|
||||
},
|
||||
Query: {
|
||||
async streamAccessRequest(_, args, ctx) {
|
||||
const { streamId } = args
|
||||
const { userId } = ctx
|
||||
if (!userId) throw new LogicError('User ID unexpectedly false')
|
||||
|
||||
return await getUserStreamAccessRequest(userId, streamId)
|
||||
}
|
||||
},
|
||||
Stream: {
|
||||
async pendingAccessRequests(parent) {
|
||||
const { id } = parent
|
||||
return await getPendingStreamRequests(id)
|
||||
}
|
||||
},
|
||||
StreamAccessRequest: {
|
||||
async requester(parent, _args, ctx) {
|
||||
const { requesterId } = parent
|
||||
const user = await ctx.loaders.users.getUser.load(requesterId)
|
||||
if (!user) {
|
||||
throw new LogicError('Unable to find requester')
|
||||
}
|
||||
|
||||
return user
|
||||
},
|
||||
async stream(parent, _args, ctx) {
|
||||
const { streamId } = parent
|
||||
const stream = await ctx.loaders.streams.getStream.load(streamId)
|
||||
if (!stream) {
|
||||
throw new LogicError('Unable to find request stream')
|
||||
}
|
||||
|
||||
return stream
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export = resolvers
|
||||
@@ -0,0 +1,6 @@
|
||||
import { StreamAccessRequest } from '@/modules/core/graph/generated/graphql'
|
||||
|
||||
export type StreamAccessRequestGraphQLReturn = Omit<
|
||||
StreamAccessRequest,
|
||||
'requester' | 'stream'
|
||||
>
|
||||
@@ -0,0 +1,20 @@
|
||||
import { initializeEventListener } from '@/modules/accessrequests/services/eventListener'
|
||||
import { Optional, SpeckleModule } from '@/modules/shared/helpers/typeHelper'
|
||||
import { modulesDebug } from '@/modules/shared/utils/logger'
|
||||
|
||||
let quitListeners: Optional<() => void> = undefined
|
||||
|
||||
const ServerAccessRequestsModule: SpeckleModule = {
|
||||
init(_, isInitial) {
|
||||
modulesDebug('🔐 Init access request module')
|
||||
|
||||
if (isInitial) {
|
||||
quitListeners = initializeEventListener()
|
||||
}
|
||||
},
|
||||
shutdown() {
|
||||
quitListeners?.()
|
||||
}
|
||||
}
|
||||
|
||||
export = ServerAccessRequestsModule
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { Knex } from 'knex'
|
||||
|
||||
const TABLE_NAME = 'server_access_requests'
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.createTable(TABLE_NAME, (table) => {
|
||||
table.string('id', 10)
|
||||
table
|
||||
.string('requesterId', 10)
|
||||
.references('id')
|
||||
.inTable('users')
|
||||
.onDelete('cascade')
|
||||
.notNullable()
|
||||
|
||||
table.string('resourceType').notNullable()
|
||||
table.string('resourceId').nullable()
|
||||
|
||||
table
|
||||
.timestamp('createdAt', { precision: 3, useTz: true })
|
||||
.defaultTo(knex.fn.now())
|
||||
.notNullable()
|
||||
table
|
||||
.timestamp('updatedAt', { precision: 3, useTz: true })
|
||||
.defaultTo(knex.fn.now())
|
||||
.notNullable()
|
||||
|
||||
table.primary(['id'])
|
||||
table.index(['resourceType', 'resourceId'])
|
||||
table.unique(['requesterId', 'resourceId', 'resourceType'])
|
||||
})
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TABLE_NAME)
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { ServerAccessRequests, Streams } from '@/modules/core/dbSchema'
|
||||
import { InvalidArgumentError } from '@/modules/shared/errors'
|
||||
import { Nullable } from '@/modules/shared/helpers/typeHelper'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
|
||||
export enum AccessRequestType {
|
||||
Stream = 'stream'
|
||||
}
|
||||
|
||||
export type ServerAccessRequestRecord<
|
||||
T extends AccessRequestType = AccessRequestType,
|
||||
I extends Nullable<string> = Nullable<string>
|
||||
> = {
|
||||
id: string
|
||||
requesterId: string
|
||||
resourceType: T
|
||||
resourceId: I
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export type StreamAccessRequestRecord = ServerAccessRequestRecord<
|
||||
AccessRequestType.Stream,
|
||||
string
|
||||
>
|
||||
|
||||
export const isStreamAccessRequest = (
|
||||
req: ServerAccessRequestRecord
|
||||
): req is StreamAccessRequestRecord =>
|
||||
!!(req.resourceId && req.resourceType === AccessRequestType.Stream)
|
||||
|
||||
const baseQuery = <
|
||||
T extends AccessRequestType = AccessRequestType,
|
||||
I extends Nullable<string> = Nullable<string>
|
||||
>(
|
||||
resourceType?: T
|
||||
) => {
|
||||
const q = ServerAccessRequests.knex().select<ServerAccessRequestRecord<T, I>[]>(
|
||||
ServerAccessRequests.cols
|
||||
)
|
||||
|
||||
if (resourceType) {
|
||||
q.where(ServerAccessRequests.col.resourceType, resourceType)
|
||||
}
|
||||
|
||||
// validate that resourceId points to a valid resource
|
||||
if (resourceType) {
|
||||
switch (resourceType) {
|
||||
case AccessRequestType.Stream:
|
||||
q.innerJoin(Streams.name, Streams.col.id, ServerAccessRequests.col.resourceId)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return q
|
||||
}
|
||||
|
||||
export const generateId = () => cryptoRandomString({ length: 10 })
|
||||
|
||||
export async function getPendingAccessRequests<T extends AccessRequestType>(
|
||||
resourceType: T,
|
||||
resourceId: string
|
||||
) {
|
||||
if (!resourceId || !resourceType) {
|
||||
throw new InvalidArgumentError('Resource type and ID missing')
|
||||
}
|
||||
|
||||
const q = baseQuery<T, string>(resourceType)
|
||||
.andWhere(ServerAccessRequests.col.resourceId, resourceId)
|
||||
.orderBy(ServerAccessRequests.col.createdAt)
|
||||
|
||||
return await q
|
||||
}
|
||||
|
||||
export async function getPendingAccessRequest<
|
||||
T extends AccessRequestType = AccessRequestType
|
||||
>(requestId: string, resourceType?: T) {
|
||||
if (!requestId) {
|
||||
throw new InvalidArgumentError('Request ID missing')
|
||||
}
|
||||
|
||||
const q = baseQuery<T, string>(resourceType)
|
||||
.andWhere(ServerAccessRequests.col.id, requestId)
|
||||
.first()
|
||||
|
||||
return await q
|
||||
}
|
||||
|
||||
export async function deleteRequestById(requestId: string) {
|
||||
if (!requestId) {
|
||||
throw new InvalidArgumentError('Request ID missing')
|
||||
}
|
||||
|
||||
const q = await ServerAccessRequests.knex()
|
||||
.where(ServerAccessRequests.col.id, requestId)
|
||||
.del()
|
||||
return !!q
|
||||
}
|
||||
|
||||
type AccessRecordInput<
|
||||
T extends AccessRequestType = AccessRequestType,
|
||||
I extends Nullable<string> = Nullable<string>
|
||||
> = Omit<ServerAccessRequestRecord<T, I>, 'createdAt' | 'updatedAt'>
|
||||
|
||||
export async function createNewRequest<
|
||||
T extends AccessRequestType = AccessRequestType,
|
||||
I extends Nullable<string> = Nullable<string>
|
||||
>(input: AccessRecordInput<T, I>) {
|
||||
const results = await ServerAccessRequests.knex().insert<
|
||||
string,
|
||||
ServerAccessRequestRecord<T, I>[]
|
||||
>(input, ServerAccessRequests.cols)
|
||||
|
||||
return results[0]
|
||||
}
|
||||
|
||||
export async function getUsersPendingAccessRequest<
|
||||
T extends AccessRequestType = AccessRequestType,
|
||||
I extends Nullable<string> = Nullable<string>
|
||||
>(userId: string, resourceType: T, resourceId: I) {
|
||||
if (!userId || !resourceType) {
|
||||
throw new InvalidArgumentError('User ID or resource type missing')
|
||||
}
|
||||
|
||||
const q = baseQuery<T, I>(resourceType)
|
||||
.andWhere(ServerAccessRequests.col.requesterId, userId)
|
||||
.andWhere(ServerAccessRequests.col.resourceId, resourceId)
|
||||
.first()
|
||||
|
||||
return await q
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
AccessRequestsEmitter,
|
||||
AccessRequestsEvents,
|
||||
AccessRequestsEventsPayloads
|
||||
} from '@/modules/accessrequests/events/emitter'
|
||||
import { isStreamAccessRequest } from '@/modules/accessrequests/repositories'
|
||||
import { Roles } from '@/modules/core/helpers/mainConstants'
|
||||
import { getStreamCollaborators } from '@/modules/core/repositories/streams'
|
||||
import { NotificationType } from '@/modules/notifications/helpers/types'
|
||||
import { publishNotification } from '@/modules/notifications/services/publication'
|
||||
|
||||
async function onServerAccessRequestCreated(
|
||||
payload: AccessRequestsEventsPayloads[AccessRequestsEvents.Created]
|
||||
) {
|
||||
const { request } = payload
|
||||
|
||||
// Send out email to all owners of the stream
|
||||
if (isStreamAccessRequest(request)) {
|
||||
const owners = await getStreamCollaborators(request.resourceId, Roles.Stream.Owner)
|
||||
await Promise.all(
|
||||
owners.map((o) =>
|
||||
publishNotification(NotificationType.NewStreamAccessRequest, {
|
||||
targetUserId: o.id,
|
||||
data: {
|
||||
requestId: request.id
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function onServerAccessRequestFinalized(
|
||||
payload: AccessRequestsEventsPayloads[AccessRequestsEvents.Finalized]
|
||||
) {
|
||||
const { approved, request, finalizedBy } = payload
|
||||
|
||||
// Send out email to requester, if accepted
|
||||
if (approved && isStreamAccessRequest(request)) {
|
||||
await publishNotification(NotificationType.StreamAccessRequestApproved, {
|
||||
targetUserId: request.requesterId,
|
||||
data: {
|
||||
request,
|
||||
finalizedBy
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize event listener for tracking various Speckle events
|
||||
*/
|
||||
export function initializeEventListener() {
|
||||
const quitCbs = [
|
||||
AccessRequestsEmitter.listen(
|
||||
AccessRequestsEvents.Created,
|
||||
onServerAccessRequestCreated
|
||||
),
|
||||
AccessRequestsEmitter.listen(
|
||||
AccessRequestsEvents.Finalized,
|
||||
onServerAccessRequestFinalized
|
||||
)
|
||||
]
|
||||
|
||||
return () => quitCbs.forEach((quit) => quit())
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import {
|
||||
AccessRequestCreationError,
|
||||
AccessRequestProcessingError
|
||||
} from '@/modules/accessrequests/errors'
|
||||
import { AccessRequestsEmitter } from '@/modules/accessrequests/events/emitter'
|
||||
import { StreamAccessRequestGraphQLReturn } from '@/modules/accessrequests/helpers/graphTypes'
|
||||
import {
|
||||
AccessRequestType,
|
||||
createNewRequest,
|
||||
deleteRequestById,
|
||||
generateId,
|
||||
getPendingAccessRequest,
|
||||
getPendingAccessRequests,
|
||||
getUsersPendingAccessRequest,
|
||||
ServerAccessRequestRecord
|
||||
} from '@/modules/accessrequests/repositories'
|
||||
import { StreamInvalidAccessError } from '@/modules/core/errors/stream'
|
||||
import { Roles, StreamRoles } from '@/modules/core/helpers/mainConstants'
|
||||
import { getStream } from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
addOrUpdateStreamCollaborator,
|
||||
validateStreamAccess
|
||||
} from '@/modules/core/services/streams/streamAccessService'
|
||||
import { ensureError } from '@/modules/shared/helpers/errorHelper'
|
||||
import { Nullable } from '@/modules/shared/helpers/typeHelper'
|
||||
|
||||
function buildStreamAccessRequestGraphQLReturn(
|
||||
record: ServerAccessRequestRecord<AccessRequestType.Stream, string>
|
||||
): StreamAccessRequestGraphQLReturn {
|
||||
return {
|
||||
id: record.id,
|
||||
requesterId: record.requesterId,
|
||||
streamId: record.resourceId,
|
||||
createdAt: record.createdAt
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUserStreamAccessRequest(
|
||||
userId: string,
|
||||
streamId: string
|
||||
): Promise<Nullable<StreamAccessRequestGraphQLReturn>> {
|
||||
const req = await getUsersPendingAccessRequest(
|
||||
userId,
|
||||
AccessRequestType.Stream,
|
||||
streamId
|
||||
)
|
||||
if (!req) return null
|
||||
|
||||
return buildStreamAccessRequestGraphQLReturn(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new stream access request
|
||||
*/
|
||||
export async function requestStreamAccess(userId: string, streamId: string) {
|
||||
const [stream, existingRequest] = await Promise.all([
|
||||
getStream({ userId, streamId }),
|
||||
getUserStreamAccessRequest(userId, streamId)
|
||||
])
|
||||
|
||||
if (existingRequest) {
|
||||
throw new AccessRequestCreationError(
|
||||
'User already has a pending access request for this stream'
|
||||
)
|
||||
}
|
||||
|
||||
if (!stream) {
|
||||
throw new AccessRequestCreationError(
|
||||
"Can't request access to a non-existant stream"
|
||||
)
|
||||
}
|
||||
|
||||
if (stream.role) {
|
||||
throw new AccessRequestCreationError(
|
||||
'User already has access to the specified stream'
|
||||
)
|
||||
}
|
||||
|
||||
const req = await createNewRequest<AccessRequestType.Stream, string>({
|
||||
id: generateId(),
|
||||
requesterId: userId,
|
||||
resourceType: AccessRequestType.Stream,
|
||||
resourceId: streamId
|
||||
})
|
||||
|
||||
await AccessRequestsEmitter.emit(AccessRequestsEmitter.events.Created, {
|
||||
request: req
|
||||
})
|
||||
|
||||
return buildStreamAccessRequestGraphQLReturn(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pending stream access requests
|
||||
*/
|
||||
export async function getPendingStreamRequests(
|
||||
streamId: string
|
||||
): Promise<StreamAccessRequestGraphQLReturn[]> {
|
||||
const reqs = await getPendingAccessRequests(AccessRequestType.Stream, streamId)
|
||||
return reqs.map(buildStreamAccessRequestGraphQLReturn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept or decline a pending access request
|
||||
*/
|
||||
export async function processPendingStreamRequest(
|
||||
userId: string,
|
||||
requestId: string,
|
||||
accept: boolean,
|
||||
role: StreamRoles = Roles.Stream.Contributor
|
||||
) {
|
||||
const req = await getPendingAccessRequest(requestId, AccessRequestType.Stream)
|
||||
if (!req) {
|
||||
throw new AccessRequestProcessingError('No request with this ID exists')
|
||||
}
|
||||
|
||||
try {
|
||||
await validateStreamAccess(userId, req.resourceId, Roles.Stream.Owner)
|
||||
} catch (e: unknown) {
|
||||
const err = ensureError(e, 'Stream access validation failed')
|
||||
if (err instanceof StreamInvalidAccessError) {
|
||||
throw new AccessRequestProcessingError(
|
||||
'You must own the stream to process access requests',
|
||||
{ cause: err }
|
||||
)
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
if (accept) {
|
||||
await addOrUpdateStreamCollaborator(req.resourceId, req.requesterId, role, userId)
|
||||
}
|
||||
|
||||
await deleteRequestById(req.id)
|
||||
|
||||
await AccessRequestsEmitter.emit(AccessRequestsEmitter.events.Finalized, {
|
||||
request: req,
|
||||
approved: accept ? { role } : undefined,
|
||||
finalizedBy: userId
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
import {
|
||||
deleteRequestById,
|
||||
getPendingAccessRequest
|
||||
} from '@/modules/accessrequests/repositories'
|
||||
import { requestStreamAccess } from '@/modules/accessrequests/services/stream'
|
||||
import { ActionTypes } from '@/modules/activitystream/services'
|
||||
import { ServerAccessRequests, StreamActivity } from '@/modules/core/dbSchema'
|
||||
import { mapStreamRoleToValue } from '@/modules/core/helpers/graphTypes'
|
||||
import { Roles } from '@/modules/core/helpers/mainConstants'
|
||||
import { getStreamCollaborators } from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
addOrUpdateStreamCollaborator,
|
||||
removeStreamCollaborator
|
||||
} from '@/modules/core/services/streams/streamAccessService'
|
||||
import { NotificationType } from '@/modules/notifications/helpers/types'
|
||||
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
|
||||
import {
|
||||
createStreamAccessRequest,
|
||||
getPendingStreamAccessRequests,
|
||||
getStreamAccessRequest,
|
||||
useStreamAccessRequest
|
||||
} from '@/test/graphql/accessRequests'
|
||||
import { StreamRole } from '@/test/graphql/generated/graphql'
|
||||
import { truncateTables } from '@/test/hooks'
|
||||
import { EmailSendingServiceMock } from '@/test/mocks/global'
|
||||
import {
|
||||
buildNotificationsStateTracker,
|
||||
NotificationsStateManager
|
||||
} from '@/test/notificationsHelper'
|
||||
import { buildAuthenticatedApolloServer } from '@/test/serverHelper'
|
||||
import { getStreamActivities } from '@/test/speckle-helpers/activityStreamHelper'
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
import { expect } from 'chai'
|
||||
import { noop } from 'lodash'
|
||||
|
||||
const createReqAndGetId = async (userId: string, streamId: string) => {
|
||||
const createReqRes = await requestStreamAccess(userId, streamId)
|
||||
return createReqRes.id
|
||||
}
|
||||
|
||||
describe('Stream access requests', () => {
|
||||
let apollo: ApolloServer
|
||||
let notificationsStateManager: NotificationsStateManager
|
||||
|
||||
const me: BasicTestUser = {
|
||||
name: 'hello itsa me',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const otherGuy: BasicTestUser = {
|
||||
name: 'and im the other guy, hi!',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const anotherGuy: BasicTestUser = {
|
||||
name: 'and im another guy lol',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const otherGuysPrivateStream: BasicTestStream = {
|
||||
name: 'other guys test stream #1',
|
||||
isPublic: false,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const otherGuysPublicStream: BasicTestStream = {
|
||||
name: 'other guys public test stream #2',
|
||||
isPublic: true,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const myPrivateStream: BasicTestStream = {
|
||||
name: 'this is my private stream #1',
|
||||
isPublic: false,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
await createTestUsers([me, otherGuy, anotherGuy])
|
||||
await createTestStreams([
|
||||
[otherGuysPrivateStream, otherGuy],
|
||||
[otherGuysPublicStream, otherGuy],
|
||||
[myPrivateStream, me]
|
||||
])
|
||||
apollo = buildAuthenticatedApolloServer(me.id)
|
||||
notificationsStateManager = buildNotificationsStateTracker()
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
notificationsStateManager.destroy()
|
||||
})
|
||||
|
||||
const createReq = (streamId: string) =>
|
||||
createStreamAccessRequest(apollo, { streamId })
|
||||
|
||||
const getReq = (streamId: string) => getStreamAccessRequest(apollo, { streamId })
|
||||
|
||||
const getStreamReqs = (streamId: string) =>
|
||||
getPendingStreamAccessRequests(apollo, { streamId })
|
||||
|
||||
const useReq = (
|
||||
requestId: string,
|
||||
accept: boolean,
|
||||
role: StreamRole = StreamRole.StreamContributor
|
||||
) => useStreamAccessRequest(apollo, { requestId, accept, role })
|
||||
|
||||
describe('when being created', () => {
|
||||
beforeEach(async () => {
|
||||
await truncateTables([ServerAccessRequests.name, StreamActivity.name])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Ensure me doesnt have any roles on stream1
|
||||
await removeStreamCollaborator(otherGuysPrivateStream.id, me.id, me.id).catch(
|
||||
noop
|
||||
)
|
||||
})
|
||||
|
||||
it('operation succeeds', async () => {
|
||||
const sendEmailCall = EmailSendingServiceMock.hijackFunction(
|
||||
'sendEmail',
|
||||
async () => true
|
||||
)
|
||||
|
||||
const results = await createReq(otherGuysPrivateStream.id)
|
||||
|
||||
// req gets created
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
expect(results.data?.streamAccessRequestCreate.id).to.be.ok
|
||||
expect(results.data?.streamAccessRequestCreate?.createdAt).to.be.ok
|
||||
expect(results.data?.streamAccessRequestCreate?.requesterId).to.be.ok
|
||||
expect(results.data?.streamAccessRequestCreate?.requester.id).to.eq(
|
||||
results.data?.streamAccessRequestCreate?.requesterId
|
||||
)
|
||||
expect(results.data?.streamAccessRequestCreate.streamId).to.be.ok
|
||||
|
||||
await notificationsStateManager.waitForAck(
|
||||
(e) => e.result?.type === NotificationType.NewStreamAccessRequest
|
||||
)
|
||||
|
||||
// email gets sent out
|
||||
expect(sendEmailCall.args?.[0]?.[0]).to.be.ok
|
||||
const emailParams = sendEmailCall.args[0][0]
|
||||
|
||||
expect(emailParams.subject).to.contain('A user requested access to your stream')
|
||||
expect(emailParams.html).to.be.ok
|
||||
expect(emailParams.text).to.be.ok
|
||||
expect(emailParams.to).to.eq(otherGuy.email)
|
||||
|
||||
// activity stream item inserted
|
||||
const streamActivity = await getStreamActivities(otherGuysPrivateStream.id, {
|
||||
actionType: ActionTypes.Stream.AccessRequestSent,
|
||||
userId: me.id
|
||||
})
|
||||
expect(streamActivity).to.have.lengthOf(1)
|
||||
})
|
||||
|
||||
it('operation fails if request already exists', async () => {
|
||||
const firstResults = await createReq(otherGuysPrivateStream.id)
|
||||
expect(firstResults).to.not.haveGraphQLErrors()
|
||||
expect(firstResults.data?.streamAccessRequestCreate.id).to.be.ok
|
||||
|
||||
const secondResults = await createReq(otherGuysPrivateStream.id)
|
||||
expect(secondResults).to.haveGraphQLErrors('already has a pending access request')
|
||||
expect(secondResults.data?.streamAccessRequestCreate.id).to.be.not.ok
|
||||
})
|
||||
|
||||
it('operation fails if stream is nonexistant', async () => {
|
||||
const secondResults = await createReq('abcdef123')
|
||||
expect(secondResults).to.haveGraphQLErrors('non-existant stream')
|
||||
expect(secondResults.data?.streamAccessRequestCreate.id).to.be.not.ok
|
||||
})
|
||||
|
||||
it('operation fails if user already has a role on the stream', async () => {
|
||||
await addOrUpdateStreamCollaborator(
|
||||
otherGuysPrivateStream.id,
|
||||
me.id,
|
||||
Roles.Stream.Contributor,
|
||||
otherGuy.id
|
||||
)
|
||||
|
||||
const secondResults = await createReq(otherGuysPrivateStream.id)
|
||||
expect(secondResults).to.haveGraphQLErrors('user already has access')
|
||||
expect(secondResults.data?.streamAccessRequestCreate.id).to.be.not.ok
|
||||
})
|
||||
})
|
||||
|
||||
describe('when being read', () => {
|
||||
let myRequestId: string
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let anotherGuysRequestId: string
|
||||
|
||||
beforeEach(async () => {
|
||||
await truncateTables([ServerAccessRequests.name])
|
||||
|
||||
const [myNewReqId, anotherGuysNewReqId] = await Promise.all([
|
||||
createReqAndGetId(me.id, otherGuysPrivateStream.id),
|
||||
createReqAndGetId(anotherGuy.id, otherGuysPrivateStream.id)
|
||||
])
|
||||
myRequestId = myNewReqId
|
||||
anotherGuysRequestId = anotherGuysNewReqId
|
||||
})
|
||||
|
||||
it('returns the request correctly', async () => {
|
||||
const results = await getReq(otherGuysPrivateStream.id)
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
expect(results.data?.streamAccessRequest?.id).to.eq(myRequestId)
|
||||
expect(results.data?.streamAccessRequest?.createdAt).to.be.ok
|
||||
expect(results.data?.streamAccessRequest?.requesterId).to.be.ok
|
||||
expect(results.data?.streamAccessRequest?.requester.id).to.eq(
|
||||
results.data?.streamAccessRequest?.requesterId
|
||||
)
|
||||
expect(results.data?.streamAccessRequest?.streamId).to.be.ok
|
||||
})
|
||||
|
||||
it('returns null if no req found', async () => {
|
||||
await deleteRequestById(myRequestId)
|
||||
|
||||
const results = await getReq(otherGuysPrivateStream.id)
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
expect(results.data?.streamAccessRequest).to.eq(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when being read from a stream', () => {
|
||||
before(async () => {
|
||||
await truncateTables([ServerAccessRequests.name])
|
||||
|
||||
await addOrUpdateStreamCollaborator(
|
||||
otherGuysPublicStream.id,
|
||||
me.id,
|
||||
Roles.Stream.Contributor,
|
||||
otherGuy.id
|
||||
)
|
||||
await Promise.all([
|
||||
createReqAndGetId(otherGuy.id, myPrivateStream.id),
|
||||
createReqAndGetId(anotherGuy.id, myPrivateStream.id)
|
||||
])
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await removeStreamCollaborator(otherGuysPublicStream.id, me.id, me.id).catch(noop)
|
||||
})
|
||||
|
||||
it(`operation fails if reading from a non-owned stream`, async () => {
|
||||
const results = await getStreamReqs(otherGuysPublicStream.id)
|
||||
expect(results).to.haveGraphQLErrors('not authorized')
|
||||
expect(results.data?.stream?.pendingAccessRequests).to.be.not.ok
|
||||
expect(results.data?.stream?.id).to.be.ok
|
||||
})
|
||||
|
||||
it('operation succeeds', async () => {
|
||||
const results = await getStreamReqs(myPrivateStream.id)
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
|
||||
expect(results.data?.stream?.pendingAccessRequests).to.have.lengthOf(2)
|
||||
|
||||
for (const pendingReq of results.data!.stream!.pendingAccessRequests!) {
|
||||
expect(pendingReq.id).to.be.ok
|
||||
expect(pendingReq.createdAt).to.be.ok
|
||||
expect(pendingReq.requesterId).to.be.ok
|
||||
expect(pendingReq.streamId).to.be.ok
|
||||
expect(pendingReq.stream.id).to.eq(results.data!.stream!.id)
|
||||
expect(pendingReq.requester.id).to.eq(pendingReq.requesterId)
|
||||
expect([otherGuy.id, anotherGuy.id].includes(pendingReq.requesterId)).to.be.true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('when being processed', () => {
|
||||
let validReqId: string
|
||||
|
||||
beforeEach(async () => {
|
||||
await truncateTables([ServerAccessRequests.name, StreamActivity.name])
|
||||
await removeStreamCollaborator(
|
||||
myPrivateStream.id,
|
||||
otherGuy.id,
|
||||
otherGuy.id
|
||||
).catch(noop)
|
||||
validReqId = await createReqAndGetId(otherGuy.id, myPrivateStream.id)
|
||||
})
|
||||
|
||||
it('processing fails when pointing to nonexistant req', async () => {
|
||||
const results = await useReq('abcd', true)
|
||||
expect(results).to.haveGraphQLErrors('no request with this id exists')
|
||||
expect(results.data?.streamAccessRequestUse).to.be.not.ok
|
||||
})
|
||||
|
||||
it('processing fails when pointing to a req the user doesnt have access to', async () => {
|
||||
const inaccessibleReqId = await createReqAndGetId(
|
||||
anotherGuy.id,
|
||||
otherGuysPrivateStream.id
|
||||
)
|
||||
|
||||
const results = await useReq(inaccessibleReqId, true)
|
||||
expect(results).to.haveGraphQLErrors('you must own the stream')
|
||||
expect(results.data?.streamAccessRequestUse).to.be.not.ok
|
||||
})
|
||||
|
||||
const validProcessingDataSet = [
|
||||
{ display: 'declining', accept: false },
|
||||
{ display: 'approving', accept: true },
|
||||
{
|
||||
display: 'approving with custom role',
|
||||
accept: true,
|
||||
role: StreamRole.StreamReviewer
|
||||
}
|
||||
]
|
||||
validProcessingDataSet.forEach(({ display, accept, role }) => {
|
||||
it(`${display} works`, async () => {
|
||||
const results = await useReq(validReqId, accept, role)
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
expect(results.data?.streamAccessRequestUse).to.be.ok
|
||||
|
||||
// req should be deleted
|
||||
const req = await getPendingAccessRequest(validReqId)
|
||||
expect(req).to.not.be.ok
|
||||
|
||||
// activity stream item should be inserted
|
||||
if (accept) {
|
||||
const streamActivity = await getStreamActivities(myPrivateStream.id, {
|
||||
actionType: ActionTypes.Stream.PermissionsAdd,
|
||||
userId: me.id
|
||||
})
|
||||
expect(streamActivity).to.have.lengthOf(1)
|
||||
|
||||
const collaborators = await getStreamCollaborators(myPrivateStream.id)
|
||||
const newCollaborator = collaborators.find((c) => c.id === otherGuy.id)
|
||||
|
||||
expect(newCollaborator).to.be.ok
|
||||
expect(newCollaborator?.role).to.eq(
|
||||
role ? mapStreamRoleToValue(role) : Roles.Stream.Contributor
|
||||
)
|
||||
} else {
|
||||
const streamActivity = await getStreamActivities(myPrivateStream.id, {
|
||||
actionType: ActionTypes.Stream.AccessRequestDeclined,
|
||||
userId: me.id
|
||||
})
|
||||
expect(streamActivity).to.have.lengthOf(1)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Nullable } from '@/modules/shared/helpers/typeHelper'
|
||||
|
||||
export type StreamActivityRecord = {
|
||||
streamId: Nullable<string>
|
||||
time: Date
|
||||
resourceType: Nullable<string>
|
||||
resourceId: Nullable<string>
|
||||
actionType: Nullable<string>
|
||||
userId: Nullable<string>
|
||||
info: Nullable<Record<string, unknown>>
|
||||
message: Nullable<string>
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { initializeEventListener } from '@/modules/activitystream/services/eventListener'
|
||||
import { SpeckleModule } from '@/modules/shared/helpers/typeHelper'
|
||||
import { modulesDebug } from '@/modules/shared/utils/logger'
|
||||
|
||||
const activityStreamModule: SpeckleModule = {
|
||||
init(_, isInitial) {
|
||||
modulesDebug('📅 Initializing activity-stream')
|
||||
|
||||
if (isInitial) {
|
||||
initializeEventListener()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export = {
|
||||
...activityStreamModule
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
ActionTypes,
|
||||
ResourceTypes,
|
||||
saveActivity
|
||||
} from '@/modules/activitystream/services'
|
||||
|
||||
/**
|
||||
* Save a "stream access requested" activity
|
||||
*/
|
||||
export async function addStreamAccessRequestedActivity(params: {
|
||||
streamId: string
|
||||
requesterId: string
|
||||
}) {
|
||||
const { streamId, requesterId } = params
|
||||
await saveActivity({
|
||||
streamId,
|
||||
resourceType: ResourceTypes.Stream,
|
||||
resourceId: streamId,
|
||||
userId: requesterId,
|
||||
actionType: ActionTypes.Stream.AccessRequestSent,
|
||||
message: `User ${requesterId} has requested access to stream ${streamId}`,
|
||||
info: { requesterId }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a "stream acccess request declined/denied" activity
|
||||
*/
|
||||
export async function addStreamAccessRequestDeclinedActivity(params: {
|
||||
streamId: string
|
||||
requesterId: string
|
||||
declinerId: string
|
||||
}) {
|
||||
const { streamId, requesterId, declinerId } = params
|
||||
await saveActivity({
|
||||
streamId,
|
||||
resourceType: ResourceTypes.Stream,
|
||||
resourceId: streamId,
|
||||
userId: declinerId,
|
||||
actionType: ActionTypes.Stream.AccessRequestDeclined,
|
||||
message: `User ${declinerId} declined access to stream ${streamId} for user ${requesterId}`,
|
||||
info: { requesterId, declinerId }
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
AccessRequestsEmitter,
|
||||
AccessRequestsEvents,
|
||||
AccessRequestsEventsPayloads
|
||||
} from '@/modules/accessrequests/events/emitter'
|
||||
import { AccessRequestType } from '@/modules/accessrequests/repositories'
|
||||
import { saveActivity } from '@/modules/activitystream/services'
|
||||
import {
|
||||
addStreamAccessRequestDeclinedActivity,
|
||||
addStreamAccessRequestedActivity
|
||||
} from '@/modules/activitystream/services/accessRequestActivity'
|
||||
import {
|
||||
UsersEmitter,
|
||||
UsersEvents,
|
||||
UsersEventsPayloads
|
||||
} from '@/modules/core/events/usersEmitter'
|
||||
|
||||
async function onUserCreated(payload: UsersEventsPayloads[UsersEvents.Created]) {
|
||||
const { user } = payload
|
||||
|
||||
await saveActivity({
|
||||
streamId: null,
|
||||
resourceType: 'user',
|
||||
resourceId: user.id,
|
||||
actionType: 'user_create',
|
||||
userId: user.id,
|
||||
info: { user },
|
||||
message: 'User created'
|
||||
})
|
||||
}
|
||||
|
||||
async function onServerAccessRequestCreated(
|
||||
payload: AccessRequestsEventsPayloads[AccessRequestsEvents.Created]
|
||||
) {
|
||||
const {
|
||||
request: { resourceId, resourceType, requesterId }
|
||||
} = payload
|
||||
if (!resourceId) return
|
||||
|
||||
if (resourceType === AccessRequestType.Stream) {
|
||||
await addStreamAccessRequestedActivity({
|
||||
streamId: resourceId,
|
||||
requesterId
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function onServerAccessRequestFinalized(
|
||||
payload: AccessRequestsEventsPayloads[AccessRequestsEvents.Finalized]
|
||||
) {
|
||||
const {
|
||||
approved,
|
||||
finalizedBy,
|
||||
request: { resourceId, resourceType, requesterId }
|
||||
} = payload
|
||||
if (!resourceId) return
|
||||
|
||||
if (resourceType === AccessRequestType.Stream) {
|
||||
// If user was added to stream, an activity stream item was already added from 'addOrUpdateStreamCollaborator'
|
||||
if (approved) return
|
||||
|
||||
await addStreamAccessRequestDeclinedActivity({
|
||||
streamId: resourceId,
|
||||
requesterId,
|
||||
declinerId: finalizedBy
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize event listener for tracking various Speckle events and responding
|
||||
* to them by creating activitystream entries
|
||||
*/
|
||||
export function initializeEventListener() {
|
||||
const quitCbs = [
|
||||
UsersEmitter.listen(UsersEvents.Created, onUserCreated),
|
||||
AccessRequestsEmitter.listen(
|
||||
AccessRequestsEvents.Created,
|
||||
onServerAccessRequestCreated
|
||||
),
|
||||
AccessRequestsEmitter.listen(
|
||||
AccessRequestsEvents.Finalized,
|
||||
onServerAccessRequestFinalized
|
||||
)
|
||||
]
|
||||
|
||||
return () => quitCbs.forEach((quit) => quit())
|
||||
}
|
||||
@@ -10,7 +10,8 @@ const ResourceTypes = Object.freeze({
|
||||
User: 'user',
|
||||
Stream: 'stream',
|
||||
Commit: 'commit',
|
||||
Branch: 'branch'
|
||||
Branch: 'branch',
|
||||
Comment: 'comment'
|
||||
})
|
||||
|
||||
const ActionTypes = Object.freeze({
|
||||
@@ -22,13 +23,22 @@ const ActionTypes = Object.freeze({
|
||||
Delete: 'stream_delete',
|
||||
Create: 'stream_create',
|
||||
InviteSent: 'stream_invite_sent',
|
||||
InviteDeclined: 'stream_invite_declined'
|
||||
InviteDeclined: 'stream_invite_declined',
|
||||
AccessRequestSent: 'stream_access_request_sent',
|
||||
AccessRequestDeclined: 'stream_access_request_declined'
|
||||
},
|
||||
Comment: {
|
||||
Mention: 'comment_mention'
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
ActionTypes,
|
||||
ResourceTypes,
|
||||
|
||||
/**
|
||||
* @param {Omit<import('@/modules/activitystream/helpers/types').StreamActivityRecord, "time">} param0
|
||||
*/
|
||||
async saveActivity({
|
||||
streamId,
|
||||
resourceType,
|
||||
|
||||
+57
-32
@@ -1,19 +1,21 @@
|
||||
const {
|
||||
import {
|
||||
saveActivity,
|
||||
ResourceTypes,
|
||||
ActionTypes
|
||||
} = require('@/modules/activitystream/services')
|
||||
const { pubsub, StreamPubsubEvents } = require('@/modules/shared')
|
||||
} from '@/modules/activitystream/services'
|
||||
import { StreamRoles } from '@/modules/core/helpers/mainConstants'
|
||||
import { pubsub, StreamPubsubEvents } from '@/modules/shared'
|
||||
|
||||
/**
|
||||
* Save "stream permissions granted to user" activity item
|
||||
*/
|
||||
async function addStreamPermissionsAddedActivity({
|
||||
streamId,
|
||||
activityUserId,
|
||||
targetUserId,
|
||||
role
|
||||
export async function addStreamPermissionsAddedActivity(params: {
|
||||
streamId: string
|
||||
activityUserId: string
|
||||
targetUserId: string
|
||||
role: StreamRoles
|
||||
}) {
|
||||
const { streamId, activityUserId, targetUserId, role } = params
|
||||
await Promise.all([
|
||||
saveActivity({
|
||||
streamId,
|
||||
@@ -37,12 +39,13 @@ async function addStreamPermissionsAddedActivity({
|
||||
/**
|
||||
* Save "user accepted stream invite" activity item
|
||||
*/
|
||||
async function addStreamInviteAcceptedActivity({
|
||||
streamId,
|
||||
inviteTargetId,
|
||||
inviterId,
|
||||
role
|
||||
export async function addStreamInviteAcceptedActivity(params: {
|
||||
streamId: string
|
||||
inviteTargetId: string
|
||||
inviterId: string
|
||||
role: StreamRoles
|
||||
}) {
|
||||
const { streamId, inviteTargetId, inviterId, role } = params
|
||||
await Promise.all([
|
||||
saveActivity({
|
||||
streamId,
|
||||
@@ -66,11 +69,12 @@ async function addStreamInviteAcceptedActivity({
|
||||
/**
|
||||
* Save "stream permissions revoked for user" activity item
|
||||
*/
|
||||
async function addStreamPermissionsRevokedActivity({
|
||||
streamId,
|
||||
activityUserId,
|
||||
removedUserId
|
||||
export async function addStreamPermissionsRevokedActivity(params: {
|
||||
streamId: string
|
||||
activityUserId: string
|
||||
removedUserId: string
|
||||
}) {
|
||||
const { streamId, activityUserId, removedUserId } = params
|
||||
const isVoluntaryLeave = activityUserId === removedUserId
|
||||
|
||||
await Promise.all([
|
||||
@@ -99,12 +103,13 @@ async function addStreamPermissionsRevokedActivity({
|
||||
/**
|
||||
* Save "user invited another user to stream" activity item
|
||||
*/
|
||||
async function addStreamInviteSentOutActivity({
|
||||
streamId,
|
||||
inviteTargetId,
|
||||
inviterId,
|
||||
inviteTargetEmail
|
||||
export async function addStreamInviteSentOutActivity(params: {
|
||||
streamId: string
|
||||
inviteTargetId: string
|
||||
inviterId: string
|
||||
inviteTargetEmail: string
|
||||
}) {
|
||||
const { streamId, inviteTargetId, inviterId, inviteTargetEmail } = params
|
||||
const targetDisplay = inviteTargetId || inviteTargetEmail
|
||||
|
||||
await saveActivity({
|
||||
@@ -121,11 +126,12 @@ async function addStreamInviteSentOutActivity({
|
||||
/**
|
||||
* Save "user declined an invite" activity item
|
||||
*/
|
||||
async function addStreamInviteDeclinedActivity({
|
||||
streamId,
|
||||
inviteTargetId,
|
||||
inviterId
|
||||
export async function addStreamInviteDeclinedActivity(params: {
|
||||
streamId: string
|
||||
inviteTargetId: string
|
||||
inviterId: string
|
||||
}) {
|
||||
const { streamId, inviteTargetId, inviterId } = params
|
||||
await saveActivity({
|
||||
streamId,
|
||||
resourceType: ResourceTypes.Stream,
|
||||
@@ -137,10 +143,29 @@ async function addStreamInviteDeclinedActivity({
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addStreamPermissionsAddedActivity,
|
||||
addStreamPermissionsRevokedActivity,
|
||||
addStreamInviteAcceptedActivity,
|
||||
addStreamInviteSentOutActivity,
|
||||
addStreamInviteDeclinedActivity
|
||||
/**
|
||||
* Save "user mentioned in stream comment" activity item
|
||||
*/
|
||||
export async function addStreamCommentMentionActivity(params: {
|
||||
streamId: string
|
||||
mentionAuthorId: string
|
||||
mentionTargetId: string
|
||||
commentId: string
|
||||
threadId: string
|
||||
}) {
|
||||
const { streamId, mentionAuthorId, mentionTargetId, commentId, threadId } = params
|
||||
await saveActivity({
|
||||
streamId,
|
||||
resourceType: ResourceTypes.Comment,
|
||||
resourceId: commentId,
|
||||
actionType: ActionTypes.Comment.Mention,
|
||||
userId: mentionAuthorId,
|
||||
message: `User ${mentionAuthorId} mentioned user ${mentionTargetId} in comment ${commentId}`,
|
||||
info: {
|
||||
mentionAuthorId,
|
||||
mentionTargetId,
|
||||
commentId,
|
||||
threadId
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { AuthorizationCodes, RefreshTokens, knex } from '@/modules/core/dbSchema'
|
||||
import { InvalidArgumentError } from '@/modules/shared/errors'
|
||||
import { Nullable } from '@/modules/shared/helpers/typeHelper'
|
||||
|
||||
export type RefreshTokenRecord = {
|
||||
id: string
|
||||
tokenDigest: string
|
||||
appId: string
|
||||
userId: string
|
||||
createdAt: string
|
||||
lifespan: number
|
||||
}
|
||||
|
||||
export type AuthorizationCodeRecord = {
|
||||
id: string
|
||||
appId: string
|
||||
userId: string
|
||||
challenge: string
|
||||
createdAt: string
|
||||
lifespan: number
|
||||
}
|
||||
|
||||
export type ApiTokenRecord = {
|
||||
id: string
|
||||
tokenDigest: string
|
||||
owner: string
|
||||
name: Nullable<string>
|
||||
lastChars: Nullable<string>
|
||||
revoked: boolean
|
||||
lifespan: number
|
||||
createdAt: string
|
||||
lastUsed: string
|
||||
}
|
||||
|
||||
export async function deleteExistingAuthTokens(userId: string) {
|
||||
if (!userId) throw new InvalidArgumentError('User ID must be set')
|
||||
|
||||
await RefreshTokens.knex().where(RefreshTokens.col.userId, userId)
|
||||
await AuthorizationCodes.knex().where(AuthorizationCodes.col.userId, userId)
|
||||
await knex.raw(
|
||||
`
|
||||
DELETE FROM api_tokens
|
||||
WHERE owner = ?
|
||||
AND id NOT IN (
|
||||
SELECT p."tokenId" FROM personal_api_tokens p WHERE p."userId" = ?
|
||||
)
|
||||
`,
|
||||
[userId, userId]
|
||||
)
|
||||
}
|
||||
@@ -36,10 +36,6 @@ module.exports = async (app) => {
|
||||
|
||||
req.session.challenge = req.query.challenge
|
||||
|
||||
if (req.query.suuid) {
|
||||
req.session.suuid = req.query.suuid
|
||||
}
|
||||
|
||||
const token = req.query.token || req.query.inviteId
|
||||
if (token) {
|
||||
req.session.token = token
|
||||
|
||||
@@ -58,8 +58,6 @@ module.exports = async (app, session, sessionStorage, finalizeAuth) => {
|
||||
name: req.user._json.name || req.user.displayName
|
||||
}
|
||||
|
||||
if (req.session.suuid) user.suuid = req.session.suuid
|
||||
|
||||
const existingUser = await getUserByEmail({ email: user.email })
|
||||
|
||||
if (existingUser && !existingUser.verified) {
|
||||
|
||||
@@ -42,8 +42,6 @@ module.exports = async (app, session, sessionStorage, finalizeAuth) => {
|
||||
|
||||
const user = { email, name, bio }
|
||||
|
||||
if (req.session.suuid) user.suuid = req.session.suuid
|
||||
|
||||
const existingUser = await getUserByEmail({ email: user.email })
|
||||
|
||||
if (existingUser && !existingUser.verified) {
|
||||
|
||||
@@ -39,8 +39,6 @@ module.exports = async (app, session, sessionStorage, finalizeAuth) => {
|
||||
|
||||
const user = { email, name, avatar: profile._json.picture }
|
||||
|
||||
if (req.session.suuid) user.suuid = req.session.suuid
|
||||
|
||||
const existingUser = await getUserByEmail({ email: user.email })
|
||||
|
||||
if (existingUser && !existingUser.verified) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user