11 Commits

Author SHA1 Message Date
Iain Sproat 303f3f5a20 chore(domains): speckle.xyz is replaced by app.speckle.systems 2024-07-18 13:03:39 +01:00
Oğuzhan Koral 3dad1a0849 Chore (FE2): CNX-9074 use new fe2 terminology persistently (#62)
* Make account servel url visible on snack bar

* Make FE2 terminology persistent, no switch anymore
2024-03-01 14:03:37 -06:00
Oğuzhan Koral e6d667c951 Feat(Notification): CNX-9017 raise warning with multi model urls (#61)
* Add global notification toast mechanism

* Warn user if url is multi model
2024-03-01 12:32:59 +03:00
connorivy 226693f82d Fix (Excel): CNX-9045 deserialize chunked data (#60)
* wip

* working

* remove weird change

---------

Co-authored-by: Connor Ivy <connor@speckle.systems>
2024-02-26 17:30:14 +03:00
Oğuzhan Koral 8caf0e5a77 Chore (UI): Increase branch limit to 100 2024-02-15 21:43:32 +03:00
Oğuzhan Koral 050aab39d0 Feat (FE2): CNX-8706 support fe2 urls in excel (#58)
* Fix create project

* Fetch 'x-speckle-frontend-2' header from server

* Change new dev default to app.speckle.systems

* Adjust stream wrapper according to frontend version

* Switch terminology according to server frontend

* Use state instead getter

* Try catch CORS issue and fallback to FE1

* Minor css
2024-02-06 12:36:47 -06:00
connorivy c7d7806a30 fix setting selectedHeaders variable for small commit objects (#57)
Co-authored-by: Connor Ivy <connor@speckle.systems>
2024-02-06 10:50:35 -06:00
connorivy a9cc5ed161 fix: CNX-7547 serialize get properties (#56)
* serialize get properties

* remove unwanted code segment

---------

Co-authored-by: Connor Ivy <connor@speckle.systems>
2024-01-29 14:53:10 +01:00
connorivy eeba4f860e Fix CNX-7547: size limit on data sending (#55)
* avoid giant post request

* add typescript

* add data retrieve to batch retrieve data from sheet

* upgrade node

* working for node 12

* creating closure table

* add key cleaning similar to c# sdk

---------

Co-authored-by: Connor Ivy <connor@speckle.systems>
2024-01-23 22:56:31 +01:00
connorivy 7aa91c2d86 fix partial receive (#54)
Co-authored-by: Connor Ivy <connor@speckle.systems>
2023-12-12 16:59:56 +01:00
Matteo Cominetti e9edf0fb17 Merge pull request #53 from specklesystems/dev
Various fixes
2023-08-01 14:05:54 +02:00
30 changed files with 1522 additions and 204 deletions
+9 -2
View File
@@ -1,4 +1,5 @@
{
"root": true,
"env": {
"browser": true,
"es2021": true,
@@ -9,12 +10,15 @@
"prettier",
"prettier/vue",
"plugin:prettier/recommended",
"eslint:recommended"
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "vue-eslint-parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module",
"parser": "@babel/eslint-parser"
"parser": "@typescript-eslint/parser"
},
"plugins": ["vue", "prettier"],
"rules": {
@@ -25,6 +29,9 @@
"after": true
}
],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-array-constructor": "off",
//"array-bracket-spacing": [2, "always"],
"block-spacing": [2, "always"],
"camelcase": [
+3
View File
@@ -0,0 +1,3 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
+1 -1
View File
@@ -19,7 +19,7 @@ Comprehensive developer and user documentation can be found in our:
For developing and debugging this connector you'll need to set up a Speckle App.
The server on which the app runs must be on `https`, so **do not use a local Speckle server** on `http://localhost:3000/` as it will not work.
You can use `https://latest.speckle.dev/` or `https://speckle.xyz/`.
You can use `https://latest.speckle.systems/` or `https://app.speckle.systems/`.
Now open up its frontend, and under your profile register a new app.
+403 -81
View File
@@ -4607,6 +4607,12 @@
"integrity": "sha1-YcyEaYSeW83QxwRBIiZcOc7BDPQ=",
"dev": true
},
"@types/crypto-js": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.1.tgz",
"integrity": "sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==",
"dev": true
},
"@types/express": {
"version": "4.17.11",
"resolved": "https://registry.npm.taobao.org/@types/express/download/@types/express-4.17.11.tgz",
@@ -4771,6 +4777,18 @@
"integrity": "sha1-5IbQ2XOW15vu3QpuM/RTT/a0lz4=",
"dev": true
},
"@types/office-js": {
"version": "1.0.364",
"resolved": "https://registry.npmjs.org/@types/office-js/-/office-js-1.0.364.tgz",
"integrity": "sha512-sEoI3Rhtyz83WjMo3GamshNU7/j8CNY/UVrSVr3n6yOTWo6eG94pZvT4f00g0bvOYlY7Wi+OTZWEqY+MtQ81MQ==",
"dev": true
},
"@types/office-runtime": {
"version": "1.0.35",
"resolved": "https://registry.npmjs.org/@types/office-runtime/-/office-runtime-1.0.35.tgz",
"integrity": "sha512-qrP3bkDNoPY6WZMTOoutFdkZHdK91OUC1/Ohzw94bE8OD8pVoPjHKSoYaS+NlTFyXH3SNKYmQU+E6o966zARIA==",
"dev": true
},
"@types/q": {
"version": "1.5.4",
"resolved": "https://registry.npm.taobao.org/@types/q/download/@types/q-1.5.4.tgz",
@@ -4906,6 +4924,297 @@
"integrity": "sha1-gIyfp+RRcnTtVV+hWPLeS09GjnE=",
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
"integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "4.33.0",
"@typescript-eslint/scope-manager": "4.33.0",
"debug": "^4.3.1",
"functional-red-black-tree": "^1.0.1",
"ignore": "^5.1.8",
"regexpp": "^3.1.0",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
},
"dependencies": {
"ignore": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
"integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
"dev": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
}
}
},
"@typescript-eslint/experimental-utils": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz",
"integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.7",
"@typescript-eslint/scope-manager": "4.33.0",
"@typescript-eslint/types": "4.33.0",
"@typescript-eslint/typescript-estree": "4.33.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"dependencies": {
"eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"dev": true,
"requires": {
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
}
},
"eslint-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^2.0.0"
}
},
"eslint-visitor-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true
}
}
},
"@typescript-eslint/parser": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz",
"integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "4.33.0",
"@typescript-eslint/types": "4.33.0",
"@typescript-eslint/typescript-estree": "4.33.0",
"debug": "^4.3.1"
}
},
"@typescript-eslint/scope-manager": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz",
"integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.33.0",
"@typescript-eslint/visitor-keys": "4.33.0"
}
},
"@typescript-eslint/types": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz",
"integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz",
"integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.33.0",
"@typescript-eslint/visitor-keys": "4.33.0",
"debug": "^4.3.1",
"globby": "^11.0.3",
"is-glob": "^4.0.1",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
},
"dependencies": {
"@nodelib/fs.stat": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true
},
"array-union": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dev": true,
"requires": {
"path-type": "^4.0.0"
}
},
"fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"dev": true,
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"requires": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.9",
"ignore": "^5.2.0",
"merge2": "^1.4.1",
"slash": "^3.0.0"
}
},
"ignore": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
"integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
"dev": true
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"requires": {
"braces": "^3.0.2",
"picomatch": "^2.3.1"
}
},
"path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true
},
"picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true
},
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"requires": {
"is-number": "^7.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
}
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz",
"integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.33.0",
"eslint-visitor-keys": "^2.0.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true
}
}
},
"@vue/babel-helper-vue-jsx-merge-props": {
"version": "1.2.1",
"resolved": "https://registry.npm.taobao.org/@vue/babel-helper-vue-jsx-merge-props/download/@vue/babel-helper-vue-jsx-merge-props-1.2.1.tgz",
@@ -5215,6 +5524,63 @@
"integrity": "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=",
"dev": true
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"ssri": {
"version": "8.0.1",
"resolved": "https://registry.npm.taobao.org/ssri/download/ssri-8.0.1.tgz?cache=0&sync_timestamp=1617826339976&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fssri%2Fdownload%2Fssri-8.0.1.tgz",
@@ -5223,6 +5589,28 @@
"requires": {
"minipass": "^3.1.1"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
}
}
},
@@ -18672,6 +19060,15 @@
"integrity": "sha1-zy04vcNKE0vK8QkcQfZhni9nLQA=",
"dev": true
},
"tsutils": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
},
"tty": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/tty/download/tty-1.0.1.tgz",
@@ -18739,6 +19136,12 @@
"is-typedarray": "^1.0.0"
}
},
"typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true
},
"ua-parser-js": {
"version": "0.7.28",
"resolved": "https://registry.npm.taobao.org/ua-parser-js/download/ua-parser-js-0.7.28.tgz?cache=0&sync_timestamp=1618065908450&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fua-parser-js%2Fdownload%2Fua-parser-js-0.7.28.tgz",
@@ -19669,87 +20072,6 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-mixpanel": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/vue-mixpanel/-/vue-mixpanel-1.0.7.tgz",
+12 -2
View File
@@ -4,9 +4,10 @@
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"compile": "tsc && prettier ./src/**/*.js --write",
"lint": "vue-cli-service lint",
"excel": "office-addin-debugging start public/manifest.xml",
"excel:web": "office-addin-debugging start public/manifest.xml web --document https://skfn3-my.sharepoint.com/:x:/r/personal/connorisadmin_skfn3_onmicrosoft_com/_layouts/15/Doc.aspx?sourcedoc=%7B46999CFC-9B5A-4716-9F71-FECAEA59A6E8%7D",
"excel:web": "office-addin-debugging start public/manifest.xml web --document https://v2k0p-my.sharepoint.com/:x:/r/personal/connor_v2k0p_onmicrosoft_com/_layouts/15/Doc.aspx?sourcedoc=%7B101920A4-0AEA-4E82-B0E5-36E3162F7E86%7D&file=signalisation-permanente.xlsx&action=default&mobileredirect=true&DefaultItemOpen=1&ct=1705007910553&wdOrigin=OFFICECOM-WEB.MAIN.REC&cid=634f97aa-c5a2-411a-a705-412dc57eaaf7&wdPreviousSessionSrc=HarmonyWeb&wdPreviousSession=96a3ad12-6cd0-44ee-bf8b-c523c706e44e",
"excel:web2": "office-addin-debugging start public/manifest.xml web --document https://teocomi-my.sharepoint.com/:x:/g/personal/teocomi_teocomi_onmicrosoft_com/EdxKPPFhnMdDoGclr4J-xJABLf-Ao6CJ902W95kcFPL2fA?e=mD0B8a",
"excel:prod": "office-addin-debugging start public/manifest-prod.xml",
"excel:web-prod": "office-addin-debugging start public/manifest-prod.xml web --document https://teocomi-my.sharepoint.com/:x:/g/personal/teocomi_teocomi_onmicrosoft_com/EdxKPPFhnMdDoGclr4J-xJAB7H6-TRJ5s5ZnXzVdrdFyUg?e=VAFmBN",
@@ -34,6 +35,11 @@
},
"devDependencies": {
"@babel/eslint-parser": "^7.14.3",
"@types/crypto-js": "^4.2.1",
"@types/office-js": "^1.0.364",
"@types/office-runtime": "^1.0.35",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"@vue/cli-plugin-babel": "~4.5.13",
"@vue/cli-plugin-eslint": "~4.5.13",
"@vue/cli-plugin-router": "^4.5.13",
@@ -52,6 +58,7 @@
"prettier": "^2.2.1",
"sass": "^1.32.0",
"sass-loader": "^10.0.0",
"typescript": "^4.9.5",
"vue-cli-plugin-apollo": "~0.22.2",
"vue-cli-plugin-vuetify": "~2.4.0",
"vue-template-compiler": "^2.6.11",
@@ -61,5 +68,8 @@
"> 1%",
"last 2 versions",
"not dead"
]
],
"engines": {
"node": ">=14.0.0 <15.0.0"
}
}
+2 -2
View File
@@ -22,8 +22,8 @@
<SupportUrl DefaultValue="https://speckle.guide/user/excel.html" />
<AppDomains>
<AppDomain>https://latest.speckle.dev</AppDomain>
<AppDomain>https://speckle.xyz</AppDomain>
<AppDomain>https://latest.speckle.systems</AppDomain>
<AppDomain>https://app.speckle.systems</AppDomain>
<AppDomain>https://localhost:3000</AppDomain>
</AppDomains>
+10 -5
View File
@@ -18,6 +18,7 @@
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>{{ user.name }}</v-list-item-title>
<v-list-item-subtitle class="smaller-text">{{ serverUrl }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
@@ -94,6 +95,7 @@
<span class="caption">CONNECTOR</span>
</v-btn>
</v-app-bar>
<global-toast />
<v-main :style="background">
<router-view />
@@ -105,13 +107,15 @@
const crypto = require('crypto')
export default {
name: 'App',
components: {},
components: {
GlobalToast: () => import('@/components/GlobalToast')
},
data: () => ({
drawer: null,
showSnackbar: false,
items: [
{
name: 'Streams',
name: 'Projects',
icon: '📃',
to: '/'
},
@@ -186,9 +190,6 @@ export default {
'@' +
crypto.createHash('md5').update(this.user.email.toLowerCase()).digest('hex').toUpperCase()
console.log(server_id)
console.log(distinct_id)
this.$mixpanel.register({ server_id: server_id, hostApp: 'excel', type: 'action' })
this.$mixpanel.identify(distinct_id)
@@ -311,4 +312,8 @@ export default {
opacity: 1;
transition: opacity 0.15s;
}
.smaller-text {
font-size: 0.7rem; /* Example: Adjust the size as needed */
}
</style>
+66
View File
@@ -0,0 +1,66 @@
<template>
<v-snackbar v-if="text" v-model="snack" app bottom :color="color">
<div v-for="(line, index) in text.split('\n')" :key="index">
{{ line }}
</div>
<template #action="{}">
<v-btn v-if="actionName" small outlined @click="openUrl(url)" @click:append="snack = false">
{{ actionName }}
</v-btn>
<v-btn small icon @click="snack = false">
<v-icon small>mdi-close</v-icon>
</v-btn>
</template>
</v-snackbar>
</template>
<script>
export default {
data() {
return {
snack: false,
color: 'primary',
text: null,
actionName: null,
url: null
}
},
watch: {
snack(newVal) {
if (!newVal) {
this.text = null
this.actionName = null
this.url = null
}
}
},
mounted() {
this.$eventHub.$on('success', (args) => {
this.snack = true
this.color = 'green'
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
})
this.$eventHub.$on('notification', (args) => {
this.snack = true
this.color = 'primary'
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
})
this.$eventHub.$on('error', (args) => {
this.snack = true
this.color = '#CC3300'
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
})
},
methods: {
openUrl(link) {
this.$mixpanel.track('Connector Action', { name: 'Open In Web' })
window.open(link)
}
}
}
</script>
+5 -11
View File
@@ -1,8 +1,8 @@
<template>
<v-card class="pa-5 mb-3" style="transition: all 0.2s" @click="openStream">
<v-card class="px-5 py-2 mb-3" style="transition: all 0.2s" @click="openStream">
<v-row>
<v-col cols="12" sm="8" class="align-self-center">
<div class="subtitle-1">
<div class="text-h6">
{{ stream.name }}
</div>
<div class="caption mb-2 mt-1 text-truncate">
@@ -19,11 +19,7 @@
{{ role }}
</v-chip>
<v-chip
v-tooltip="
stream.branches.totalCount +
' branch' +
(stream.branches.totalCount === 1 ? '' : 'es')
"
v-tooltip="`${stream.branches.totalCount === 1 ? '1 model' : 'models'}`"
outlined
class="ml-2"
small
@@ -33,9 +29,7 @@
</v-chip>
<v-chip
v-tooltip="
stream.commits.totalCount + ' commit' + (stream.commits.totalCount === 1 ? '' : 's')
"
v-tooltip="`${stream.commits.totalCount === 1 ? '1 version' : 'versions'}`"
outlined
class="ml-2"
small
@@ -45,7 +39,7 @@
</v-chip>
</div>
</v-col>
<v-col cols="12" sm="4" class="text-sm-center text-md-right align-self-center">
<v-col cols="12" sm="4" class="text-sm-center text-md-right align-self-center pt-0">
<div>
<span v-for="user in collaboratorsSlice" :key="user.id">
<user-avatar
+228
View File
@@ -0,0 +1,228 @@
<template>
<v-card :class="`my-1 mb-0 pa-0 pb-2 ${localExpand ? 'elevation-3' : 'elevation-0'} my-0`">
<v-card-title>
<v-chip @click="toggleLoadExpand">
<v-icon small class="mr-2">mdi-code-array</v-icon>
{{ keyName }}
<span class="caption ml-2">List ( >{{ (value.length - 1) * 5000 }} elements)</span>
<v-icon class="ml-2" small>
{{ localExpand ? 'mdi-minus' : 'mdi-plus' }}
</v-icon>
</v-chip>
<v-dialog v-model="progress" persistent>
<v-card class="pt-3">
<v-card-text class="caption">
Receiving data from the Speckleverse...
<v-progress-linear
class="mt-2"
:value="`${(numChunksReceived / value.length) * 100}`"
color="primary"
></v-progress-linear>
<v-btn class="mt-3" outlined x-small color="primary" @click="cancel">Cancel</v-btn>
</v-card-text>
</v-card>
</v-dialog>
<v-btn icon small @click="bake">
<v-icon small>mdi-download</v-icon>
</v-btn>
</v-card-title>
<v-card-text v-if="localExpand" class="pb-0 pr-0 pl-3">
<component
:is="entry.type"
v-for="(entry, index) in rangeEntries"
:key="index"
:key-name="entry.key"
:full-key-name="fullKeyName ? `${fullKeyName}.${entry.key}` : entry.key"
:value="entry.value"
:stream-id="streamId"
:commit-id="commitId"
:commit-msg="commitMsg"
:nearest-object-id="nearestObjectId"
:path-from-nearest-object="`${entry.pathFromNearestObject}`"
></component>
</v-card-text>
<v-card-text v-if="localExpand && currentLimit < value.length">
<v-btn small @click="loadMore">Show more</v-btn>
</v-card-text>
<filter-modal ref="modal" />
</v-card>
</template>
<script>
import { bake } from '../plugins/excel'
import objectQuery from '../graphql/object.gql'
import { createClient } from '../vue-apollo'
let ac = new AbortController()
export default {
name: 'ObjectChunkedListViewer',
components: {
FilterModal: () => import('./FilterModal'),
ObjectSpeckleViewer: () => import('./ObjectSpeckleViewer'),
ObjectSimpleViewer: () => import('./ObjectSimpleViewer'),
ObjectValueViewer: () => import('./ObjectValueViewer')
},
props: {
value: {
type: Array,
default: () => []
},
keyName: {
type: String,
default: null
},
fullKeyName: {
type: String,
default: null
},
streamId: {
type: String,
default: null
},
commitId: {
type: String,
default: null
},
commitMsg: {
type: String,
default: null
},
nearestObjectId: {
type: String,
default: null
},
pathFromNearestObject: {
type: String,
default: null
}
},
data() {
return {
localExpand: false,
itemsPerLoad: 3,
currentLimit: 3,
progress: false,
numChunksReceived: 0
}
},
computed: {
rangeEntries() {
let arr = []
let index = 0
const delimiter = ':::'
for (let val of this.range) {
index++
if (Array.isArray(val)) {
arr.push({
key: `${index}`,
value: val,
type: 'ObjectChunkedListViewer',
pathFromNearestObject: this.pathFromNearestObject
? this.pathFromNearestObject + (index - 1) + delimiter
: index - 1 + delimiter
})
} else if (typeof val === 'object' && val !== null) {
if (val.speckle_type && val.speckle_type === 'reference') {
arr.push({
key: `${index}`,
value: val,
type: 'ObjectSpeckleViewer'
})
} else {
arr.push({
key: `${index}`,
value: val,
type: 'ObjectSimpleViewer'
})
}
} else {
arr.push({
key: `${index}`,
value: val,
type: 'ObjectValueViewer'
})
}
}
arr.sort((a, b) => {
if (a.type === b.type) return 0
if (a.type === 'ObjectValueViewer') return -1
return 0
})
return arr
},
range() {
return this.value.slice(0, this.currentLimit)
}
},
methods: {
toggleLoadExpand() {
this.localExpand = !this.localExpand
},
loadMore() {
this.currentLimit += this.itemsPerLoad
},
cancel() {
ac.abort()
},
async bake() {
ac = new AbortController()
this.progress = true
this.$mixpanel.track('Receive')
let allData = []
for (let i = 0; i < this.value.length; i++) {
if (ac.signal.aborted) {
break
}
let speckleObj = await this.getObjectFromCurrentStreamWithId(this.value[i].referencedId)
let speckleType = speckleObj?.data?.stream?.object?.data?.speckle_type
if (speckleType !== 'Speckle.Core.Models.DataChunk') {
continue
}
allData.push(...speckleObj.data.stream.object.data.data)
this.numChunksReceived++
}
let receiverSelection = null
if (!ac.signal.aborted) {
receiverSelection = await bake(
allData,
this.streamId,
this.commitId,
this.commitMsg,
this.$refs.modal,
ac.signal,
this.nearestObjectId,
this.pathFromNearestObject
)
}
if (receiverSelection) {
receiverSelection.fullKeyName = this.fullKeyName
this.$store.dispatch('setReceiverSelection', {
id: this.streamId,
receiverSelection: receiverSelection
})
}
this.progress = false
this.numChunksReceived = 0
},
async getObjectFromCurrentStreamWithId(id) {
let client = createClient()
return await client.query({
query: objectQuery,
variables: {
streamId: this.streamId,
id: id
}
})
}
}
}
</script>
+1 -2
View File
@@ -55,8 +55,7 @@ export default {
},
props: {
value: {
type: Object,
default: () => {}
type: Object
},
streamId: {
type: String,
+37 -12
View File
@@ -60,6 +60,7 @@ export default {
components: {
FilterModal: () => import('./FilterModal'),
ObjectListViewer: () => import('./ObjectListViewer'),
ObjectChunkedListViewer: () => import('./ObjectChunkedListViewer'),
ObjectSimpleViewer: () => import('./ObjectSimpleViewer'),
ObjectValueViewer: () => import('./ObjectValueViewer')
},
@@ -123,8 +124,8 @@ export default {
id: this.value.referencedId
}
},
result() {
this.objectEntries = this.getObjectEntries()
async result() {
this.objectEntries = await this.getObjectEntries()
},
skip() {
return !this.localExpand
@@ -145,14 +146,12 @@ export default {
cancel() {
ac.abort()
},
getObjectEntries() {
async getObjectEntries() {
if (!this.object) return []
let entries = Object.entries(this.object.data)
let arr = []
this.updatedObjectId = this.object.data.id ?? this.nearestObjectId
const delimiter = ':::'
console.log(this.updatedObjectId, delimiter)
console.log(this.nearestObjectId)
for (let [key, val] of entries) {
let name = key
if (key.startsWith('__')) continue
@@ -161,13 +160,29 @@ export default {
if (key === 'speckle_type') name = 'speckle type'
if (Array.isArray(val)) {
arr.push({
key,
name,
value: val,
type: 'ObjectListViewer',
pathFromNearestObject: key + delimiter
})
let speckleTypeOfFirstObj = null
if (val.length > 0 && val[0].referencedId) {
let firstObj = await this.getObjectFromCurrentStreamWithId(val[0].referencedId)
speckleTypeOfFirstObj = firstObj?.data?.stream?.object?.data?.speckle_type
}
if (speckleTypeOfFirstObj === 'Speckle.Core.Models.DataChunk') {
arr.push({
key,
name,
value: val,
type: 'ObjectChunkedListViewer',
pathFromNearestObject: key + delimiter
})
} else {
arr.push({
key,
name,
value: val,
type: 'ObjectListViewer',
pathFromNearestObject: key + delimiter
})
}
} else if (typeof val === 'object' && val !== null) {
if (val.speckle_type && val.speckle_type === 'reference') {
arr.push({
@@ -236,6 +251,16 @@ export default {
}
this.progress = false
},
async getObjectFromCurrentStreamWithId(id) {
let client = createClient()
return await client.query({
query: objectQuery,
variables: {
streamId: this.streamId,
id: id
}
})
}
}
}
+12 -1
View File
@@ -12,7 +12,7 @@
small
icon
color="primary"
:href="`${serverUrl}/streams/${stream.id}/branches/${selectedBranch.name}`"
:href="commitViewUrl()"
target="_blank"
>
<v-icon small>mdi-open-in-new</v-icon>
@@ -313,6 +313,17 @@ export default {
})
},
methods: {
commitViewUrl() {
if (this.$store.state.isFE2) {
if (this.selectedCommit && this.selectedCommit.id) {
return `${this.serverUrl}/projects/${this.stream.id}/models/${this.selectedBranch.id}@${this.selectedCommit.id}`
} else {
return `${this.serverUrl}/projects/${this.stream.id}/models/${this.selectedBranch.id}`
}
} else {
return `${this.serverUrl}/streams/${this.stream.id}/branches/${this.selectedBranch.name}`
}
},
swapReceiver() {
this.isReceiver = !this.isReceiver
this.$emit('loadByCommitId', this.selectedCommitId)
@@ -14,7 +14,9 @@
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5 mb-1">Create a New Branch</v-card-title>
<v-card-title class="text-h5 mb-1">
{{ `Create a New Model` }}
</v-card-title>
<v-card-subtitle class="py-0 my-0 font-italic">under {{ streamName }} stream</v-card-subtitle>
<v-container class="px-6" pb-0>
<v-text-field
@@ -48,6 +50,7 @@
<script>
import gql from 'graphql-tag'
import { createClient } from '../../vue-apollo'
// import { bus } from '@/main'
export default {
name: 'CreateBranchDialog',
@@ -66,7 +69,7 @@ export default {
showCreateBranch: false,
branchName: '',
description: '',
defaultDescription: 'Stream created from SketchUp',
defaultDescription: 'Stream created from Excel',
accountToCreateStream: null
}
},
@@ -76,6 +79,9 @@ export default {
return this.$store.getters.isAuthenticated
}
},
apollo: {
client: createClient()
},
methods: {
async createBranch() {
let res = await this.$apollo.mutate({
+33 -14
View File
@@ -1,18 +1,20 @@
<template>
<v-container fluid class="px-1 pb-0 pt-1">
<v-row>
<v-col class="center-content">
<v-row class="px-3 py-0">
<v-col class="d-flex justify-center pb-0">
<!-- DIALOG: Create New Stream -->
<v-dialog v-model="showCreateNewStream">
<template #activator="{ on, attrs }">
<v-btn class="ma-2 pa-3" x-small v-bind="attrs" v-on="on">
<v-btn block class="pa-3" small v-bind="attrs" v-on="on">
<v-icon dark left>mdi-plus-circle</v-icon>
Create New Stream
{{ `Create New Project` }}
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5">Create a New Stream</v-card-title>
<v-card-title class="text-h5">
{{ `Create New Project` }}
</v-card-title>
<v-container class="px-6" pb-0>
<v-text-field
v-model="streamName"
@@ -20,7 +22,7 @@
hide-details
dense
flat
placeholder="Stream Name (Optional)"
:placeholder="`Project Name (Optional)`"
/>
<v-text-field
v-model="description"
@@ -30,7 +32,7 @@
flat
placeholder="Description (Optional)"
/>
<v-switch v-model="privateStream" :label="'Private Stream'"></v-switch>
<v-switch v-model="privateStream" label="Private Project"></v-switch>
</v-container>
<v-card-actions>
@@ -40,19 +42,24 @@
</v-card-actions>
</v-card>
</v-dialog>
</v-col>
<v-col class="d-flex justify-center">
<!-- DIALOG: Add a Stream by ID or URL -->
<v-dialog v-model="showCreateStreamById">
<template #activator="{ on, attrs }">
<v-btn class="ma-2 pa-3" x-small min-width="163" v-bind="attrs" v-on="on">
<v-btn block class="pa-3" small min-width="163" v-bind="attrs" v-on="on">
<v-icon dark left>mdi-link-plus</v-icon>
Add By ID or URL
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5">Add a Stream by ID or URL</v-card-title>
<v-card-text>Stream IDs and Stream/Branch/Commit URLs are supported.</v-card-text>
<v-card-title class="text-h5">
{{ `Add a Project by ID or URL` }}
</v-card-title>
<v-card-text>
{{ 'Project IDs and Project/Model/Version URLs are supported.' }}
</v-card-text>
<v-container class="px-6">
<v-text-field
v-model="createStreamByIdText"
@@ -60,7 +67,7 @@
hide-details
dense
flat
placeholder="Stream URL"
:placeholder="'Project URL'"
/>
</v-container>
<v-card-actions>
@@ -85,6 +92,7 @@
<script>
import gql from 'graphql-tag'
import { StreamWrapper } from '@/utils/streamWrapper'
import { createClient } from '../../vue-apollo'
export default {
name: 'CreateStreamDialog',
@@ -117,14 +125,26 @@ export default {
return this.$store.getters.isAuthenticated
}
},
apollo: {
$client: createClient()
},
methods: {
async getStream() {
try {
const streamWrapper = new StreamWrapper(
this.createStreamByIdText,
this.accountId,
this.serverUrl
this.serverUrl,
this.$store.state.isFE2
)
const match = streamWrapper.matchUrl(this.createStreamByIdText)
if (match.groups.additionalModels !== undefined) {
this.$eventHub.$emit('error', {
text:
'Multi-model URLs are not supported!\nTry to select just one single model in the web app and paste that in.'
})
return
}
this.$router.push(`/streams/${streamWrapper.streamId}/${streamWrapper.commitId}`)
} catch (e) {
console.log(e)
@@ -150,7 +170,6 @@ export default {
this.streamName = ''
this.description = ''
this.$mixpanel.track('Connector Action', { name: 'Create Stream' })
this.refresh()
return res
}
}
+2 -2
View File
@@ -1,9 +1,9 @@
query Stream($id: String!) {
query Stream($id: String!, $limit: Int!) {
stream(id: $id) {
id
name
role
branches {
branches (limit: $limit) {
totalCount
items {
id
+2
View File
@@ -6,6 +6,8 @@ import { apolloProvider } from './vue-apollo'
import vuetify from './plugins/vuetify'
import '@mdi/font/css/materialdesignicons.css'
Vue.prototype.$eventHub = new Vue()
Vue.config.productionTip = false
import VueTimeago from 'vue-timeago'
+137
View File
@@ -0,0 +1,137 @@
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
exports.SerializedBase = exports.ObjectReference = exports.DataChunk = exports.BaseObjectSerializer = void 0
/* eslint-disable @typescript-eslint/ban-types */
const crypto_js_1 = require('crypto-js')
/**
* Serializer for Speckle objects written in Typescript
*/
class BaseObjectSerializer {
constructor(transports) {
this.transports = transports
}
async SerializeBase(object) {
return await this.SerializeBaseWithClosures(object, [])
}
async SerializeBaseWithClosures(object, closures) {
const thisClosure = new Map()
closures.push(thisClosure)
const converted = await this.PreserializeEachObjectProperty(object, closures)
let json = this.SerializeMap(converted)
const id = this.GetId(json)
converted.set('id', id)
this.AddSelfToParentClosures(id, closures)
if (thisClosure.size > 0) {
converted.set('__closure', Object.fromEntries(thisClosure))
}
converted.set('totalChildrenCount', thisClosure.size)
json = this.SerializeMap(converted)
await this.StoreObject(id, converted)
return new SerializedBase(id, json)
}
async PreserializeObject(object, closures) {
if (!(object instanceof Object) || object instanceof String) {
return object
}
if (object instanceof DataChunk) {
const serialized = await this.SerializeBaseWithClosures(object, [...closures])
return new ObjectReference(serialized.id)
}
if (object instanceof Array) {
// chunk array into 5000 by default
const chunkSize = 5000
if (object.length > chunkSize) {
let serializedCount = 0
const data = new Array()
while (serializedCount < object.length) {
const dataChunkCount = Math.min(chunkSize, object.length - serializedCount)
data.push(new DataChunk(object.slice(serializedCount, serializedCount + dataChunkCount)))
serializedCount += dataChunkCount
}
return await this.PreserializeObject(data, closures)
}
const convertedList = new Array()
for (const element of object) {
convertedList.push(await this.PreserializeObject(element, closures))
}
return convertedList
}
if (object instanceof Object) {
return Object.fromEntries(await this.PreserializeEachObjectProperty(object, closures))
}
throw new Error(`Cannot serialize object ${object}`)
}
async PreserializeEachObjectProperty(o, closures) {
const converted = new Map()
const getters = Object.entries(Object.getOwnPropertyDescriptors(Reflect.getPrototypeOf(o)))
.filter(([key, descriptor]) => typeof descriptor.get === 'function' && key !== '__proto__')
.map(([key]) => key)
const objectKeys = new Array()
objectKeys.push(...Object.keys(o))
objectKeys.push(...getters)
for (const key of objectKeys) {
const objKey = key
converted.set(
BaseObjectSerializer.CleanKey(key),
await this.PreserializeObject(o[objKey], closures)
)
}
return converted
}
static CleanKey(originalKey) {
const newStringChars = []
for (let i = 0; i < originalKey.length; i++) {
if (i == 1 && originalKey[i] == '@' && originalKey[0] == '@') {
continue
}
if (this.disallowedCharacters.includes(originalKey[i])) {
continue
}
newStringChars.push(originalKey[i])
}
return newStringChars.join('')
}
async StoreObject(objectId, object) {
for (const transport of this.transports) {
await transport.SaveObject(objectId, object)
}
}
SerializeMap(map) {
return JSON.stringify(Object.fromEntries(map))
}
GetId(json) {
return (0, crypto_js_1.MD5)(json).toString(crypto_js_1.enc.Hex)
}
AddSelfToParentClosures(objectId, closureTables) {
// only go to closureTable length - 1 because the last closure table belongs to the object with the
// provided id
const parentClosureTablesCount = closureTables.length - 1
for (let parentLevel = 0; parentLevel < parentClosureTablesCount; parentLevel++) {
const childDepth = parentClosureTablesCount - parentLevel
closureTables[parentLevel].set(objectId, childDepth)
}
}
}
exports.BaseObjectSerializer = BaseObjectSerializer
BaseObjectSerializer.disallowedCharacters = ['.', '/']
class DataChunk {
constructor(data) {
this.speckle_type = 'Speckle.Core.Models.DataChunk'
this.data = data ?? []
}
}
exports.DataChunk = DataChunk
class ObjectReference {
constructor(referencedId) {
this.referencedId = referencedId
this.speckle_type = 'reference'
}
}
exports.ObjectReference = ObjectReference
class SerializedBase {
constructor(id, json) {
this.id = id
this.json = json
}
}
exports.SerializedBase = SerializedBase
+166
View File
@@ -0,0 +1,166 @@
/* eslint-disable @typescript-eslint/ban-types */
import { MD5, enc } from 'crypto-js'
/**
* Serializer for Speckle objects written in Typescript
*/
export class BaseObjectSerializer {
constructor(public transports: ITransport[]) {}
public async SerializeBase(object: object): Promise<SerializedBase> {
return await this.SerializeBaseWithClosures(object, [])
}
private async SerializeBaseWithClosures(object: object, closures: Array<Map<string, number>>) {
const thisClosure = new Map<string, number>()
closures.push(thisClosure)
const converted = await this.PreserializeEachObjectProperty(object, closures)
let json = this.SerializeMap(converted)
const id = this.GetId(json)
converted.set('id', id)
this.AddSelfToParentClosures(id, closures)
if (thisClosure.size > 0) {
converted.set('__closure', Object.fromEntries(thisClosure))
}
converted.set('totalChildrenCount', thisClosure.size)
json = this.SerializeMap(converted)
await this.StoreObject(id, converted)
return new SerializedBase(id, json)
}
private async PreserializeObject(
object: any,
closures: Array<Map<string, number>>
): Promise<any> {
if (!(object instanceof Object) || object instanceof String) {
return object
}
if (object instanceof DataChunk) {
const serialized = await this.SerializeBaseWithClosures(object, [...closures])
return new ObjectReference(serialized.id)
}
if (object instanceof Array) {
// chunk array into 5000 by default
const chunkSize = 5000
if (object.length > chunkSize) {
let serializedCount = 0
const data = new Array<DataChunk>()
while (serializedCount < object.length) {
const dataChunkCount = Math.min(chunkSize, object.length - serializedCount)
data.push(new DataChunk(object.slice(serializedCount, serializedCount + dataChunkCount)))
serializedCount += dataChunkCount
}
return await this.PreserializeObject(data, closures)
}
const convertedList = new Array<any>()
for (const element of object) {
convertedList.push(await this.PreserializeObject(element, closures))
}
return convertedList
}
if (object instanceof Object) {
return Object.fromEntries(await this.PreserializeEachObjectProperty(object, closures))
}
throw new Error(`Cannot serialize object ${object}`)
}
private async PreserializeEachObjectProperty(
o: object,
closures: Array<Map<string, number>>
): Promise<Map<string, any>> {
const converted = new Map<string, any>()
const getters = Object.entries(Object.getOwnPropertyDescriptors(Reflect.getPrototypeOf(o)))
.filter(([key, descriptor]) => typeof descriptor.get === 'function' && key !== '__proto__')
.map(([key]) => key)
const objectKeys = new Array<string>()
objectKeys.push(...Object.keys(o))
objectKeys.push(...getters)
for (const key of objectKeys) {
const objKey = key as keyof object
converted.set(
BaseObjectSerializer.CleanKey(key),
await this.PreserializeObject(o[objKey], closures)
)
}
return converted
}
private static disallowedCharacters: string[] = ['.', '/']
private static CleanKey(originalKey: string): string {
const newStringChars = []
for (let i = 0; i < originalKey.length; i++) {
if (i == 1 && originalKey[i] == '@' && originalKey[0] == '@') {
continue
}
if (this.disallowedCharacters.includes(originalKey[i])) {
continue
}
newStringChars.push(originalKey[i])
}
return newStringChars.join('')
}
private async StoreObject(objectId: string, object: Map<string, any>) {
for (const transport of this.transports) {
await transport.SaveObject(objectId, object)
}
}
private SerializeMap(map: Map<string, any>): string {
return JSON.stringify(Object.fromEntries(map))
}
private GetId(json: string): string {
return MD5(json).toString(enc.Hex)
}
private AddSelfToParentClosures(objectId: string, closureTables: Array<Map<string, number>>) {
// only go to closureTable length - 1 because the last closure table belongs to the object with the
// provided id
const parentClosureTablesCount = closureTables.length - 1
for (let parentLevel = 0; parentLevel < parentClosureTablesCount; parentLevel++) {
const childDepth = parentClosureTablesCount - parentLevel
closureTables[parentLevel].set(objectId, childDepth)
}
}
}
export class DataChunk implements IBase {
public speckle_type = 'Speckle.Core.Models.DataChunk'
public data: any[]
constructor(data: any[] | null) {
this.data = data ?? []
}
}
export class ObjectReference implements IBase {
public speckle_type = 'reference'
constructor(public referencedId: string) {}
}
export interface IBase {
readonly speckle_type: string
}
export interface ITransport {
SaveObject(id: string, object: Map<string, any>): Promise<void>
}
export class SerializedBase {
constructor(public id: string, public json: string) {}
}
+33
View File
@@ -0,0 +1,33 @@
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
exports.ExcelSheetDataRetriever = void 0
class ExcelSheetDataRetriever {
// Takes address as a string formatted like this 'Sheet1!A1:B4' and returns a 2d array of values.
// This queries the document in batches in order to avoid api errors about requests that are too large
static async GetValuesAsNestedList(context, address) {
const sheetName = address.split('!')[0].replace(/'/g, '')
const rangeAddress = address.split('!')[1]
const sheet = context.workbook.worksheets.getItem(sheetName)
const totalRange = sheet.getRange(rangeAddress)
totalRange.load('columnCount, columnIndex, rowCount, rowIndex')
await context.sync()
let numRowsRetrieved = 0
const chunkSize = 5000
let values = []
while (numRowsRetrieved < totalRange.rowCount) {
const numRowsToRetrieve = Math.min(chunkSize, totalRange.rowCount - numRowsRetrieved)
const currentRange = sheet.getRangeByIndexes(
totalRange.rowIndex + numRowsRetrieved,
totalRange.columnIndex,
numRowsToRetrieve,
totalRange.columnCount
)
currentRange.load('values')
await context.sync()
values = values.concat(currentRange.values)
numRowsRetrieved += numRowsToRetrieve
}
return values
}
}
exports.ExcelSheetDataRetriever = ExcelSheetDataRetriever
+37
View File
@@ -0,0 +1,37 @@
export class ExcelSheetDataRetriever {
// Takes address as a string formatted like this 'Sheet1!A1:B4' and returns a 2d array of values.
// This queries the document in batches in order to avoid api errors about requests that are too large
static async GetValuesAsNestedList(
context: Excel.RequestContext,
address: string
): Promise<any[][]> {
const sheetName = address.split('!')[0].replace(/'/g, '')
const rangeAddress = address.split('!')[1]
const sheet = context.workbook.worksheets.getItem(sheetName)
const totalRange = sheet.getRange(rangeAddress)
totalRange.load('columnCount, columnIndex, rowCount, rowIndex')
await context.sync()
let numRowsRetrieved = 0
const chunkSize = 5000
let values: any[][] = []
while (numRowsRetrieved < totalRange.rowCount) {
const numRowsToRetrieve = Math.min(chunkSize, totalRange.rowCount - numRowsRetrieved)
const currentRange = sheet.getRangeByIndexes(
totalRange.rowIndex + numRowsRetrieved,
totalRange.columnIndex,
numRowsToRetrieve,
totalRange.columnCount
)
currentRange.load('values')
await context.sync()
values = values.concat(currentRange.values)
numRowsRetrieved += numRowsToRetrieve
}
return values
}
}
+57
View File
@@ -0,0 +1,57 @@
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
exports.ServerTransport = void 0
class ServerTransport {
/**
*
*/
constructor(serverUrl, token, streamId) {
this.serverUrl = serverUrl
this.token = token
this.streamId = streamId
}
async SaveObject(id, map) {
const query = `mutation objectCreate ($object: ObjectCreateInput!) {objectCreate(objectInput: $object)}`
await fetch(`${this.serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
object: {
streamId: this.streamId,
objects: [Object.fromEntries(map)]
}
}
})
})
// const data = (await response.json()) as any
}
async CreateCommit(branchName, objectId, message) {
const query = `mutation commitCreate($myCommit: CommitCreateInput!){ commitCreate(commit: $myCommit)}`
await fetch(`${this.serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
myCommit: {
streamId: this.streamId,
branchName: branchName,
objectId: objectId,
message: message ? message : 'Data from Excel',
sourceApplication: 'excel'
}
}
})
})
// const data = (await response.json()) as any
}
}
exports.ServerTransport = ServerTransport
+57
View File
@@ -0,0 +1,57 @@
import { ITransport } from './BaseObjectSerializer'
export class ServerTransport implements ITransport {
/**
*
*/
constructor(private serverUrl: string, private token: string, private streamId: string) {}
async SaveObject(id: string, map: Map<string, any>): Promise<void> {
const query = `mutation objectCreate ($object: ObjectCreateInput!) {objectCreate(objectInput: $object)}`
await fetch(`${this.serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
object: {
streamId: this.streamId,
objects: [Object.fromEntries(map)]
}
}
})
})
// const data = (await response.json()) as any
}
async CreateCommit(branchName: string, objectId: string, message: string | null) {
const query = `mutation commitCreate($myCommit: CommitCreateInput!){ commitCreate(commit: $myCommit)}`
await fetch(`${this.serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
myCommit: {
streamId: this.streamId,
branchName: branchName,
objectId: objectId,
message: message ? message : 'Data from Excel',
sourceApplication: 'excel'
}
}
})
})
// const data = (await response.json()) as any
}
}
+10 -9
View File
@@ -11,6 +11,7 @@ import {
onTableChanged,
onTableDeleted
} from './dataTable.js'
import { ExcelSheetDataRetriever } from './ExcelSheetDataRetreiver.js'
const unflatten = require('flat').unflatten
@@ -214,7 +215,7 @@ export function hideRowOrColumn(sheet, columnIndex = -1, rowIndex = -1) {
}
}
async function addIdDataToObjectData() {
async function addIdDataToObjectData(arrayData) {
if (
arrayData.length != arrayIdData.length ||
arrayData.length <= 1 ||
@@ -599,14 +600,13 @@ export async function bake(
} else if (previousHeaders) {
selectedHeaders = filterArrayData(previousHeaders, arrayData)
}
console.log(arrayData)
}
if (signal.aborted) return
await addIdDataToObjectData()
await bakeArray(arrayData, rowStart, colStart, context)
selectedHeaders ??= arrayData
await addIdDataToObjectData(selectedHeaders)
await bakeArray(selectedHeaders, rowStart, colStart, context)
}
await context.sync()
@@ -668,11 +668,12 @@ export async function send(savedStream, streamId, branchName, message) {
let sheetName = savedStream.selection.split('!')[0].replace(/'/g, '')
let rangeAddress = savedStream.selection.split('!')[1]
let sheet = context.workbook.worksheets.getItem(sheetName)
let range = sheet.getRange(rangeAddress)
range.load('values')
await context.sync()
let values = range.values
let values = await ExcelSheetDataRetriever.GetValuesAsNestedList(
context,
savedStream.selection
)
let data = []
// check for specific conversion
+58 -42
View File
@@ -6,6 +6,8 @@ import ObjectLoader from '@speckle/objectloader'
import streamsModule from './streams'
import userModule from './user'
import router from '../router'
import { BaseObjectSerializer } from '../plugins/BaseObjectSerializer'
import { ServerTransport } from '../plugins/ServerTransport'
const xml2js = require('xml2js')
@@ -100,7 +102,8 @@ const vuexExcel = new VuexPersistence({
export default new Vuex.Store({
state: {
snackbar: {}
snackbar: {},
isFE2: false
},
plugins: [vuexLocal.plugin, vuexExcel.plugin],
getters: {
@@ -109,9 +112,16 @@ export default new Vuex.Store({
mutations: {
SET_SNACKBAR(state, value) {
state.snackbar = value
},
UPDATE_IS_FE2(state, value) {
localStorage.setItem('frontend2', value)
state.isFE2 = value
}
},
actions: {
updateIsFE2({ commit }, value) {
commit('UPDATE_IS_FE2', value)
},
async redirect(_, data) {
//go to login and refresh token
window.location = `${data.serverUrl}/authn/verify/${process.env.VUE_APP_SPECKLE_ID}/${data.challenge}`
@@ -140,11 +150,13 @@ export default new Vuex.Store({
dialog.close()
await dispatch('exchangeAccessCode', args.message)
await dispatch('hasValidToken')
await dispatch('getServerInfo')
router.push('/')
})
}
)
},
async hasValidToken({ state, dispatch }) {
if (localStorage.getItem(TOKEN) === null) return false
await dispatch('getUser')
@@ -161,6 +173,7 @@ export default new Vuex.Store({
localStorage.removeItem('serverUrl')
localStorage.removeItem(REFRESH_TOKEN)
localStorage.removeItem('uuid')
localStorage.removeItem('frontend2')
window.location = window.location.origin
},
@@ -193,51 +206,14 @@ export default new Vuex.Store({
}
},
async createCommit(context, { streamId, branchName, message, object }) {
let query = `mutation objectCreate ($object: ObjectCreateInput!) {objectCreate(objectInput: $object)}`
let serverUrl = localStorage.getItem('serverUrl')
let token = localStorage.getItem(TOKEN)
let response = await fetch(`${serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
object: {
streamId: streamId,
objects: [object]
}
}
})
})
let data = await response.json()
let objectId = data.data.objectCreate[0]
const serverTransport = new ServerTransport(serverUrl, token, streamId)
const serializer = new BaseObjectSerializer([serverTransport])
const serialized = await serializer.SerializeBase(object)
query = `mutation commitCreate($myCommit: CommitCreateInput!){ commitCreate(commit: $myCommit)}`
response = await fetch(`${serverUrl}/graphql`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query,
variables: {
myCommit: {
streamId: streamId,
branchName: branchName,
objectId: objectId,
message: message ? message : 'Data from Excel',
sourceApplication: 'excel'
}
}
})
})
await serverTransport.CreateCommit(branchName, serialized.id, message)
},
async receiveCommit(context, { sourceApplication, streamId, commitId, message }) {
let query = `mutation objectReceive ($myInput:CommitReceivedInput!) {commitReceive(input:$myInput)}`
@@ -264,6 +240,46 @@ export default new Vuex.Store({
})
})
},
async getServerInfo({ dispatch }) {
let serverUrl = localStorage.getItem('serverUrl')
// Now, to check for a specific header
const isFrontend2Server = (headers) => {
const HEADER = 'x-speckle-frontend-2'
const headerValue = headers.get(HEADER)
if (headerValue === null) {
return false
}
const value = headerValue.toLowerCase() === 'true'
if (headerValue !== 'true' && headerValue !== 'false') {
throw new Error(
`Headers contained ${HEADER} header, but value ${headerValue} could not be parsed to a bool`
)
}
return value
}
// Use the function to check the header
try {
let response = await fetch(serverUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
let isFE2Server = isFrontend2Server(response.headers)
await dispatch('updateIsFE2', isFE2Server)
console.log('Is Frontend2 Server:', isFE2Server)
} catch (error) {
console.warn(error.message)
// TODO: fallback is FE1, this should be changed later
await dispatch('updateIsFE2', false)
}
},
async getUser(context) {
try {
let query = `query {
+14 -5
View File
@@ -1,7 +1,11 @@
require('url')
export class StreamWrapper {
constructor(streamIdOrUrl, accountId, serverUrl) {
constructor(streamIdOrUrl, accountId, serverUrl, isFE2) {
this.isFE2 = isFE2
this.streamsKey = this.isFE2 ? 'projects/' : 'streams/'
this.branchesKey = this.isFE2 ? 'models/' : 'branches/'
this.commitsKey = this.isFE2 ? 'versions/' : 'commits/'
this.originalOutput = streamIdOrUrl
try {
this.streamWrapperFromUrl(streamIdOrUrl)
@@ -12,12 +16,17 @@ export class StreamWrapper {
}
}
matchUrl(streamUrl) {
const fe2UrlRegex = /\/projects\/(?<projectId>[\w\d]+)(?:\/models\/(?<model>[\w\d]+(?:@[\w\d]+)?)(?:,(?<additionalModels>[\w\d]+(?:@[\w\d]+)?))*)?/
return fe2UrlRegex.exec(streamUrl)
}
streamWrapperFromUrl(streamUrl) {
this.url = new URL(streamUrl)
this.segments = this.url.pathname.split('/').map((segment) => segment + '/')
this.serverUrl = this.url.origin
if (this.segments.length >= 4 && this.segments[3]?.toLowerCase() === 'branches/') {
if (this.segments.length >= 4 && this.segments[3]?.toLowerCase() === this.branchesKey) {
this.streamId = this.segments[2].replace('/', '')
if (this.segments.length > 5) {
let branchSegments = this.segments.slice(4, this.segments.length - 1)
@@ -28,7 +37,7 @@ export class StreamWrapper {
} else {
switch (this.segments.length) {
case 3: // ie http://speckle.server/streams/8fecc9aa6d
if (this.segments[1].toLowerCase() === 'streams/')
if (this.segments[1].toLowerCase() === this.streamsKey)
this.streamId = this.segments[2].replace('/', '')
else throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`)
break
@@ -40,7 +49,7 @@ export class StreamWrapper {
break
case 5: // ie http://speckle.server/streams/8fecc9aa6d/commits/76a23d7179
switch (this.segments[3].toLowerCase()) {
case 'commits/':
case this.commitsKey:
this.streamId = this.segments[2].replace('/', '')
this.commitId = this.segments[4].replace('/', '')
break
@@ -49,7 +58,7 @@ export class StreamWrapper {
this.branchName = this.segments[3].replace('/', '')
this.commitId = this.segments[4].replace('/', '')
break
case 'branches/':
case this.branchesKey:
this.streamId = this.segments[2].replace('/', '')
this.branchName = this.segments[4].replace('/', '')
break
+3 -3
View File
@@ -43,7 +43,9 @@ export default {
name: 'Login',
data: () => ({
serverUrl:
process.env.NODE_ENV === 'development' ? 'https://latest.speckle.dev' : 'https://speckle.xyz',
process.env.NODE_ENV === 'development'
? 'https://latest.speckle.systems'
: 'https://app.speckle.systems',
validForm: true,
serverError: '',
serverUrlRules: [
@@ -53,8 +55,6 @@ export default {
'URL must be valid, with no trailing slash'
]
}),
mounted() {},
methods: {
async login() {
//when for is not visible (coming from web) don't validate
+5 -4
View File
@@ -17,7 +17,7 @@
small
icon
color="primary"
:href="`${serverUrl}/streams/${savedStream.id}`"
:href="`${serverUrl}/${$store.state.isFE2 ? 'projects' : 'streams'}/${savedStream.id}`"
target="_blank"
>
<v-icon small>mdi-open-in-new</v-icon>
@@ -127,13 +127,15 @@ export default {
}
},
apollo: {
$client: createClient(),
stream: {
prefetch: true,
query: streamQuery,
fetchPolicy: 'network-only',
variables() {
return {
id: this.streamId
id: this.streamId,
limit: 100
}
},
result() {
@@ -178,7 +180,6 @@ export default {
console.log(this.error)
}
},
$client: createClient(),
$subscribe: {
streamUpdated: {
query: gql`
@@ -423,7 +424,7 @@ export default {
speckleIdRange.load('text')
await context.sync()
let idsInViewer = new Array()
let idsInViewer = []
for (let i = 0; i < speckleIdRange.text?.length; i++) {
for (let j = 0; j < speckleIdRange.text[i].length; j++) {
if (speckleIdRange.text[i][j].length < 32) continue
+2 -4
View File
@@ -1,7 +1,7 @@
<template>
<v-container>
<v-container class="pa-0">
<v-row align="center">
<v-col cols="12" align="center" class="mt-5">
<v-col cols="12" align="center" class="mt-3">
<p v-if="search" class="subtitle">No streams found 🧐</p>
<div v-else-if="!$apollo.loading && filteredStreams && filteredStreams.length == 0">
<p class="subtitle">
@@ -122,7 +122,6 @@ export default {
},
computed: {
isAuthenticated() {
console.log(this.user)
return this.$store.getters.isAuthenticated
},
user() {
@@ -132,7 +131,6 @@ export default {
return this.$store.getters.serverUrl
},
filteredStreams() {
console.log('user', this.$store.state.user.user)
if (!this.streams.items) return null
let savedStreams = this.streams.items.filter(
+109
View File
@@ -0,0 +1,109 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}