diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8b8b7b9 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 4, + "semi": false, + "singleQuote": true +} \ No newline at end of file diff --git a/README.md b/README.md index dd6769b..4f5e8c7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Community Voice -This project uses [bos-loader](https://github.com/near/bos-loader) v0.10.0 to keep track of the widgets required to implement Community Voice on BOS. + +This project uses [bos-loader](https://github.com/near/bos-loader) v0.10.0 to keep track of the widgets required to implement Community Voice on BOS. # How to use -To use it, download bos-loader as [instructed](https://github.com/near/bos-loader/releases/tag/v0.10.0) and run `npm start`. Then, open https://near.org/flags and paste the following: `http://127.0.0.1:3030` and save flags. Then go to the desired widget, assuming user communityvoice.ndctools.near. As an example, https://near.org/communityvoice.ndctools.near/widget/HelloCV should work, printing `Hello Community Voice` \ No newline at end of file + +To use it, download bos-loader as [instructed](https://github.com/near/bos-loader/releases/tag/v0.10.0) and run `npm start`. Then, open https://near.org/flags and paste the following: `http://127.0.0.1:3030` and save flags. Then go to the desired widget, assuming user chatter.cheddar.near. As an example, https://near.org/chatter.cheddar.near/widget/HelloCV should work, printing `Hello Community Voice` diff --git a/context.json b/context.json index a9962ff..be97c6c 100644 --- a/context.json +++ b/context.json @@ -1 +1,5 @@ -{"wrapperSrc":"near/widget/DIG.Theme","wrapperProps":{},"networkId":"mainnet"} \ No newline at end of file +{ + "wrapperSrc": "near/widget/DIG.Theme", + "wrapperProps": {}, + "networkId": "mainnet" +} diff --git a/deploy.js b/deploy.js index 37a4d8d..7a3fdd0 100644 --- a/deploy.js +++ b/deploy.js @@ -1,23 +1,24 @@ -const path = require("path"); -const { readdirSync, readFileSync, writeFileSync } = require("fs"); -const { keyStores, connect, Contract } = require("near-api-js"); -const { homedir } = require("os"); +const path = require('path') +const { readdirSync, readFileSync, writeFileSync } = require('fs') +const { keyStores, connect, Contract } = require('near-api-js') +const { homedir } = require('os') -const ACCOUNT = "communityvoice.ndctools.near" +const ACCOUNT = 'chatter.cheddar.near' // const ACCOUNT = "silkking.near" -function getAllFilesNames(addedPath = "") { - const pathToSearch = path.join("./src", addedPath) - const foldersToExclude = ["bosTests", "testingWidgets", "tests"] - const filesToExclude = ["HelloCV.jsx"] +function getAllFilesNames(addedPath = '') { + const pathToSearch = path.join('./src', addedPath) + const foldersToExclude = ['bosTests', 'testingWidgets', 'tests'] + const filesToExclude = ['HelloCV.jsx'] const files = [] - readdirSync(pathToSearch).forEach(file => { - if(filesToExclude.includes(file) || foldersToExclude.includes(file)) return + readdirSync(pathToSearch).forEach((file) => { + if (filesToExclude.includes(file) || foldersToExclude.includes(file)) + return - const isFile = file.endsWith(".jsx") - if(isFile) { + const isFile = file.endsWith('.jsx') + if (isFile) { files.push(path.join(addedPath, file)) } else { files.push(...getAllFilesNames(path.join(addedPath, file))) @@ -27,87 +28,93 @@ function getAllFilesNames(addedPath = "") { } async function getContract() { - const CREDENTIALS_DIR = ".near-credentials"; - const credentialsPath = path.join(homedir(), CREDENTIALS_DIR); - console.log("credentialsPath", credentialsPath ) - const myKeyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); + const CREDENTIALS_DIR = '.near-credentials' + const credentialsPath = path.join(homedir(), CREDENTIALS_DIR) + console.log('credentialsPath', credentialsPath) + const myKeyStore = new keyStores.UnencryptedFileSystemKeyStore( + credentialsPath + ) const connectionConfig = { - networkId: "mainnet", + networkId: 'mainnet', keyStore: myKeyStore, // first create a key store - nodeUrl: "https://rpc.mainnet.near.org", - walletUrl: "https://wallet.mainnet.near.org", - helperUrl: "https://helper.mainnet.near.org", - explorerUrl: "https://nearblocks.io", - }; - const nearConnection = await connect(connectionConfig); + nodeUrl: 'https://rpc.mainnet.near.org', + walletUrl: 'https://wallet.mainnet.near.org', + helperUrl: 'https://helper.mainnet.near.org', + explorerUrl: 'https://nearblocks.io', + } + const nearConnection = await connect(connectionConfig) // const walletConnection = new WalletConnection(nearConnection); - const account = await nearConnection.account(ACCOUNT); + const account = await nearConnection.account(ACCOUNT) const contract = new Contract( - account , // the account object that is connecting - "social.near", + account, // the account object that is connecting + 'social.near', { // name of contract you're connecting to viewMethods: [], // view methods do not change state but usually return a value - changeMethods: ["set"], // change methods modify state + changeMethods: ['set'], // change methods modify state } - ); + ) return contract } function getWidgetsJsons(files) { - return files.map(file => { - const fileContent = readFileSync(path.join("./src", file), "utf-8").toString() - - const widgetName = file.replace(".jsx", "").split("/").join(".") + return files.map((file) => { + const fileContent = readFileSync( + path.join('./src', file), + 'utf-8' + ).toString() + + const widgetName = file.replace('.jsx', '').split('/').join('.') return { [ACCOUNT]: { widget: { [widgetName]: { - "": fileContent - } - } - } + '': fileContent, + }, + }, + }, } }) } -function sleep(ms){ - return new Promise(resolve => setTimeout(resolve, ms)); -}; +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)) +} async function run() { const indexesToDeploy = [] const indexesWithError = [] const files = getAllFilesNames() - + const widgetJsons = getWidgetsJsons(files) - + const socialContract = await getContract() - for(let i = 29; i < widgetJsons.length; i++) { - if(indexesToDeploy.length > 0 && !indexesToDeploy.includes(i)) continue + for (let i = 0; i < widgetJsons.length; i++) { + if (indexesToDeploy.length > 0 && !indexesToDeploy.includes(i)) continue const json = widgetJsons[i] const widgetName = Object.keys(json[ACCOUNT].widget)[0] - console.log("Deploying widget with index", i, widgetName) + console.log('Deploying widget with index', i, widgetName) try { - await socialContract.set({ - data: json - }, - "300000000000000", - "1" + "0".repeat(21)) - console.log("Deployed", widgetName) - } catch(err) { - console.log("Error deploying widget with index", i) + await socialContract.set( + { + data: json, + }, + '300000000000000', + '1' + '0'.repeat(21) + ) + console.log('Deployed', widgetName) + } catch (err) { + console.log('Error deploying widget with index', i) console.log(err) indexesWithError.push(i) } finally { await sleep(1521) } } - console.log("Indexes with error", indexesWithError) - + console.log('Indexes with error', indexesWithError) } -run() \ No newline at end of file +run() diff --git a/flags.json b/flags.json index 33c7ec2..7c1cb51 100644 --- a/flags.json +++ b/flags.json @@ -1 +1 @@ -{"components":{}} \ No newline at end of file +{ "components": {} } diff --git a/package-lock.json b/package-lock.json index a8fa629..1b90c4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,490 +1,490 @@ { - "name": "community_voice", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "community_voice", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "fs": "^0.0.1-security", - "near-api-js": "^3.0.4", - "path": "^0.12.7" - } - }, - "node_modules/@near-js/accounts": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@near-js/accounts/-/accounts-1.0.4.tgz", - "integrity": "sha512-6zgSwq/rQ9ggPOIkGUx9RoEurbJiojqA/axeh6o1G+46GqUBI7SUcDooyVvZjeiOvUPObnTQptDYpbV+XZji8g==", - "dependencies": { - "@near-js/crypto": "1.2.1", - "@near-js/providers": "0.1.1", - "@near-js/signers": "0.1.1", - "@near-js/transactions": "1.1.2", - "@near-js/types": "0.0.4", - "@near-js/utils": "0.1.0", - "ajv": "8.11.2", - "ajv-formats": "2.1.1", - "bn.js": "5.2.1", - "borsh": "1.0.0", - "depd": "2.0.0", - "lru_map": "0.4.1", - "near-abi": "0.1.1" - } - }, - "node_modules/@near-js/crypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@near-js/crypto/-/crypto-1.2.1.tgz", - "integrity": "sha512-iJOHaGKvdudYfR8nEtRhGlgcTEHeVmxMoT0JVXmuP3peG96v/sSnA03CE6MZBeCC8txKAQOffagxE7oU6hJp9g==", - "dependencies": { - "@near-js/types": "0.0.4", - "@near-js/utils": "0.1.0", - "@noble/curves": "1.2.0", - "bn.js": "5.2.1", - "borsh": "1.0.0", - "randombytes": "2.1.0" - } - }, - "node_modules/@near-js/keystores": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@near-js/keystores/-/keystores-0.0.9.tgz", - "integrity": "sha512-j8ySgVEcm2Gg6zxkSdadNtPlIqhJZdPGfWWM3tPtEoowNS9snhwZn5NRFPrgmX0+MzpF7E091CRcY90MvRVhsg==", - "dependencies": { - "@near-js/crypto": "1.2.1", - "@near-js/types": "0.0.4" - } - }, - "node_modules/@near-js/keystores-browser": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@near-js/keystores-browser/-/keystores-browser-0.0.9.tgz", - "integrity": "sha512-JzPj+RHJN2G3CEm/LyfbtZDQy/wxgOlqfh52voqPGijUHg93b27KBqtZShazAgJNkhzRbWcoluWQnd2jL8vF7A==", - "dependencies": { - "@near-js/crypto": "1.2.1", - "@near-js/keystores": "0.0.9" - } - }, - "node_modules/@near-js/keystores-node": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@near-js/keystores-node/-/keystores-node-0.0.9.tgz", - "integrity": "sha512-2B9MYz6uIhysG1fhQSjvaPYCM7gM+UAeDchX0J8QRauXIeN8TGzpcdgkdkMUnWNTIdt3Iblh0ZuCs+FY02dTXg==", - "dependencies": { - "@near-js/crypto": "1.2.1", - "@near-js/keystores": "0.0.9" - } - }, - "node_modules/@near-js/providers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@near-js/providers/-/providers-0.1.1.tgz", - "integrity": "sha512-0M/Vz2Ac34ShKVoe2ftVJ5Qg4eSbEqNXDbCDOdVj/2qbLWZa7Wpe+me5ei4TMY2ZhGdawhgJUPrYwdJzOCyf8w==", - "dependencies": { - "@near-js/transactions": "1.1.2", - "@near-js/types": "0.0.4", - "@near-js/utils": "0.1.0", - "bn.js": "5.2.1", - "borsh": "1.0.0", - "http-errors": "1.7.2" - }, - "optionalDependencies": { - "node-fetch": "2.6.7" - } - }, - "node_modules/@near-js/signers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@near-js/signers/-/signers-0.1.1.tgz", - "integrity": "sha512-focJgs04dBUfawMnyGg3yIjaMawuVz2OeLRKC4t5IQDmO4PLfdIraEuwgS7tckMq3GdrJ7nqkwkpSNYpdt7I5Q==", - "dependencies": { - "@near-js/crypto": "1.2.1", - "@near-js/keystores": "0.0.9", - "@noble/hashes": "1.3.3" - } - }, - "node_modules/@near-js/transactions": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@near-js/transactions/-/transactions-1.1.2.tgz", - "integrity": "sha512-AqYA56ncwgrWjIu+bNaWjTPRZb0O+SfpWIP7U+1FKNKxNYMCtkt6zp7SlQeZn743shKVq9qMzA9+ous/KCb0QQ==", - "dependencies": { - "@near-js/crypto": "1.2.1", - "@near-js/signers": "0.1.1", - "@near-js/types": "0.0.4", - "@near-js/utils": "0.1.0", - "@noble/hashes": "1.3.3", - "bn.js": "5.2.1", - "borsh": "1.0.0" - } - }, - "node_modules/@near-js/types": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@near-js/types/-/types-0.0.4.tgz", - "integrity": "sha512-8TTMbLMnmyG06R5YKWuS/qFG1tOA3/9lX4NgBqQPsvaWmDsa+D+QwOkrEHDegped0ZHQwcjAXjKML1S1TyGYKg==", - "dependencies": { - "bn.js": "5.2.1" - } - }, - "node_modules/@near-js/utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@near-js/utils/-/utils-0.1.0.tgz", - "integrity": "sha512-kOVAXmJzaC8ElJD3RLEoBuqOK+d5s7jc0JkvhyEtbuEmXYHHAy9Q17/YkDcX9tyr01L85iOt66z0cODqzgtQwA==", - "dependencies": { - "@near-js/types": "0.0.4", - "bn.js": "5.2.1", - "bs58": "4.0.0", - "depd": "2.0.0", - "mustache": "4.0.0" - } - }, - "node_modules/@near-js/wallet-account": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@near-js/wallet-account/-/wallet-account-1.1.1.tgz", - "integrity": "sha512-NnoJKtogBQ7Qz+AP+LdF70BP8Az6UXQori7OjPqJLMo73bn6lh5Ywvegwd1EB7ZEVe4BRt9+f9QkbU5M8ANfAw==", - "dependencies": { - "@near-js/accounts": "1.0.4", - "@near-js/crypto": "1.2.1", - "@near-js/keystores": "0.0.9", - "@near-js/signers": "0.1.1", - "@near-js/transactions": "1.1.2", - "@near-js/types": "0.0.4", - "@near-js/utils": "0.1.0", - "bn.js": "5.2.1", - "borsh": "1.0.0" - } - }, - "node_modules/@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "dependencies": { - "@noble/hashes": "1.3.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/curves/node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", - "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "node_modules/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/base-x": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-2.0.6.tgz", - "integrity": "sha512-UAmjxz9KbK+YIi66xej+pZVo/vxUOh49ubEvZW5egCbxhur05pBb+hwuireQwKO4nDpsNm64/jEei17LEpsr5g==", - "deprecated": "use 3.0.0 instead, safe-buffer has been merged and release for compatability", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": ">=4.5.0" - } - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" - }, - "node_modules/borsh": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/borsh/-/borsh-1.0.0.tgz", - "integrity": "sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ==" - }, - "node_modules/bs58": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.0.tgz", - "integrity": "sha512-/jcGuUuSebyxwLLfKrbKnCJttxRf9PM51EnHTwmFKBxl4z1SGkoAhrfd6uZKE0dcjQTfm6XzTP8DPr1tzE4KIw==", - "dependencies": { - "base-x": "^2.0.1" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fs": { - "version": "0.0.1-security", - "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", - "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" - }, - "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/lru_map": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.4.1.tgz", - "integrity": "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==" - }, - "node_modules/mustache": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.0.0.tgz", - "integrity": "sha512-FJgjyX/IVkbXBXYUwH+OYwQKqWpFPLaLVESd70yHjSDunwzV2hZOoTBvPf4KLoxesUzzyfTH6F784Uqd7Wm5yA==", - "bin": { - "mustache": "bin/mustache" - }, - "engines": { - "npm": ">=1.4.0" - } - }, - "node_modules/near-abi": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/near-abi/-/near-abi-0.1.1.tgz", - "integrity": "sha512-RVDI8O+KVxRpC3KycJ1bpfVj9Zv+xvq9PlW1yIFl46GhrnLw83/72HqHGjGDjQ8DtltkcpSjY9X3YIGZ+1QyzQ==", - "dependencies": { - "@types/json-schema": "^7.0.11" - } - }, - "node_modules/near-api-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/near-api-js/-/near-api-js-3.0.4.tgz", - "integrity": "sha512-qKWjnugoB7kSFhzZ5GXyH/eABspCQYWBmWnM4hpV5ctnQBt89LqgEu9yD1z4sa89MvUu8BuCxwb1m00BE8iofg==", - "dependencies": { - "@near-js/accounts": "1.0.4", - "@near-js/crypto": "1.2.1", - "@near-js/keystores": "0.0.9", - "@near-js/keystores-browser": "0.0.9", - "@near-js/keystores-node": "0.0.9", - "@near-js/providers": "0.1.1", - "@near-js/signers": "0.1.1", - "@near-js/transactions": "1.1.2", - "@near-js/types": "0.0.4", - "@near-js/utils": "0.1.0", - "@near-js/wallet-account": "1.1.1", - "@noble/curves": "1.2.0", - "ajv": "8.11.2", - "ajv-formats": "2.1.1", - "bn.js": "5.2.1", - "borsh": "1.0.0", - "depd": "2.0.0", - "http-errors": "1.7.2", - "near-abi": "0.1.1", - "node-fetch": "2.6.7" - } - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", - "dependencies": { - "process": "^0.11.1", - "util": "^0.10.3" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "name": "community_voice", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "community_voice", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "fs": "^0.0.1-security", + "near-api-js": "^3.0.4", + "path": "^0.12.7" + } + }, + "node_modules/@near-js/accounts": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@near-js/accounts/-/accounts-1.0.4.tgz", + "integrity": "sha512-6zgSwq/rQ9ggPOIkGUx9RoEurbJiojqA/axeh6o1G+46GqUBI7SUcDooyVvZjeiOvUPObnTQptDYpbV+XZji8g==", + "dependencies": { + "@near-js/crypto": "1.2.1", + "@near-js/providers": "0.1.1", + "@near-js/signers": "0.1.1", + "@near-js/transactions": "1.1.2", + "@near-js/types": "0.0.4", + "@near-js/utils": "0.1.0", + "ajv": "8.11.2", + "ajv-formats": "2.1.1", + "bn.js": "5.2.1", + "borsh": "1.0.0", + "depd": "2.0.0", + "lru_map": "0.4.1", + "near-abi": "0.1.1" + } + }, + "node_modules/@near-js/crypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@near-js/crypto/-/crypto-1.2.1.tgz", + "integrity": "sha512-iJOHaGKvdudYfR8nEtRhGlgcTEHeVmxMoT0JVXmuP3peG96v/sSnA03CE6MZBeCC8txKAQOffagxE7oU6hJp9g==", + "dependencies": { + "@near-js/types": "0.0.4", + "@near-js/utils": "0.1.0", + "@noble/curves": "1.2.0", + "bn.js": "5.2.1", + "borsh": "1.0.0", + "randombytes": "2.1.0" + } + }, + "node_modules/@near-js/keystores": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@near-js/keystores/-/keystores-0.0.9.tgz", + "integrity": "sha512-j8ySgVEcm2Gg6zxkSdadNtPlIqhJZdPGfWWM3tPtEoowNS9snhwZn5NRFPrgmX0+MzpF7E091CRcY90MvRVhsg==", + "dependencies": { + "@near-js/crypto": "1.2.1", + "@near-js/types": "0.0.4" + } + }, + "node_modules/@near-js/keystores-browser": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@near-js/keystores-browser/-/keystores-browser-0.0.9.tgz", + "integrity": "sha512-JzPj+RHJN2G3CEm/LyfbtZDQy/wxgOlqfh52voqPGijUHg93b27KBqtZShazAgJNkhzRbWcoluWQnd2jL8vF7A==", + "dependencies": { + "@near-js/crypto": "1.2.1", + "@near-js/keystores": "0.0.9" + } + }, + "node_modules/@near-js/keystores-node": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@near-js/keystores-node/-/keystores-node-0.0.9.tgz", + "integrity": "sha512-2B9MYz6uIhysG1fhQSjvaPYCM7gM+UAeDchX0J8QRauXIeN8TGzpcdgkdkMUnWNTIdt3Iblh0ZuCs+FY02dTXg==", + "dependencies": { + "@near-js/crypto": "1.2.1", + "@near-js/keystores": "0.0.9" + } + }, + "node_modules/@near-js/providers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@near-js/providers/-/providers-0.1.1.tgz", + "integrity": "sha512-0M/Vz2Ac34ShKVoe2ftVJ5Qg4eSbEqNXDbCDOdVj/2qbLWZa7Wpe+me5ei4TMY2ZhGdawhgJUPrYwdJzOCyf8w==", + "dependencies": { + "@near-js/transactions": "1.1.2", + "@near-js/types": "0.0.4", + "@near-js/utils": "0.1.0", + "bn.js": "5.2.1", + "borsh": "1.0.0", + "http-errors": "1.7.2" + }, + "optionalDependencies": { + "node-fetch": "2.6.7" + } + }, + "node_modules/@near-js/signers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@near-js/signers/-/signers-0.1.1.tgz", + "integrity": "sha512-focJgs04dBUfawMnyGg3yIjaMawuVz2OeLRKC4t5IQDmO4PLfdIraEuwgS7tckMq3GdrJ7nqkwkpSNYpdt7I5Q==", + "dependencies": { + "@near-js/crypto": "1.2.1", + "@near-js/keystores": "0.0.9", + "@noble/hashes": "1.3.3" + } + }, + "node_modules/@near-js/transactions": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@near-js/transactions/-/transactions-1.1.2.tgz", + "integrity": "sha512-AqYA56ncwgrWjIu+bNaWjTPRZb0O+SfpWIP7U+1FKNKxNYMCtkt6zp7SlQeZn743shKVq9qMzA9+ous/KCb0QQ==", + "dependencies": { + "@near-js/crypto": "1.2.1", + "@near-js/signers": "0.1.1", + "@near-js/types": "0.0.4", + "@near-js/utils": "0.1.0", + "@noble/hashes": "1.3.3", + "bn.js": "5.2.1", + "borsh": "1.0.0" + } + }, + "node_modules/@near-js/types": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@near-js/types/-/types-0.0.4.tgz", + "integrity": "sha512-8TTMbLMnmyG06R5YKWuS/qFG1tOA3/9lX4NgBqQPsvaWmDsa+D+QwOkrEHDegped0ZHQwcjAXjKML1S1TyGYKg==", + "dependencies": { + "bn.js": "5.2.1" + } + }, + "node_modules/@near-js/utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@near-js/utils/-/utils-0.1.0.tgz", + "integrity": "sha512-kOVAXmJzaC8ElJD3RLEoBuqOK+d5s7jc0JkvhyEtbuEmXYHHAy9Q17/YkDcX9tyr01L85iOt66z0cODqzgtQwA==", + "dependencies": { + "@near-js/types": "0.0.4", + "bn.js": "5.2.1", + "bs58": "4.0.0", + "depd": "2.0.0", + "mustache": "4.0.0" + } + }, + "node_modules/@near-js/wallet-account": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@near-js/wallet-account/-/wallet-account-1.1.1.tgz", + "integrity": "sha512-NnoJKtogBQ7Qz+AP+LdF70BP8Az6UXQori7OjPqJLMo73bn6lh5Ywvegwd1EB7ZEVe4BRt9+f9QkbU5M8ANfAw==", + "dependencies": { + "@near-js/accounts": "1.0.4", + "@near-js/crypto": "1.2.1", + "@near-js/keystores": "0.0.9", + "@near-js/signers": "0.1.1", + "@near-js/transactions": "1.1.2", + "@near-js/types": "0.0.4", + "@near-js/utils": "0.1.0", + "bn.js": "5.2.1", + "borsh": "1.0.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/base-x": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-2.0.6.tgz", + "integrity": "sha512-UAmjxz9KbK+YIi66xej+pZVo/vxUOh49ubEvZW5egCbxhur05pBb+hwuireQwKO4nDpsNm64/jEei17LEpsr5g==", + "deprecated": "use 3.0.0 instead, safe-buffer has been merged and release for compatability", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4.5.0" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/borsh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-1.0.0.tgz", + "integrity": "sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ==" + }, + "node_modules/bs58": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.0.tgz", + "integrity": "sha512-/jcGuUuSebyxwLLfKrbKnCJttxRf9PM51EnHTwmFKBxl4z1SGkoAhrfd6uZKE0dcjQTfm6XzTP8DPr1tzE4KIw==", + "dependencies": { + "base-x": "^2.0.1" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/lru_map": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.4.1.tgz", + "integrity": "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==" + }, + "node_modules/mustache": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.0.0.tgz", + "integrity": "sha512-FJgjyX/IVkbXBXYUwH+OYwQKqWpFPLaLVESd70yHjSDunwzV2hZOoTBvPf4KLoxesUzzyfTH6F784Uqd7Wm5yA==", + "bin": { + "mustache": "bin/mustache" + }, + "engines": { + "npm": ">=1.4.0" + } + }, + "node_modules/near-abi": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/near-abi/-/near-abi-0.1.1.tgz", + "integrity": "sha512-RVDI8O+KVxRpC3KycJ1bpfVj9Zv+xvq9PlW1yIFl46GhrnLw83/72HqHGjGDjQ8DtltkcpSjY9X3YIGZ+1QyzQ==", + "dependencies": { + "@types/json-schema": "^7.0.11" + } + }, + "node_modules/near-api-js": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/near-api-js/-/near-api-js-3.0.4.tgz", + "integrity": "sha512-qKWjnugoB7kSFhzZ5GXyH/eABspCQYWBmWnM4hpV5ctnQBt89LqgEu9yD1z4sa89MvUu8BuCxwb1m00BE8iofg==", + "dependencies": { + "@near-js/accounts": "1.0.4", + "@near-js/crypto": "1.2.1", + "@near-js/keystores": "0.0.9", + "@near-js/keystores-browser": "0.0.9", + "@near-js/keystores-node": "0.0.9", + "@near-js/providers": "0.1.1", + "@near-js/signers": "0.1.1", + "@near-js/transactions": "1.1.2", + "@near-js/types": "0.0.4", + "@near-js/utils": "0.1.0", + "@near-js/wallet-account": "1.1.1", + "@noble/curves": "1.2.0", + "ajv": "8.11.2", + "ajv-formats": "2.1.1", + "bn.js": "5.2.1", + "borsh": "1.0.0", + "depd": "2.0.0", + "http-errors": "1.7.2", + "near-abi": "0.1.1", + "node-fetch": "2.6.7" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } - ] - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } } - } } diff --git a/package.json b/package.json index 283b30c..d0890c5 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,19 @@ { - "name": "community_voice", - "version": "1.0.0", - "description": "This project uses [bos-loader](https://github.com/near/bos-loader)v0.10.0 to keep track of the widgets required to implement Community Voice on BOS.", - "main": "index.js", - "scripts": { - "start": "bos-loader communityvoice.ndctools.near --path ./src", - "test": "echo \"Error: no test specified\" && exit 1", - "deploy": "node deploy.js" - }, - "author": "", - "license": "ISC", - "dependencies": { - "fs": "^0.0.1-security", - "near-api-js": "^3.0.4", - "path": "^0.12.7" - } + "name": "community_voice", + "version": "1.0.0", + "description": "This project uses [bos-loader](https://github.com/near/bos-loader)v0.10.0 to keep track of the widgets required to implement Community Voice on BOS.", + "main": "index.js", + "scripts": { + "start": "bos-loader chatter.cheddar.near --path ./src", + "test": "echo \"Error: no test specified\" && exit 1", + "deploy": "node deploy.js", + "fmt": "prettier --write '**/*.{js,jsx,ts,tsx,json}'" + }, + "author": "", + "license": "ISC", + "dependencies": { + "fs": "^0.0.1-security", + "near-api-js": "^3.0.4", + "path": "^0.12.7" + } } diff --git a/props.json b/props.json index 9e26dfe..0967ef4 100644 --- a/props.json +++ b/props.json @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/src/Cheddar/AddComment.jsx b/src/Cheddar/AddComment.jsx new file mode 100644 index 0000000..28d6003 --- /dev/null +++ b/src/Cheddar/AddComment.jsx @@ -0,0 +1,502 @@ +// Cheddar.AddComment +const { createComment, editComment } = VM.require( + 'chatter.cheddar.near/widget/lib.comment' +) || { createComment: () => {}, editComment: () => {} } +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) || { getConfig: () => {} } + +const { + widgets, + isTest, + article, + onCloseModal, + originalComment, + isReplying, + username, + placement, + rootCommentId, + replyingTo, + baseActions, + editionData, + loadComments, + setLoadingComments, +} = props + +const rootId = rootCommentId ?? article.value.metadata.id //To render in the proper location + +const commentId = editionData ? editionData.value.metadata.id : undefined //(OPTIONAL) to edit comment + +const isEdition = commentId !== undefined + +const ModalCard = styled.div` + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + background: rgba(0, 0, 0, 0.7); +` +const CommentCard = styled.div` + display: flex; + width: 400px; + padding: 20px; + flex-direction: column; + align-items: flex-start; + gap: 16px; + border-radius: 10px; + background: #fff; + border: 1px solid transparent; + margin-left: auto; + margin-right: auto; + margin-buttom: 50%; + @media only screen and (max-width: 480px) { + width: 90%; + } +` +const H1 = styled.h1` + color: black; + font-size: 14px; + font-weight: 500; +` +const Container = styled.div` + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 20px; + align-self: stretch; +` +const CommentBody = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 16px; + align-self: stretch; +` +const BComment = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + align-self: stretch; +` +const BCommentmessage = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + align-self: stretch; +` +const BCMHeader = styled.div` + display: flex; + width: 100%; + align-items: center; + gap: 8px; +` +const BCMProfile = styled.div` + width: 28px; + height: 28px; + flex-shrink: 0; + flex-direction: row; + border-radius: 29px; + background: #d0d6d9; + text-align: center; +` +const BCMProfileimg = styled.img` + width: 28px; + height: 28px; + flex-shrink: 0; + vertical-align: initial; +` +const BCMProfileUsername = styled.label` + display: flex; + width: 100%; + flex-direction: column; + justify-content: center; + flex-shrink: 0; + color: #000; + font-size: 14px; + + font-style: normal; + font-weight: 500; + line-height: 120%; +` +const BCMMessage = styled.div` + display: flex; + flex-direction: column; + align-self: stretch; + color: #686b6d; + font-size: 14px; + + font-style: normal; + font-weight: 400; + line-height: 120%; +` +const BFooter = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 4px; + align-self: stretch; +` +const BFootercont = styled.div` + display: flex; + align-items: center; + align-self: stretch; +` +const BFootercontTime = styled.div` + display: flex; + align-items: center; + gap: 4px; + flex: 1 0 0; +` +const BFCTimetext = styled.div` + display: flex; + height: 19.394px; + flex-direction: column; + justify-content: center; + flex: 1 0 0; + color: #000; + font-size: 14px; + + font-style: normal; + font-weight: 300; + line-height: normal; +` +const BFCButton = styled.div` + display: flex; + justify-content: flex-end; + align-items: center; + gap: 4px; +` +const BFCButtonitem = styled.button` + display: flex; + padding: 2px 12px; + align-items: center; + gap: 6px; + border-radius: 4px; + border-width: 1px; + border: solid 1px #9333ea; + + background-image: linear-gradient(#fff, #fff), + radial-gradient(circle at top left, #f0e1ce, #f0e1ce); + background-origin: border-box; + background-clip: padding-box, border-box; +` +const BFCBIText = styled.label` + font-size: 12px; + + font-style: normal; + font-weight: 500; + line-height: 24px; + color: #9333ea; + cursor: pointer; +` +const NewComment = styled.textarea` + width: 100%; + display: flex; + height: 100px; + padding: 9px 10px 0px 10px; + align-items: flex-start; + + gap: 10px; + align-self: stretch; + border-radius: 8px; + border: 1px solid #d0d6d9; + background: #fff; + + font-size: 12px; + + font-style: normal; + font-weight: 400; + line-height: 120%; +` +const CommentFooter = styled.div` + display: flex; + flex-direction: row; + align-items: flex-end; + justify-content: end; + gap: 16px; + align-self: stretch; +` +const CFCancel = styled.button` + display: flex; + width: 107px; + padding: 8px 12px; + justify-content: center; + align-items: center; + gap: 10px; + color: #9333ea; + border-radius: 10px; + border-width: 1px; + border: solid 1px #9333ea; + + background-image: linear-gradient(#fff, #fff), + radial-gradient(circle at top left, #f0e1ce, #f0e1ce); + background-origin: border-box; + background-clip: padding-box, border-box; + @media only screen and (max-width: 480px) { + width: 100%; + } +` + +const CFSubmit = styled.button` + display: flex; + width: 107px; + padding: 8px 12px; + justify-content: center; + align-items: center; + gap: 10px; + color: #000; + display: flex; + width: 107px; + padding: 8px 12px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 10px; + border-width: 1px; + border: solid 1px transparent; + + background-image: linear-gradient(#ffd50d, #ffd50d), + radial-gradient(circle at top left, #f0e1ce, #f0e1ce); + background-origin: border-box; + background-clip: padding-box, border-box; + @media only screen and (max-width: 480px) { + width: 100%; + } +` + +const Spinner = styled.div` + height: 1rem; + width: 1rem; +` + +const CallLibrary = styled.div` + display: none; +` + +State.init({ + theme, + reply: '', + cancel: false, + e_message: '', +}) + +function getShouldDisplayOriginalComment() { + return ( + (!editionData && replyingTo) || + (editionData && + replyingTo && + editionData.value.metadata.id !== editionData.value.metadata.rootId) + ) +} + +function getInitialText() { + if (editionData) { + if ( + !state.reply || + editionData.value.commentData.text === state.reply + ) { + return editionData.value.commentData.text + } + } else if (replyingTo) { + return `@${replyingTo} ` + } else if ( + state.reply && + state.reply !== editionData.value.commentData.text + ) { + return state.reply + } else { + return 'Reply here' + } +} + +const SetText = (txt) => { + State.update({ shareText: txt }) +} + +const renderSpinner = () => { + return +} + +function onCommit() { + setLoadingComments && setLoadingComments(true) + setTimeout(() => { + loadComments && loadComments() + State.update({ reply: 'Reply here', showSpinner: false }) + onCloseModal() + }, 3000) +} + +function onCancel() { + setLoadingComments(false) + State.update({ showSpinner: false }) +} + +function handleSubmitButton() { + if (state.showSpinner) { + return () => {} + } else { + if (isEdition) { + return editCommentListener + } else { + return addCommentListener + } + } +} + +function addCommentListener() { + State.update({ showSpinner: true }) + + createComment({ + config: getConfig(isTest), + author: context.accountId, + commentText: state.reply, + replyingTo: rootId, + articleId: article.value.metadata.id, + onCommit, + onCancel, + }) +} + +function editCommentListener() { + State.update({ showSpinner: true }) + const comment = originalComment + comment.value.commentData.text = state.reply + + editComment({ + config: getConfig(isTest), + comment, + onCommit, + onCancel, + }) +} + +return ( + + +

+ {isReplying + ? 'Reply to comment' + : isEdition + ? 'Edit comment' + : 'Add a Comment'} +

+ + {getShouldDisplayOriginalComment() && ( + <> + + + + + + + + + {replyingTo + ? '@' + replyingTo + : '@user.near'} + + + + {originalComment && + originalComment.value.commentData + .text} + + + + + + + + schedule + + + + +
+ + )} +
+ + State.update({ + reply: e, + }), + }} + /> +
+ + + + ), + }, + }} + /> + +
+
+
+) diff --git a/src/Cheddar/AllArticlesSortByAuthors.jsx b/src/Cheddar/AllArticlesSortByAuthors.jsx new file mode 100644 index 0000000..3770d16 --- /dev/null +++ b/src/Cheddar/AllArticlesSortByAuthors.jsx @@ -0,0 +1,56 @@ +//Cheddar.AllArticlesSortByAuthors + +const { + finalArticles, + tabs, + widgets, + handleOpenArticle, + handleFilterArticles, + authorForWidget, +} = props + +const allFinalArticles = finalArticles + +const articlesAuthors = + allFinalArticles.length && + Array.from(allFinalArticles, (article) => article.value.metadata.author) + +let authors = [...new Set(articlesAuthors)] + +let articlesByAuthorsArray = [] +authors.map((author) => { + let thisAuthorArtciles = allFinalArticles.filter( + (article) => article.value.metadata.author == author + ) + articlesByAuthorsArray.push(thisAuthorArtciles) +}) + +return ( +
+
Total authors: {articlesByAuthorsArray.length}
+ +
+ {articlesByAuthorsArray && + articlesByAuthorsArray.map((authorArticlesArray) => { + const filter = { + filterBy: 'author', + value: authorArticlesArray[0].value.metadata.author, + } + return ( + + ) + })} +
+
+) diff --git a/src/Cheddar/ArticleHistory/Container.jsx b/src/Cheddar/ArticleHistory/Container.jsx new file mode 100644 index 0000000..5eba7cf --- /dev/null +++ b/src/Cheddar/ArticleHistory/Container.jsx @@ -0,0 +1,81 @@ +// Cheddar.ArticleHistory.Container + +const { + pathToCurrentArticle, + pathToPrevArticle, + currentBlockHeight, + currentVersionData, + allVersionsData, + prevBlockHeight, + widgets, +} = props + +State.init({}) + +function getDatastring(time) { + const date = new Date(time) + return date.toDateString() + ' ' + date.toLocaleTimeString() +} + +return ( +
+
+ +
+
+
+ changes in block #{currentBlockHeight} +
+ + count inserted lines} + > + + {state.lineCountInserted} + + + + count deleted lines} + > + + {state.lineCountDeleted} + + +
+
+ {getDatastring( + currentVersionData.value.metadata.lastEditTimestamp + )} +
+
+
+
+ { + if ( + state.lineCountDeleted === undefined || + state.lineCountInserted === undefined + ) + State.update({ lineCountDeleted, lineCountInserted }) + }, + }} + /> +
+) diff --git a/src/Cheddar/ArticleHistory/Handler.jsx b/src/Cheddar/ArticleHistory/Handler.jsx new file mode 100644 index 0000000..427c2de --- /dev/null +++ b/src/Cheddar/ArticleHistory/Handler.jsx @@ -0,0 +1,203 @@ +//Cheddar.ArticleHistory.Handler +const addressForArticles = 'wikiTest2Article' + +const { articleId, isTest, baseActions, kanbanColumns, widgets, versions } = + props + +State.init({ + selectedTab: 'code', + selectedBlockHeight: null, +}) + +//TODO Need the function getArticlesVersions in the lib.articles + +if (props.count) props.count(versions.length) + +if (!state.selectedBlockHeight && versions.length > 0) + state.selectedBlockHeight = versions[0].blockHeight + +const renderBlockChangesLink = (version) => { + if (!version) + return ( + + ) + + const timeLastEdit = new Date(version.value.metadata.lastEditTimestamp) + + return ( +
+ +
+ ) +} + +function renderWidgetCode(blockHeight) { + const currentVersionDisplayed = versions.find( + (version) => version.blockHeight === blockHeight + ) + const index = versions.findIndex((el) => el.blockHeight === blockHeight) + const prevBlockHeightObject = versions[index + 1] + return ( + + ) +} + +function blockHeightToWidgetRender(blockHeight, allArticles) { + const index = versions.findIndex((el) => el.blockHeight === blockHeight) + return +} + +function articleHistoryHasndlerStateUpdate(obj) { + State.update(obj) +} + +//styles forked from calebjacob.near/widget/Activity +const Tabs = styled.div` + display: flex; + padding: 0 12px; + height: 48px; + border-bottom: 1px solid #eceef0; +` + +const TabsButton = styled.button` + font-weight: 400; + font-size: 14px; + line-height: 17px; + padding: 0 12px; + position: relative; + color: ${(p) => (p.selected ? '#11181C' : '#687076')}; + background: none; + border: none; + outline: none; + + &:hover { + color: #11181c; + } + + &::after { + content: ''; + display: ${(p) => (p.selected ? 'block' : 'none')}; + position: absolute; + bottom: 0; + left: 12px; + right: 12px; + height: 3px; + background: #0091ff; + } +` + +return ( + <> +
+

Article History

+ {!versions ? ( +
incorrent widget path
+ ) : ( +
+
+

+ {versions.length} Commits +

+
+ {versions + .slice(0, 5) + .map((height) => + renderBlockChangesLink(height) + )} +
+ {versions + .slice(5) + .map((height) => + renderBlockChangesLink(height) + )} +
+ {versions.length > 5 && ( + + )} +
+
+ + + + State.update({ + selectedTab: 'code', + }) + } + selected={state.selectedTab === 'code'} + > + Code + + + + State.update({ + selectedTab: 'render', + }) + } + selected={state.selectedTab === 'render'} + > + Render + + + + {state.selectedTab === 'code' && ( +
{renderWidgetCode(state.selectedBlockHeight)}
+ )} + + {state.selectedTab === 'render' && ( +
+ {blockHeightToWidgetRender( + state.selectedBlockHeight, + versions + )} +
+ )} +
+ )} +
+ +) diff --git a/src/Cheddar/ArticleHistory/SecondContainer.jsx b/src/Cheddar/ArticleHistory/SecondContainer.jsx new file mode 100644 index 0000000..a327c09 --- /dev/null +++ b/src/Cheddar/ArticleHistory/SecondContainer.jsx @@ -0,0 +1,32 @@ +// Cheddar.ArticleHistory.SecondContainer + +const { + pathToCurrentArticle, + pathToPrevArticle, + currentBlockHeight, + currentVersionData, + allVersionsData, + prevBlockHeight, +} = props + +if (!pathToCurrentArticle || !pathToPrevArticle || !currentBlockHeight) + return 'send pathToCurrentArticle and pathToPrevArticle and currentBlockHeight in props' + +const currentCode = currentVersionData.value.articleData.body + +if (currentCode === null) return 'Loading' + +const prevCode = prevBlockHeight + ? allVersionsData.find( + (versionData) => versionData.blockHeight == prevBlockHeight + ).body + : undefined + +if (prevCode === null) return 'Loading' + +return ( + +) diff --git a/src/Cheddar/ArticleView.jsx b/src/Cheddar/ArticleView.jsx new file mode 100644 index 0000000..c0b3a93 --- /dev/null +++ b/src/Cheddar/ArticleView.jsx @@ -0,0 +1,1047 @@ +// Cheddar.ArticleView +const promise = new Promise((resolve, reject) => { + setTimeout(() => { + resolve([]) + }, 1) +}) +const { getComments } = VM.require( + 'chatter.cheddar.near/widget/lib.comment' +) || { + getComments: () => promise, +} +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) || { getConfig: () => {} } +const { getUpVotes } = VM.require( + 'chatter.cheddar.near/widget/lib.upVotes' +) || { getUpVotes: () => promise } +const { getArticlesVersions } = VM.require( + 'chatter.cheddar.near/widget/lib.article' +) || { getArticlesVersions: () => promise } + +const { HumanityWrapperButton, isHuman } = VM.require( + 'chatter.cheddar.near/widget/lib.nadaBot' +) || { HumanityWrapperButton: () => <>, isHuman: () => {} } + +const { + widgets, + isTest, + handleFilterArticles, + articleToRenderData, + authorForWidget, + handleEditArticle, + handleDeleteArticle, + handleShareButton, + baseActions, + kanbanColumns, + sharedData, +} = props + +const accountId = articleToRenderData.value.metadata.author +const id = + articleToRenderData.value.metadata.id ?? + `article/${articleToRenderData.value.metadata.author}/${articleToRenderData.value.metadata.createdTimestamp}` + +if ( + !Array.isArray(articleToRenderData.value.articleData.tags) && + typeof articleToRenderData.value.articleData.tags === 'object' +) { + articleToRenderData.value.articleData.tags = Object.keys( + articleToRenderData.value.articleData.tags + ) +} + +articleToRenderData.value.articleData.tags = + articleToRenderData.value.articleData.tags.filter( + (tag) => tag !== undefined && tag !== null + ) + +const tabs = [ + { + id: 'generalInfo', + title: 'Post info', + icon: 'bi bi-primary-circle', + }, +] + +//To slice the article body and show the showMore button just uncoment the sliceContent: true, un the State.init +State.init({ + tabSelected: tabs[0].id, + // sliceContent: true, +}) + +const [comments, setComments] = useState(undefined) +const [loadingComments, setLoadingComments] = useState(true) +const [versions, setVersions] = useState([]) + +if (versions.length === 0) { + try { + const versionsPromise = getArticlesVersions( + getConfig(isTest), + articleToRenderData.value.metadata.id + ) + versionsPromise.then((newVersions) => { + setVersions(newVersions) + }) + } catch (err) { + return console.error('Error in article history handler: ', err) + } +} + +function loadComments() { + const articleId = articleToRenderData.value.metadata.id + getComments(articleId, getConfig(isTest)).then((newComments) => { + setComments(newComments) + setLoadingComments(false) + }) +} + +useEffect(() => { + loadComments() + setInterval(() => { + loadComments() + }, 30000) +}, []) + +const [upVotes, setUpVotes] = useState(undefined) +const [loadingUpVotes, setLoadingUpVotes] = useState(true) + +function loadUpVotes() { + getUpVotes(getConfig(isTest), id).then((newVotes) => { + setUpVotes(newVotes) + setLoadingUpVotes(false) + }) +} + +loadUpVotes() + +const timeLastEdit = new Date( + articleToRenderData.value.metadata.lastEditTimestamp +) + +const CursorPointer = styled.div` + margin-bottom: 0.5rem; + cursor: pointer; +` + +const DetailContent = styled.div` + display: inline-flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; +` + +const TagContainer = styled.div` + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: 4px; + margin-top: 1rem; +` + +const HouseTagDiv = styled.div` + display: flex; + padding: 4px 8px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 100px; + background: var( + --gradient-purple-gradient, + linear-gradient(90deg, #9333ea 0%, #4f46e5 100%) + ); +` + +const HouseTagText = styled.p` + color: #fff; + font-size: 7px; + font-weight: 500; + line-height: 120%; + margin: 0px; +` + +const TagDiv = styled.div` + display: flex; + justify-content: center; + padding: 4px 8px; + align-items: center; + gap: 10px; + border-radius: 100px; + border: solid 1px transparent; + border-radius: 80px; + background-image: linear-gradient(#eae5f7, #eae5f7), + radial-gradient(circle at top left, #9333ea 0%, #4f46e5 100%); + background-origin: border-box; + background-clip: padding-box, border-box; +` + +const TagDivText = styled.p` + font-size: 8px; + margin: 0px; + font-weight: 500; + line-height: 120%; + background: linear-gradient(90deg, #9333ea 0%, #4f46e5 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + text-fill-color: transparent; +` + +const NominationTitleContainer = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; +` + +const NominationTitle = styled.p` + display: flex; + flex-direction: column; + justify-content: center; + margin: 7px 0 0 0; + color: #000; + font-size: 18px; + font-weight: 500; + line-height: 120%; +` +const UserLink = styled.a` + cursor: pointer; + &:hover { + text-decoration: none; + } +` +const NominationUser = styled.p` + display: flex; + flex-direction: column; + justify-content: center; + color: #828688; + margin: 0 0 7px 0; + font-size: 14px; + line-height: 120%; +` + +const UpvoteButtonDisabled = styled.button` + display: flex; + padding: 2px 12px; + align-items: center; + gap: 6px; + border-radius: 4px; + border: solid 1px transparent; + background: var(--buttons-disable, #c3cace); + cursor: default !important; +` + +const UpvoteButton = styled.button` + display: flex; + padding: 2px 12px; + align-items: center; + gap: 6px; + border-radius: 4px; + border: solid 1px transparent; + background-image: linear-gradient(#f8f8f9, #f8f8f9), + radial-gradient( + circle at left top, + rgb(147, 51, 234) 0%, + rgb(79, 70, 229) 100% + ); + background-origin: border-box; + background-clip: padding-box, border-box; +` + +const UpvoteCount = styled.p` + font-size: 12px; + font-weight: 500; + line-height: 24px; + margin: 0px; + background: linear-gradient(90deg, #9333ea 0%, #4f46e5 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + text-fill-color: transparent; +` + +const Icon = styled.img` + width: 17px; + height: 17px; +` +const BodyContainer = styled.div` + border-radius: 8px; + margin: 10px 0; + background: #f8f8f9; +` + +const PlatformCard = styled.div` + display: flex; + border-radius: 6px; + background: background: "rgb(255 255 255 / 0%); + ` + +const PlatformContent = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + width: 100%; +` + +const PlatformInfoDiv = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; +` + +const PlatformInfoHeader = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 2px; +` + +const PlatInforHeadText = styled.p` + margin: 0px; + color: var(--000000, #000); + font-size: 10px; + font-weight: 500; + line-height: 120%; +` + +const PlatInfoHeadSeparator = styled.hr` + height: 0px; + margin: 8px 0 0 0; + + border: 1px solid rgba(208, 214, 217, 1); +` + +const KeyIssuesContainer = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 2px; +` + +const KeyIssueTitle = styled.p` + font-size: 12px; + line-height: 120%; + margin: 0px; + font-weight: 500; + line-height: 18px; + text-align: left; + padding: 10px; +` + +const KeyIssueDescription = styled.p` + color: #212427; + font-size: 12px; + line-height: 130%; + margin: 0px; + padding: 10px; + line-height: 18px; + text-align: justify; +` + +const CandidateCard = styled.div` + display: flex; + padding: 20px; + align-items: center; + align-self: stretch; + border-radius: 6px; + background: #fff; +` + +const CandidateContent = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 12px; +` + +const ContentHeader = styled.div` + display: flex; + align-items: center; + gap: 12px; + align-self: stretch; +` + +const ContentHeaderText = styled.p` + font-size: 18px; + font-weight: 500; + margin: 0px; +` + +const CandidateInfoDiv = styled.div` + width: 100%; + padding: 16px; + background: white; + gap: 16px; + border-radius: 8px; +` + +const CandidateInfoHeader = styled.div` + display: flex; + align-items: center; + gap: 8px; + align-self: stretch; +` + +const CandidateImage = styled.img` + width: 32px; + height: 32px; +` + +const CandidateInfoData = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + flex: 1 0 0; +` + +const CandidateTagDiv = styled.div` + display: flex; + height: 20px; + padding: 4px 8px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 100px; + border: 1px solid var(--secondary-warning, #f19d38); + background: #f0e1ce; +` + +const CandidateTagText = styled.p` + color: var(--secondary-warning, #f19d38); + font-size: 10px; + font-weight: 500; + line-height: 120%; + margin: 0px; +` + +const CandidateTime = styled.h6` + margin: 3px 0 0 0; + font-size: 10px; + font-weight: 500; + line-height: 120%; + color: #828688; +` + +const CandidateTextInfo = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + align-self: stretch; +` + +const SectionTitle = styled.h5` + font-size: 12px; + font-weight: 500; + line-height: 120%; + margin: 16px 0 0 0; +` + +const SectionDescription = styled.p` + font-size: 12px; + line-height: 18px; + margin: 0px; + text-align: justify; + color: #828688; +` + +const DescriptionSubtitle = styled.h5` + display: inline-block; + font-size: 12px; + line-height: 120%; + margin-right: 0.3rem; +` + +const DescriptionInfoSpan = styled.span` + font-size: 12px; + line-height: 18px; + margin: 0px; + text-align: justify; + color: #828688; +` + +const DeclarationCard = styled.div` + padding: 0px; +` + +const CommentSection = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + border-radius: 8px; + background: #f8f8f9; +` + +const Container = styled.div` + display: flex; + margin-right: 5px; + justify-content: center; +` + +const SecondContainer = styled.div` + background: #f8f8f9; + border-radius: 8px; + padding: 20px; +` + +const H6 = styled.h6` + font-size: 14px; + margin-bottom: 0; +` + +const Tab = styled.div` + font-weight: ${(props) => (props.active ? '600' : '500')}; + border-bottom: 2px solid; + border-color: ${(props) => (props.active ? '#ffd50d' : '#dee2e6')}; + color: ${(props) => (props.active ? '#ffd50d' : '#ababab')}; + cursor: pointer; + padding-bottom: 8px; + font-size: 14px; + + i { + &::before { + color: ${(props) => (props.active ? '#ffd50d' : '#ababab')}; + } + margin-right: 5px; + } +` + +const TH = styled.th` + border: 1px solid rgba(208, 214, 217, 0.4) !important; + text-align: left !important; + padding: 15px 20px !important; +` + +const CallLibrary = styled.div` + display: none; +` + +const HeaderButtonsContainer = styled.div` + display: flex; + justify-content: flex-end; + @media only screen and (max-width: 768px) { + justify-content: flex-start; + } + + gap: 0.5rem; +` + +const EditDeleteButtonsContainer = styled.div` + display: flex; + gap: 0.5rem; +` + +const AcordionContainer = styled.div` + --bs-accordion-border-width: 0px !important; +` + +const NoMargin = styled.div` + margin: 0 0.75rem; +` + +const AccordionBody = styled.div` + padding: 0; +` + +//Get basic original comments info +const rootComments = comments + ? comments.filter((comment) => comment.value.metadata.rootId === id) + : [] + +//Append answers to original comments +const articleComments = rootComments.map((rootComment) => { + let answers = comments.filter((comment) => { + return comment.value.metadata.rootId === rootComment.value.metadata.id + }) + + return { + ...rootComment, + answers, + } +}) + +function stateUpdate(obj) { + State.update(obj) +} + +function getUserName() { + const profile = data.authorProfile + + return profile.name ?? getShortUserName() +} + +const getShortUserName = () => { + const userId = accountId + + if (userId.length === 64) + return `${userId.slice(0, 4)}..${userId.slice(-4)}` + const name = userId.slice(0, -5) // truncate .near + + return name.length > 20 ? `${name.slice(0, 20)}...` : name +} + +let displayedContent = state.sliceContent + ? articleToRenderData.value.articleData.body.slice(0, 1000) + : articleToRenderData.value.articleData.body + +return ( + <> + {sharedData.sharedCommentId && ( + + Click to redirect to comment that mentioned you + + )} + + + +

+ +

+
+ +
+ +
+
+
+
+
+ + +
+
+
+
+
+ + + {articleToRenderData.value.articleData.tags + .length > 0 && + articleToRenderData.value.articleData.tags.map( + (tag) => { + const filter = { + filterBy: 'tag', + value: tag, + } + return ( + + handleFilterArticles( + filter + ) + } + > + + + ) + } + )} + +
+
+
+ + + + + + ), + onClick: () => + handleShareButton(true, { + key: 'said', + type: 'sharedArticleId', + value: articleToRenderData + .value.metadata.id, + }), + }} + /> + + + + + {context.accountId == accountId && ( + + + + Edit + + +
+ ), + className: `primary outline mt-2`, + onClick: () => { + handleEditArticle( + articleToRenderData + ) + }, + }} + /> + + +
+ ), + className: `danger outline mt-2`, + onClick: () => + handleDeleteArticle( + articleToRenderData + ), + }} + /> + + )} +
+
+
+
+ + + + + + { + articleToRenderData.value + .articleData.title + } + + + + ( + + + #{hashtag} + + + ), + }} + /> + {state.sliceContent && + articleToRenderData.value.articleData.body + .length > 1000 && ( + + + Show more + + + + ), + size: 'sm', + className: 'w-100', + onClick: () => { + State.update({ + sliceContent: false, + }) + }, + }} + /> + )} + + + + +
+ + + {' '} + Comments + + + + {state.showModal && ( + + State.update({ showModal: false }), + baseActions, + loadComments, + setLoadingComments, + }} + /> + )} + + + + Add comment + + +
+ ), + disabled: !context.accountId, + className: + 'primary outline w-100 mt-4 mb-2', + onClick: () => { + State.update({ showModal: true }) + }, + }} + /> + + + {loadingComments ? ( + + ) : ( + articleComments.map((data) => ( + + )) + )} +
+ + + + <> +
    + {tabs.map(({ id, title, icon }, i) => ( +
  • + + State.update({ tabSelected: id }) + } + > + + {title} + +
  • + ))} +
+
+ {state.tabSelected == 'generalInfo' && ( + + +
+ + Created by: + + + {articleToRenderData.authorProfile + .name ?? getShortUserName()} + +
+
+ + Edited on: + + + {timeLastEdit + ''} + +
+
+ + Edit versions: + + + {versions.length ?? 0} + +
+
+ )} +
+ +
+
+ +) diff --git a/src/Cheddar/ArticlesByAuthorCard.jsx b/src/Cheddar/ArticlesByAuthorCard.jsx new file mode 100644 index 0000000..e893eef --- /dev/null +++ b/src/Cheddar/ArticlesByAuthorCard.jsx @@ -0,0 +1,53 @@ +//Cheddar.ArticlesByAuthorCard + +const { authorArticlesArray, filter, handleFilterArticles, widgets } = props + +const CardContainer = styled.a` + color: black; + font-size: 16px; + line-height: 19.2px; + font-family: inherit; + box-shadow: 0px 0px 30px 0px #0000000d; + cursor: pointer; + with: fit-content; + min-width: 18rem; + display: flex; + flex-wrap: nowrap; + + &:hover { + color: white; + text-decoration: none; + background: linear-gradient( + 90deg, + rgba(147, 51, 234, 1) 0%, + rgba(79, 70, 229, 1) 100% + ); + } +` + +const ImgContainer = styled.div` + border-radius: 20px; + overflow: hidden; +` + +return ( +
+ handleFilterArticles(filter)} + > + + + + {authorArticlesArray.length} articles + +
+) diff --git a/src/Cheddar/CommentView.jsx b/src/Cheddar/CommentView.jsx new file mode 100644 index 0000000..b369756 --- /dev/null +++ b/src/Cheddar/CommentView.jsx @@ -0,0 +1,631 @@ +// Cheddar.CommentView +const { deleteComment } = VM.require( + 'chatter.cheddar.near/widget/lib.comment' +) || { deleteComment: () => {} } +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) || { getConfig: () => {} } + +const { HumanityWrapperButton, isHuman } = VM.require( + 'chatter.cheddar.near/widget/lib.nadaBot' +) || { HumanityWrapperButton: () => <>, isHuman: () => {} } + +const { + widgets, + data, + isTest, + authorForWidget, + isReply, + orginalCommentData, + canLoggedUserCreateComment, + baseActions, + sharedData, + articleToRenderData, + loadComments, + setLoadingComments, +} = props + +State.init({ + showModal: false, + hasReply: false, +}) + +function stateUpdate(obj) { + State.update(obj) +} + +const CallLibrary = styled.div` + display: none; +` + +const CommentCard = styled.div` + margin-left: ${isReply ? '2rem' : '0'}; + display: flex; + padding: 14px 16px; + flex-direction: column; + align-items: flex-start; + gap: 12px; + border-radius: "10px"}; + background: ${ + sharedData.sharedCommentId === data.value.metadata.id + ? 'rgba(194, 205, 255, 0.8)' + : '#fff' + }; + width: 100%; + ` + +const CommentCardHeader = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + width: 100%; +` + +const CommentUserContent = styled.div` + display: flex; + align-items: center; + gap: 8px; +` + +const CommentEdition = styled.div` + display: flex; + align-items: center; + gap: 8px; +` + +const ProfileImageComment = styled.img` + width: 20px; + height: 20px; + flex-shrink: 0; + border-radius: 20px; +` + +const CommentUser = styled.p` + color: #000; + font-size: 12px; + font-weight: 500; + line-height: 120%; + margin: 0px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +` + +const ReplyCounterDiv = styled.div` + display: flex; + align-items: center; + gap: 4px; +` + +const ReplyIconPurple = styled.img` + width: 14px; + height: 14px; +` + +const ReplyCounterText = styled.p` + color: #000; + font-size: 10px; + font-weight: 500; + margin: 0px; +` + +const CommentCardContent = styled.p` + color: #585b5c; + font-size: 12px; + line-height: 18px; + display: flex; + flex-direction: column; + margin: 0px; +` + +const CommentCardLowerSection = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + gap: 4px; + width: 100%; +` + +const TimestampCommentDiv = styled.div` + display: flex; + align-items: center; + gap: 4px; + flex: 1 0 0; +` + +const TimestampIconComment = styled.img` + width: 12px; + height: 12px; +` + +const TimestampTextComment = styled.span` + color: #000; + font-size: 10px; + font-weight: 300; + margin: 0px; +` + +const EditedIndication = styled.span` + font-size: 12px; + margin: 0px; +` + +const DeleteCommentButton = styled.button` + display: flex; + width: 28px; + padding: 2px 12px; + justify-content: center; + align-items: center; + gap: 6px; + align-self: stretch; + border-radius: 4px; + border: 1px solid #c23f38; + background: #f1d6d5; +` + +const DeleteCommentIcon = styled.img` + width: 14px; + height: 14px; + flex-shrink: 0; +` + +const ShareCommentButton = styled.button` + display: flex; + width: 28px; + height: 28px; + padding: 2px 12px; + justify-content: center; + align-items: center; + gap: 6px; + border-radius: 4px; + border: solid 1px transparent; + background-image: linear-gradient(white, white), + radial-gradient(circle at top left, #9333ea 0%, #4f46e5 100%); + background-origin: border-box; + background-clip: padding-box, border-box; +` + +const ShareCommentIcon = styled.img` + width: 14px; + height: 14px; + flex-shrink: 0; +` + +const ReplyCommentButtonDisabled = styled.div` + display: flex; + padding: 2px 12px; + cursor: not-allowed; + background: rgb(195, 202, 206); + color: rgb(130, 134, 136); + border: 0px; + align-items: center; + gap: 6px; + align-self: stretch; + border-radius: 4px; +` + +const ReplyCommentButtonActive = styled.div` + cursor: pointer; + display: flex; + padding: 2px 12px; + align-items: center; + gap: 6px; + align-self: stretch; + border-radius: 4px; + background: var(--buttons-yellow-default, #ffd50d); +` + +const ReplyCommentText = styled.p` + color: var(--primary-black, #000); + font-size: 12px; + font-weight: 500; + line-height: 24px; + margin: 0px; +` + +const CommentReplySeparator = styled.hr` + height: 0px; + margin: 16px 0 16px 0; + border: 1px solid rgba(208, 214, 217, 1); +` + +const ReplyContainer = styled.div` + display: flex; + width: 260px; + flex-direction: column; + align-items: flex-start; + gap: 12px; + margin: 0 0 0 35px; +` + +const ReplyHeader = styled.div` + display: flex; + align-items: center; + gap: 8px; + align-self: stretch; +` + +const ReplyContent = styled.p` + color: #828688; + font-size: 12px; + line-height: 120%; + margin: 0px; +` + +const ReplyLowerSection = styled.div` + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 4px; +` + +const ReplyButtonSection = styled.div` + display: flex; + justify-content: flex-end; + align-items: center; + gap: 4px; +` + +const DeleteReplyButton = styled.button` + display: flex; + padding: 2px 12px; + align-items: center; + gap: 6px; + align-self: stretch; + border-radius: 4px; + border: 1px solid #c23f38; + background: #f1d6d5; +` + +const DeleteReplyText = styled.p` + color: #c23f38; + font-size: 12px; + font-weight: 500; + line-height: 24px; + margin: 0px; +` + +const AnswerContainer = styled.div` + width: 96%; +` + +const renderDeleteModal = () => { + const ModalCard = styled.div` + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + background: rgba(0, 0, 0, 0.7); + ` + const ModalContainer = styled.div` + display: flex; + width: 400px; + padding: 20px; + flex-direction: column; + align-items: flex-start; + gap: 16px; + border-radius: 10px; + background: #fff; + border: 1px solid transparent; + margin-left: auto; + margin-right: auto; + margin-buttom: 50%; + @media only screen and (max-width: 480px) { + width: 90%; + } + ` + const Container = styled.div` + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 20px; + align-self: stretch; + ` + const Footer = styled.div` + display: flex; + flex-direction: row; + align-items: flex-end; + justify-content: end; + gap: 16px; + align-self: stretch; + ` + + return ( + + + +

Delete this comment?

+
+ + +
+
+
+
+ ) +} + +function onCommitDeleteComment() { + State.update({ + showDeleteModal: false, + }) + setLoadingComments(true) + setTimeout(() => { + loadComments() + }, 3000) +} + +function closeDeleteCommentModal() { + State.update({ + showDeleteModal: false, + }) +} + +function deleteCommentListener() { + State.update({ saving: true }) + + deleteComment({ + config: getConfig(isTest), + commentId: data.value.metadata.id, + articleId: data.value.metadata.articleId, + rootId: data.value.metadata.rootId, + onCommit: onCommitDeleteComment, + onCancel: closeDeleteCommentModal, + }) +} + +function handleDeleteComment() { + State.update({ + showDeleteModal: true, + }) +} + +function closeModal() { + State.update({ showModal: false }) +} + +function getProperRootId(isEdition) { + if (isEdition) { + return data.value.metadata.rootId + } + + if (data.answers) { + return data.value.metadata.id + } else { + return data.value.metadata.rootId + } +} + +function handleEditComment() { + State.update({ + showModal: true, + editionData: data, + rootId: getProperRootId(true), + }) +} + +function handleReplyListener() { + State.update({ + showModal: true, + editionData: undefined, + rootId: getProperRootId(false), + }) +} + +return ( + <> + {state.showDeleteModal && renderDeleteModal()} + + + + + + + {context.accountId == data.accountId && ( + + + Edit + + + ), + className: `primary outline mt-2`, + onClick: () => handleEditComment(), + }} + /> + + + + ), + className: `danger outline mt-2`, + onClick: () => handleDeleteComment(), + }} + /> + + )} + + + ( + + + #{hashtag} + + + ), + }} + /> + + + + + + {new Date( + data.value.metadata.lastEditTimestamp + ).toDateString()} + + {data.isEdition && ( + + (edited) + + )} + +
+ {state.showModal && ( + + )} + { + + + Reply + +
+ ), + size: 'sm', + className: 'primary outline', + onClick: handleReplyListener, + }} + /> + + } + + + + +
+
+ {!isReply && ( + <> + {data.answers.length > 0 && ( + + )} + {data.answers.map((answer) => { + return ( + + + + ) + })} + + )} + +) diff --git a/src/Cheddar/CompactPost.jsx b/src/Cheddar/CompactPost.jsx new file mode 100644 index 0000000..9295f83 --- /dev/null +++ b/src/Cheddar/CompactPost.jsx @@ -0,0 +1,331 @@ +// Cheddar.CompactPost + +const { + widgets, + article, + kanbanColumns, + handleOpenArticle, + handleFilterArticles, + handleShareButton, + colLabel, + baseActions, +} = props + +State.init({ + showModal: false, +}) + +function compactPostStateUpdate(obj) { + State.update(obj) +} + +function onCommit() { + State.update({ showModal: false }) +} + +function onCancel() { + State.update({ showModal: false }) +} + +function moveArticleListener() { + //TODO add createArticle function +} + +function getArticleData() { + let newTags = article.value.articleData.tags.filter((tag) => { + let lowerCaseTag = tag.toLowerCase().replace(` `, '-') + + const lowercaseLabels = [] + kanbanColumns.forEach((col) => + lowercaseLabels.push(col.toLowerCase().replace(` `, '-')) + ) + + return !lowercaseLabels.includes(lowerCaseTag) + }) + + newTags.push(state.newLabel.toLowerCase().replace(` `, '-')) + + let newArticleData = article + newArticleData.tags = newTags + + return newArticleData +} + +const Spinner = () => { + return ( + + ) +} + +const ModalContainer = styled.div` + position: fixed; + display: flex; + justify-content: center; + align-items: center; + top: 0; + left: 0; + height: 100%; + width: 100%; + backdrop-filter: blur(10px); + z-index: 1; +` + +const ChangeLabelMainContainer = styled.div` + display: flex; + flex-direction: column; + background: white; + padding: 1rem; + border-radius: 12px; + border: 1px solid black; +` + +const ClosePopUpContainer = styled.div` + display: flex; + flex-direction: row-reverse; +` + +const CloseIcon = styled.div` + cursor: pointer; +` + +function handleLabelSelection(selectedLabel) { + State.update({ + newLabel: selectedLabel.replace(` `, '-'), + }) +} + +function closeModal() { + State.update({ showModal: false }) +} + +const modal = ( + + + + + + { + return { title: label, value: col } + }), + onChange: handleLabelSelection, + }} + /> + + {state.saving ? ( + + ) : ( + <> + Move + + + )} + + ), + }} + /> + + +) + +const header = ( +
+ +
+ +
+
+ , + onClick: () => + handleShareButton(true, { + key: 'sb', + type: 'sharedBlockheight', + value: article.value.metadata + .blockHeight, + }), + }} + /> +
+
+
+
+
+) + +const borders = { + Idea: 'border-secondary', + Comment: 'border-secondary', + Submission: 'border-secondary', + Attestation: 'border-secondary', + Sponsorship: 'border-secondary', +} + +const CursorPointer = styled.p` + cursor: pointer; +` + +function toggleShowModal() { + State.update({ showModal: !state.showModal }) +} + +const articleTags = article.value.articleData.tags ? ( +
+ {article.value.articleData.tags.map((tag) => { + const filter = { filterBy: 'tag', value: tag } + return ( + handleFilterArticles(filter)}> + + + ) + })} +
+) : null + +const articleTitle = ( +
+
+
{article.value.articleData.title}
+
+
+) + +const footerActionButtons = ( +
+ + Move + +
+ ), + disabled: + context.accountId !== article.value.articleData.author, + size: 'sm', + className: 'primary outline w-25', + onClick: toggleShowModal, + }} + /> + + View + + + ), + size: 'sm', + className: 'primary mx-1 w-25', + onClick: () => { + handleOpenArticle(article) + }, + }} + /> + +) + +const CallLibrary = styled.div` + display: block; +` + +const Card = styled.div` + user-select: none; + &:hover { + box-shadow: rgba(3, 102, 214, 0.3) 0px 0px 0px 3px; + } +` + +const LimitedMarkdown = styled.div` + max-height: 6em; +` + +// Should make sure the posts under the currently top viewed post are limited in size. +const descriptionArea = ( + + + +) + +return ( + <> + {state.showModal && modal} + + {header} +
+ {articleTitle} + {descriptionArea} + {articleTags} + {footerActionButtons} +
+
+ +) diff --git a/src/Cheddar/Forum.jsx b/src/Cheddar/Forum.jsx new file mode 100644 index 0000000..f44d59c --- /dev/null +++ b/src/Cheddar/Forum.jsx @@ -0,0 +1,769 @@ +// Cheddar.Forum +const promise = new Promise((resolve, reject) => { + setTimeout(() => { + resolve([]) + }, 1) +}) +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) || { getConfig: () => {} } +const { getArticles, deleteArticle } = VM.require( + 'chatter.cheddar.near/widget/lib.article' +) || { deleteArticle: () => {} } + +if (typeof getArticles !== 'function') { + return <> +} + +//===============================================INITIALIZATION===================================================== +let { + isTest, + accountId, + authorForWidget, + widgets, + brand, + baseActions, + kanbanColumns, + kanbanRequiredTags, + kanbanExcludedTags, + handleChangeCategory, + categories, + category, + sharedData, +} = props + +const [searchInputValue, setSearchInputValue] = useState('') + +accountId = context.accountId + +function getInitialFilter() { + if (sharedData.sharedBlockheight) { + return { + parameterName: 'getPost', + parameterValue: sharedData.sharedBlockheight, + } + } else if (sharedData.sharedTag) { + return { + parameterName: 'tag', + parameterValue: sharedData.sharedTag, + } + } else if (authorShared) { + return { + parameterName: 'author', + parameterValue: authorShared, + } + } else if (sharedData.sharedArticleId) { + return { + parameterName: 'articleId', + parameterValue: sharedData.sharedArticleId, + } + } else { + return { + parameterName: '', + } + } +} + +const [articlesToRender, setArticlesToRender] = useState([]) +const [showShareModal, setShowShareModal] = useState(false) +const [sharedElement, setSharedElement] = useState(undefined) +const [showShareSearchModal, setShowShareSearchModal] = useState(false) +const [sharingSearch, setSharingSearch] = useState(false) +const [linkCopied, setLinkCopied] = useState(false) +const [filterBy, setFilterBy] = useState(getInitialFilter()) +const [loadingArticles, setLoadingArticles] = useState(true) + +function loadArticles(category) { + const userFilters = { category: category } + console.log('Reloading categories', category) + getArticles(getConfig(isTest), userFilters).then((newArticles) => { + setArticlesToRender(newArticles) + setLoadingArticles(false) + }) +} +loadArticles(category) +useEffect(() => { + setLoadingArticles(true) +}, [category]) + +accountId = context.accountId + +const tabs = { + SHOW_ARTICLES_LIST: { id: 0 }, + SHOW_ARTICLE: { id: 1 }, + ARTICLE_WORKSHOP: { id: 2 }, + SHOW_ARTICLES_LIST_BY_AUTHORS: { id: 3 }, + //SHOW_KANBAN_VIEW: { id: 4 }, +} + +function getInitialTabId() { + if (sharedData.sharedBlockheight || sharedData.sharedArticleId) { + return tabs.SHOW_ARTICLE.id + } else { + return tabs.SHOW_ARTICLES_LIST.id + } +} + +State.init({ + displayedTabId: getInitialTabId(), + articleToRenderData: {}, + authorsProfiles: [], + firstRender: + !isNaN(sharedData.sharedBlockheight) || + typeof sharedData.sharedArticleId === 'string', +}) + +//=============================================END INITIALIZATION=================================================== + +//==================================================CONSTS========================================================== + +const profile = props.profile ?? Social.getr(`${accountId}/profile`) + +if (profile === null) { + return 'Loading' +} + +let authorProfile = {} +if (filterBy.parameterName == 'author') { + authorProfile = Social.getr(`${filterBy.parameterValue}/profile`) +} + +const navigationPills = [ + { id: tabs.SHOW_ARTICLES_LIST.id, title: 'Articles' }, + { id: tabs.SHOW_ARTICLES_LIST_BY_AUTHORS.id, title: 'Authors' }, + // { id: tabs.SHOW_KANBAN_VIEW.id, title: "Kanban" }, +] + +const navigationButtons = [ + // { id: tabs.ARTICLE_WORKSHOP.id, title: "+Create article" }, +] + +const initialBodyAtCreation = state.editArticleData.value.articleData.body + +//=================================================END CONSTS======================================================= + +//=================================================GET DATA========================================================= +const finalArticles = state.articles + +function filterArticlesByTag(tag, articles) { + return articles.filter((article) => { + return article.value.articleData.tags.includes(tag) + }) +} + +function filterArticlesByAuthor(author, articles) { + return articles.filter((article) => { + return article.value.metadata.author === author + }) +} + +function filterOnePostByBlockHeight(blockHeight, articles) { + if (articles) { + return articles.filter((article) => article.blockHeight === blockHeight) + } else { + return [] + } +} + +function filterOnePostByArticleId(articleId, articles) { + if (articles) { + return articles.filter( + (article) => article.value.metadata.id === articleId + ) + } else { + return [] + } +} + +if (filterBy.parameterName === 'tag') { + setArticlesToRender( + filterArticlesByTag(filterBy.parameterValue, articlesToRender) + ) +} else if (filterBy.parameterName === 'author') { + setArticlesToRender( + filterArticlesByAuthor(filterBy.parameterValue, articlesToRender) + ) +} else if (filterBy.parameterName === 'getPost') { + setArticlesToRender( + filterOnePostByBlockHeight(filterBy.parameterValue, articlesToRender) + ) + + if (articlesToRender.length > 0) { + State.update({ articleToRenderData: articlesToRender[0] }) + } +} else if (filterBy.parameterName === 'articleId') { + setArticlesToRender( + filterOnePostByArticleId(filterBy.parameterValue, articlesToRender) + ) + if (articlesToRender.length > 0) { + State.update({ articleToRenderData: articlesToRender[0] }) + } +} +//===============================================END GET DATA======================================================= + +//=============================================STYLED COMPONENTS==================================================== +const data = fetch(`https://httpbin.org/headers`) +const gatewayURL = data?.body?.headers?.Origin ?? '' + +const AppContainer = gatewayURL.includes('near.org') + ? styled.div` + width: 100%; + ` + : styled.div` + position: fixed; + inset: 73px 0px 0px; + width: 100%; + overflow-y: scroll; + ` + +const SecondContainer = styled.div` + display: flex; + flex-direction: column; + padding-top: calc(-1 * var(--body-top-padding)); + padding: 0 1rem; + background-color: rgb(248, 248, 249); + min-height: 100vh; +` + +const ShareInteractionGeneralContainer = styled.div` + position: fixed; + display: flex; + justify-content: center; + align-items: center; + top: 0; + left: 0; + height: 100vh; + width: 100vw; + backdrop-filter: blur(10px); + z-index: 1; +` + +const ShareInteractionMainContainer = styled.div` + display: flex; + flex-direction: column; + background: white; + padding: 1rem; + border-radious: 12px; +` + +const ClosePopUpContainer = styled.div` + display: flex; + flex-direction: row-reverse; +` + +const CloseIcon = styled.div` + cursor: pointer; +` + +const PopUpDescription = styled.p` + color: #474d55; +` + +const ShowLinkShared = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + background-color: #f2f6fa; + padding: 1rem 2rem; + border-radius: 17px; +` + +const LinkShared = styled.span` + color: #0065ff; + word-wrap: anywhere; +` + +const ClipboardContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + margin-left: 0.5rem; + min-width: 2.5rem; +` + +const ClipboardIcon = styled.i` + color: ${state.linkCopied ? '#0065FF' : 'black'}; + transition: color 0.3s linear; + cursor: pointer; +` + +const CopiedFeedback = styled.span` + font-size: 0.7rem; + color: #6c757d; +` + +const SmallButton = styled.button` + position: relative; + border: 0; + background: transparent; + width: 35px; + height: 35px; +` +//===========================================END STYLED COMPONENTS================================================== + +//================================================COMPONENTS======================================================== +const renderShareInteraction = () => { + return ( + + + + { + setShowShareSearchModal(false) + setShowShareModal(false) + setLinkCopied(false) + setSharedElement(undefined) + setSharingSearch(false) + }} + > + +

Share

+ + {sharedElement.value + ? 'Use this link to share the article' + : sharingSearch + ? 'Use this link to share the search' + : "Can't share yet. Reload the app and try again."} + + + {(sharedElement.value || sharingSearch) && ( + {getLink()} + )} + + {(sharedElement.value || sharingSearch) && ( + { + clipboard.writeText(getLink()) + setLinkCopied(true) + }} + /> + )} + {linkCopied && Copied!} + + +
+
+ ) +} + +const renderDeleteModal = () => { + const ModalCard = styled.div` + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + background: rgba(0, 0, 0, 0.7); + ` + const ModalContainer = styled.div` + display: flex; + width: 400px; + padding: 20px; + flex-direction: column; + align-items: flex-start; + gap: 16px; + border-radius: 10px; + background: #fff; + border: 1px solid transparent; + margin-left: auto; + margin-right: auto; + margin-buttom: 50%; + @media only screen and (max-width: 480px) { + width: 90%; + } + ` + const Container = styled.div` + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 20px; + align-self: stretch; + ` + const Footer = styled.div` + display: flex; + flex-direction: row; + align-items: flex-end; + justify-content: end; + gap: 16px; + align-self: stretch; + ` + + return ( + + + +

Delete this post?

+
+ + +
+
+
+
+ ) +} + +const getCategoriesSelectorLabel = () => { + return ( + <> + Post & Filter by Categories + + + +

+ Topics for Community SBT Holders. +

+ + } + > + +
+
+ + ) +} +//==============================================END COMPONENTS====================================================== + +//=================================================FUNCTIONS======================================================== +function onCommitDeletArticle() { + setArticlesToRender([]) + setTimeout(() => { + loadArticles() + }, 3000) + setFilterBy({ parameterName: '', parameterValue: {} }) + State.update({ + showDeleteModal: false, + deleteArticleData: undefined, + displayedTabId: tabs.SHOW_ARTICLES_LIST.id, + articleToRenderData: undefined, + editArticleData: undefined, + }) +} + +function deletePostListener() { + State.update({ saving: true }) + const article = state.deleteArticleData + deleteArticle( + getConfig(isTest), + article.value.metadata.id, + onCommitDeletArticle, + closeDeleteArticleModal + ) +} + +function getValidEditArticleDataTags() { + let tags = state.editArticleData.value.articleData.tags ?? [] + let newFormatTags = {} + + tags && + tags.map((tag) => { + newFormatTags[tag] = '' + }) + return newFormatTags +} + +const initialCreateState = { + title: state.editArticleData.value.articleData.title ?? '', + articleBody: + state.editArticleData.value.articleData.body ?? initialBodyAtCreation, + tags: state.editArticleData.value.articleData.tags + ? getValidEditArticleDataTags() + : {}, + libsCalls: { comment: {}, article: {}, emojis: {}, upVotes: {} }, +} + +function handleOpenArticle(articleToRenderData) { + State.update({ + displayedTabId: tabs.SHOW_ARTICLE.id, + articleToRenderData, + editArticleData: undefined, + }) +} + +function handleEditArticle(articleData) { + State.update({ + displayedTabId: tabs.ARTICLE_WORKSHOP.id, + editArticleData: articleData, + }) +} + +function handleDeleteArticle(articleData) { + State.update({ + showDeleteModal: true, + deleteArticleData: articleData, + }) +} + +function closeDeleteArticleModal() { + State.update({ + showDeleteModal: false, + deleteArticleData: undefined, + }) +} + +function handleFilterArticles(filter) { + setFilterBy({ + parameterName: filter.filterBy, + parameterValue: filter.value, + }) + State.update({ + displayedTabId: tabs.SHOW_ARTICLES_LIST.id, + editArticleData: undefined, + }) +} + +function handleBackButton() { + loadArticles() + if (props.editArticleData) { + setFilterBy({ + parameterName: '', + parameterValue: undefined, + handleBackClicked: true, + }) + State.update({ + displayedTabId: tabs.SHOW_ARTICLE.id, + editArticleData: undefined, + firstRender: false, + }) + } else { + setFilterBy({ + parameterName: '', + parameterValue: undefined, + handleBackClicked: true, + }) + State.update({ + displayedTabId: tabs.SHOW_ARTICLES_LIST.id, + articleToRenderData: {}, + editArticleData: undefined, + firstRender: false, + }) + } +} + +function handleGoHomeButton() { + setFilterBy({ parameterName: '', parameterValue: {} }) + State.update({ + displayedTabId: tabs.SHOW_ARTICLES_LIST.id, + articleToRenderData: {}, + editArticleData: undefined, + }) + loadArticles() +} + +function handlePillNavigation(navegateTo) { + State.update({ displayedTabId: navegateTo, editArticleData: undefined }) +} + +function handleShareButton(showShareModal, sharedElement) { + setShowShareModal(showShareModal) + setSharedElement(sharedElement) +} + +function handleShareSearch(showShareSearchModal, newSearchInputValue) { + //showShareSearchModal is a boolean + setShowShareSearchModal(showShareSearchModal) + setSharingSearch(true) + setSearchInputValue(newSearchInputValue ?? '') +} + +function getLink() { + const baseUrl = `https://near.org/${widgets.thisForum}?${ + isTest && 'isTest=true' + }` + if (sharingSearch) { + const link = `${baseUrl}${ + filterBy.parameterName === 'tag' + ? `&st=${filterBy.parameterValue}` + : '' + }${searchInputValue !== '' ? `&ss=${searchInputValue}` : ''}` + return link + } else { + const link = `${baseUrl}&${sharedElement.key}=${sharedElement.value}` + return link + } +} + +function handleOnCommitArticle(articleId) { + setTimeout(() => { + const userFilters = { id: articleId, sbt: undefined } + getArticles(getConfig(isTest), userFilters).then((newArticles) => { + if (newArticles && newArticles.length > 0) { + State.update({ + displayedTabId: tabs.SHOW_ARTICLE.id, + articleToRenderData: newArticles[0], + }) + } + }) + }, 3000) +} +let category2 = category +//===============================================END FUNCTIONS====================================================== +return ( + + + {state.showDeleteModal && renderDeleteModal()} + {(showShareModal || showShareSearchModal) && + renderShareInteraction()} + + {state.displayedTabId == tabs.SHOW_ARTICLES_LIST.id && ( +
+ { + category2 = e + handleChangeCategory(e) + }, + options: categories, + }} + /> +
+ )} + {state.displayedTabId == tabs.SHOW_ARTICLES_LIST.id && + (loadingArticles ? ( + + ) : ( + + ))} + {state.articleToRenderData.value.articleData.title && + state.displayedTabId == tabs.SHOW_ARTICLE.id && ( + + )} + + {state.displayedTabId == tabs.SHOW_ARTICLES_LIST_BY_AUTHORS.id && ( + + )} + + {state.displayedTabId == tabs.ARTICLE_WORKSHOP.id && ( + + )} +
+
+) diff --git a/src/Cheddar/Forum/AllArticlesList.jsx b/src/Cheddar/Forum/AllArticlesList.jsx new file mode 100644 index 0000000..fa6f685 --- /dev/null +++ b/src/Cheddar/Forum/AllArticlesList.jsx @@ -0,0 +1,305 @@ +// Cheddar.Forum.AllArticlesList +const { arrayIncludesIgnoreCase } = VM.require( + 'chatter.cheddar.near/widget/lib.strings' +) || { arrayIncludesIgnoreCase: () => {} } + +const { HumanityWrapperButton } = VM.require( + 'chatter.cheddar.near/widget/lib.nadaBot' +) || { HumanityWrapperButton: () => <> } + +//===============================================INITIALIZATION===================================================== + +let { + isTest, + stateUpdate, + articlesToRender, + tabs, + widgets, + addressForArticles, + handleFilterArticles, + handleOpenArticle, + authorForWidget, + initialCreateState, + editArticleData, + handleEditArticle, + handleShareButton, + handleShareSearch, + filterBy, + baseActions, + handleOnCommitArticle, + sharedSearchInputValue, + category, + sharedData, +} = props + +if (!articlesToRender) return <> + +const [searchInputValue, setSearchInputValue] = useState( + sharedSearchInputValue ?? '' +) + +function filterArticlesBySearch(articles, searchInputValue) { + if (!searchInputValue || searchInputValue === '') return articles + return articles.filter((article) => { + const { title, body } = article.value.articleData + const { author } = article.value.metadata + const arr = [title, body, author] + if (arr.some((item) => item === undefined)) return false + + return arrayIncludesIgnoreCase(arr, searchInputValue) + }) +} + +const articlesFilteredBySearch = filterArticlesBySearch( + articlesToRender, + searchInputValue +) + +const fiveDaysTimeLapse = 5 * 24 * 60 * 60 * 1000 +const newestArticlesWithUpVotes = articlesFilteredBySearch.filter( + (article) => + article.value.metadata.lastEditTimestamp > + Date.now() - fiveDaysTimeLapse +) +// .sort((a, b) => b.timeLastEdit - a.timeLastEdit); + +const olderArticlesWithUpVotes = articlesFilteredBySearch.filter( + (article) => + article.value.metadata.lastEditTimestamp < + Date.now() - fiveDaysTimeLapse +) +// .sort((a, b) => b.upVotes.length - a.upVotes.length); + +const sortedArticlesToRender = [ + ...newestArticlesWithUpVotes, + ...olderArticlesWithUpVotes, +] + +//=============================================END INITIALIZATION=================================================== + +//===================================================CONSTS========================================================= + +const AcordionContainer = styled.div` + --bs-accordion-border-width: 0px !important; +` + +const NoMargin = styled.div` + margin: 0 0rem; +` + +const AccordionBody = styled.div` + padding: 0; +` + +const ArticlesListContainer = styled.div` + margin: 0; +` + +const CallLibrary = styled.div` + display: none; +` + +const IconCursorPointer = styled.i` + cursor: pointer; +` + +const ShareSearchRow = styled.div` + display: flex; + justify-content: flex-start; + align-items: center; + margin-bottom: 1rem; + margin-top: 1rem; +` + +const ShareSearchText = styled.h6` + margin-bottom: 0; + margin-left: 1rem; + margin-right: 1rem; +` + +const SearchResult = styled.span` + margin-left: 0.5rem; + font-size: small; +` + +//=================================================END CONSTS======================================================= + +//==================================================FUNCTIONS======================================================= + +function getDateLastEdit(timestamp) { + const date = new Date(Number(timestamp)) + const dateString = { + date: date.toLocaleDateString(), + time: date.toLocaleTimeString(), + } + return dateString +} + +function handleSearch(e) { + setSearchInputValue(e.target.value) +} + +//================================================END FUNCTIONS===================================================== +return ( + <> + <> + + +

+ +
+ Create post
+
+

+
+ + + +
+
+
+ +
+ +
+ {searchInputValue && + searchInputValue !== '' && + sortedArticlesToRender.length > 0 && ( + + {`Found ${sortedArticlesToRender.length} articles searching for "${searchInputValue}"`} + + )} + + Share search +
, + onClick: () => handleShareSearch(true, searchInputValue), + }} + /> + + + {filterBy.parameterName === 'tag' && ( +
+
Filter by tag:
+
+ + + handleFilterArticles({ + filterBy: '', + value: '', + }) + } + > +
+
+ )} + + {sortedArticlesToRender.length > 0 ? ( + sortedArticlesToRender.map((article, i) => { + const authorProfileCall = Social.getr( + `${article.value.metadata.author}/profile` + ) + + if (authorProfileCall) { + article.authorProfile = authorProfileCall + } + + // If some widget posts data different than an array it will be ignored + if (!Array.isArray(article.value.articleData.tags)) + article.value.articleData.tags = [] + return ( +
+ +
+ ) + }) + ) : ( +
{`No articles ${ + searchInputValue !== '' + ? `have been found searching for ${searchInputValue}` + : 'uploaded yet' + }`}
+ )} +
+
+ +) diff --git a/src/Cheddar/Forum/Create.jsx b/src/Cheddar/Forum/Create.jsx new file mode 100644 index 0000000..eda667c --- /dev/null +++ b/src/Cheddar/Forum/Create.jsx @@ -0,0 +1,415 @@ +//Cheddar.Forum.Create +const { createArticle, editArticle, buildArticle } = VM.require( + 'chatter.cheddar.near/widget/lib.article' +) || { createArticle: () => {}, editArticle: () => {}, buildArticle: () => {} } +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) || { getConfig: () => {} } + +const { + isTest, + addressForArticles, + authorForWidget, + stateUpdate, + initialBody, + initialCreateState, + editArticleData, + widgets, + handleFilterArticles, + handleEditArticle, + handlerStateUpdate, + baseActions, + handleOnCommitArticle, + category, +} = props + +const errTextNoBody = 'ERROR: no article Body', + errTextNoId = 'ERROR: no article Id', + errTextDublicatedId = 'ERROR: there is article with such name' + +State.init({ + ...initialCreateState, + initialBody: props.initialBody ?? '', + tags: [], +}) + +function createStateUpdate(obj) { + State.update(obj) +} + +const tagsArray = + editArticleData && !state.tagsModified + ? editArticleData.value.articleData.tags + : state.tags + +const accountId = context.accountId + +function getRealArticleId() { + if (editArticleData) { + return ( + editArticleData.value.metadata.id ?? + `article/${editArticleData.value.metadata.author}/${editArticleData.value.metadata.createdTimestamp}` + ) + } else { + return `article/${accountId}/${Date.now()}` + } +} + +function getArticleData() { + const args = { + title: editArticleData.value.articleData.title ?? state.title, + author: editArticleData.value.metadata.author ?? accountId, + lastEditor: accountId, + timeLastEdit: Date.now(), + timeCreate: + editArticleData.value.metadata.createdTimestamp ?? Date.now(), + body: state.articleBody, + version: editArticleData + ? editArticleData.value.metadata.versionKey + 1 + : 0, + navigation_id: null, + tags: tagsArray ?? [], + id: getRealArticleId(), + category: editArticleData.value.articleData.category ?? category, + } + return args +} + +function onCommit(articleId) { + State.update({ + title: '', + clearArticleId: true, + tags: [], + clearTags: true, + articleBody: '', + clearArticleBody: true, + initalBody: '', + // showCreatedArticle: true, + showPreview: false, + saving: true, + }) + + //if (!Array.isArray(article.tags)) article.tags = Object.keys(article.tags); + + handleOnCommitArticle(articleId) +} + +function onCancel() { + State.update({ + createdArticle: undefined, + saving: false, + }) +} + +const handleCreate = () => { + const { title, body, tags, category } = getArticleData() + + const articleData = { title, body, tags, category } + + const metadataHelper = { + author: context.accountId, + } + createArticle( + getConfig(isTest), + articleData, + metadataHelper, + (id) => onCommit(id), + onCancel + ) +} + +const handleEdit = () => { + const { title, body, tags, id, category } = getArticleData() + + const articleData = { title, body, tags, category } + + const articleMetadata = editArticleData.value.metadata + + editArticle( + getConfig(isTest), + articleData, + articleMetadata, + () => onCommit(id), + onCancel + ) +} + +function getInitialMarkdownBody() { + if ( + editArticleData && + (!state.articleBody || + state.articleBody === editArticleData.value.articleData.body) + ) { + return editArticleData.value.articleData.body + } else if ( + state.articleBody && + state.articleBody !== editArticleData.value.articleData.body + ) { + return state.articleBody + } else { + return state.initialBody == '' || !state.initialBody + ? 'Post content (markdown supported)' + : state.initialBody + } +} + +function switchShowPreview() { + State.update({ + showPreview: !state.showPreview, + initialBody: state.articleBody, + }) +} + +const GeneralContainer = styled.div` + margin: 0; +` + +const Button = styled.button` + margin: 0px 1rem; + display: inline-block; + text-align: center; + vertical-align: middle; + cursor: pointer; + user-select: none; + transition: + color 0.15s ease-in-out, + background-color 0.15s ease-in-out, + border-color 0.15s ease-in-out, + box-shadow 0.15s ease-in-out; + + border: 2px solid transparent; + font-weight: 500; + padding: 0.3rem 0.5rem; + background-color: #010a2d; + border-radius: 12px; + color: white; + text-decoration: none; + + &:hover { + color: #010a2d; + background-color: white; + } +` + +const CreationContainer = styled.div` + background-color: rgb(230, 230, 230); + border-radius: 20px; + padding: 1rem 0; + position: relative; +` + +const SecondContainer = styled.div` + min-width: 360px; + background-color: white; + padding: 1rem; +` + +const SpinnerContainer = styled.div` + height: 1rem; + width: 1rem; + margintop: 2px; +` + +const Spinner = () => { + return ( + + ) +} + +const initialTagsObject = {} + +Array.isArray(tagsArray) && + tagsArray.forEach((tag) => { + initialTagsObject[tag] = true + }) + +if (state.saving) { + return ( + + ) +} + +return ( +
+ +
+ + {state.showPreview ? ( + {}, + handleFilterArticles: () => {}, + authorForWidget, + handleShareButton: () => {}, + baseActions, + switchShowPreview, + isPreview: state.showPreview, + }} + /> + ) : ( +
+
+ + State.update(obj), + filterText: (e) => e.target.value, + placeholder: + 'Post title (case-sensitive)', + editable: editArticleData, + }} + /> +
+
+ + + + State.update({ + articleBody, + clearArticleBody: false, + }), + clearArticleBody: + state.clearArticleBody, + }} + /> +
+ +
+ State.update(obj), + initialTagsObject, + placeholder: 'Input tags', + setTagsObject: (tags) => { + // state.tags = Object.keys(tags); + State.update({ + tagsModified: true, + tags: Object.keys(tags), + }) + }, + }} + /> +
+
+ )} +
+ + ), + }} + /> + + {state.saving ? ( + + ) : ( + <> + + {editArticleData + ? 'Save edition' + : 'Post'} + + + + )} +
+ ), + }} + /> +
+ +
+ + +) diff --git a/src/Cheddar/GeneralCard.jsx b/src/Cheddar/GeneralCard.jsx new file mode 100644 index 0000000..e894af8 --- /dev/null +++ b/src/Cheddar/GeneralCard.jsx @@ -0,0 +1,655 @@ +// Cheddar.GeneralCard +const promise = new Promise((resolve, reject) => { + setTimeout(() => { + resolve([]) + }, 1) +}) + +const { getUpVotes } = VM.require('chatter.cheddar.near/widget/lib.upVotes') +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) || { getConfig: () => {} } + +const { HumanityWrapperButton, isHuman } = VM.require( + 'chatter.cheddar.near/widget/lib.nadaBot' +) || { HumanityWrapperButton: () => <>, isHuman: () => {} } + +//===============================================INITIALIZATION===================================================== + +if (typeof getUpVotes !== 'function') { + return <> +} +const { + widgets, + isTest, + data, + handleOpenArticle, + handleFilterArticles, + addressForArticles, + authorForWidget, + handleShareButton, + handleEditArticle, + baseActions, + switchShowPreview, + isPreview, +} = props + +if ( + !Array.isArray(data.value.articleData.tags) && + typeof data.value.articleData.tags === 'object' +) { + data.value.articleData.tags = Object.keys(data.value.articleData.tags) +} + +data.value.articleData.tags = data.value.articleData.tags.filter( + (tag) => tag !== undefined && tag !== null +) + +const tags = data.value.articleData.tags +const accountId = data.value.metadata.author +const title = data.value.articleData.title +const content = data.value.articleData.body +const timeLastEdit = data.value.metadata.lastEditTimestamp +const id = + data.value.metadata.id ?? + `${data.author}-${data.metadata.createdTiemestamp}` +const [upVotes, setUpVotes] = useState(undefined) +const [loadingUpVotes, setLoadingUpVotes] = useState(true) + +function loadUpVotes() { + getUpVotes(getConfig(isTest), id).then((newVotes) => { + setUpVotes(newVotes) + setLoadingUpVotes(false) + }) +} + +loadUpVotes() + +function stateUpdate(obj) { + State.update(obj) +} + +State.init({ + verified: true, + start: true, + voted: false, + sliceContent: true, +}) +//=============================================END INITIALIZATION=================================================== + +//===================================================CONSTS========================================================= + +//=================================================END CONSTS======================================================= + +//==================================================FUNCTIONS======================================================= +function getPublicationDate(creationTimestamp) { + if (creationTimestamp == 0) { + return 'Creation timestamp passed wrong' + } + return new Date(creationTimestamp).toDateString() +} + +function getUserName() { + const profile = data.authorProfile + + return profile.name ?? getShortUserName() +} + +const getShortUserName = () => { + const userId = accountId + + if (userId.length === 64) + return `${userId.slice(0, 4)}..${userId.slice(-4)}` + const name = userId.slice(0, -5) // truncate .near + + return name.length > 20 ? `${name.slice(0, 20)}...` : name +} + +function toggleShowModal() { + State.update({ showModal: !state.showModal }) +} + +//================================================END FUNCTIONS===================================================== + +//==============================================STYLED COMPONENTS=================================================== + +const CardContainer = styled.div` + box-shadow: rgba(140, 149, 159, 0.1) 0px 4px 28px 0px; +` + +const Card = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 16px; + background: rgba(140, 149, 159, 0.1) 0px 4px 28px 0px; + border-radius: 10px; +` +const HeaderCard = styled.div` + display: flex; + flex-direction: row; + align-items: center; + padding: 0px; + gap: 12px; + width: 100%; +` + +const profilePictureStyles = { + width: '45px', + height: '45px', + borderRadius: '50%', +} +const HeaderContent = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + padding: 0px; + gap: 4px; + width: 70%; +` +const HeaderButtonsContainer = styled.div` + display: flex; + gap: 0.5rem; +` +const HeaderContentText = styled.div` + display: flex; + width: 100%; + flex-direction: column; + align-items: flex-start; + padding: 0px; + cursor: pointer; +` +const NominationName = styled.p` + font-weight: 500; + font-size: 14px; + margin: 0; + align-items: center; + color: #000000; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +` +const NominationUser = styled.p` + font-style: normal; + font-weight: 400; + font-size: 12px; + margin: 0px; + line-height: 120%; + display: flex; + align-items: center; + color: #828688; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +` + +const KeyIssues = styled.div` + display: flex; + flex-direction: row; + align-items: flex-start; + padding: 12px; + gap: 12px; + background: #ffffff; + border: 1px solid rgb(248, 248, 249); + border-radius: 6px; + width: 100%; +` +const KeyIssuesContent = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + padding: 0px; + gap: 12px; + width: 100%; +` +const KeyIssuesHeader = styled.div` + display: flex; + flex-direction: row; + align-items: flex-start; + padding: 0px; + gap: 12px; +` +const KeyIssuesTitle = styled.p` + font-style: normal; + font-weight: 700; + font-size: 14px; + line-height: 120%; + margin-bottom: 0; +` +const KeyIssuesContainer = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + padding: 0px; + gap: 8px; + overflow-y: scroll; + max-height: 250px; + width: 100%; + border: 1px solid rgb(248, 248, 249); + border-radius: var(--bs-border-radius-lg) !important; +` + +const ArticleBodyContainer = styled.div` + margin: 0 0.5rem 0.5rem 0.5rem; +` + +const LowerSection = styled.div` + display: flex; + width: 100%; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 8px; +` +const LowerSectionContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 12px; + align-self: stretch; +` +const ButtonsLowerSection = styled.div` + display: flex; + flex-direction: row; + align-items: center; + padding: 0px; + width: 100%; +` +const TextLowerSectionContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + padding: 0px; + gap: 4px; + width: 239px; + height: 24px; + cursor: pointer; + + flex-grow: 1; +` +const TimestampText = styled.div` + font-style: italic; + font-weight: 300; + font-size: 11px; + line-height: 14px; + margin: 0px; + gap: 2px; + color: #000000; + + b { + font-weight: 600; + } +` +const ButtonsContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + padding: 0px; + gap: 4px; + width: 87px; + height: 28px; +` +const TagSection = styled.div` + display: flex; + align-items: flex-start; + gap: 4px; + flex-wrap: wrap; + overflow: hidden; + cursor: pointer; +` + +const Element = styled.div` + width: 150px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + padding: 10px; + + &:hover { + border-radius: 6px; + background: #f8f8f9; + } +` + +const CallLibrary = styled.div` + display: none; +` +//============================================END STYLED COMPONENTS================================================= + +//=================================================MORE STYLES====================================================== + +const profileImageStyles = { + width: profilePictureStyles.width, + height: profilePictureStyles.height, + borderRadius: profilePictureStyles.borderRadius, + overflow: 'hidden', +} + +//===============================================END MORE STYLES==================================================== + +//=================================================COMPONENTS======================================================= + +const inner = ( +
+ +
+) + +const renderTags = () => { + return ( + <> + {tags && + tags.map((tag) => { + const filter = { filterBy: 'tag', value: tag } + + return ( +
handleFilterArticles(filter)}> + {tag && ( + + )} +
+ ) + })} + + ) +} + +const renderArticleBody = () => { + let displayedContent = state.sliceContent ? content.slice(0, 1000) : content + return ( + + ( + + + #{hashtag} + + + ), + }} + /> + {state.sliceContent && content.length > 1000 && ( + { + State.update({ sliceContent: false }) + }, + icon: ( + + ), + }, + }} + /> + )} + + ) +} + +//===============================================END COMPONENTS==================================================== + +//===================================================RENDER======================================================== + +return ( + + + {state.showModal && ( + + )} + +
+
+ +
+ { + // + // { + // handleOpenArticle(data); + // }} + // > + // {getUserName()} + // {getShortUserName()} + // + // + } +
+ + + + + , + onClick: () => + handleShareButton(true, { + key: 'said', + type: 'sharedArticleId', + value: data.value.metadata.id, + }), + }} + /> + +
+ + { + handleOpenArticle(data) + }} + > + {title} + + + + {renderArticleBody()} + + + + {tags.length > 0 && ( + + + + Tags + +
+ {renderTags()} +
+
+
+ )} + + { + handleOpenArticle(data) + }} + > + + + {getPublicationDate(timeLastEdit)} + by + {author} + + + + + + +
+
+ + + + Add comment + + +
+ ), + size: 'sm', + className: 'primary outline', + onClick: isPreview + ? () => {} + : toggleShowModal, + }} + /> + + + + View + + +
+ ), + size: 'sm', + className: 'primary w-25', + onClick: () => handleOpenArticle(data), + }} + /> + + {context.accountId === data.author && ( + + + Edit + + + + ), + className: `primary w-25`, + onClick: () => + isPreview + ? switchShowPreview() + : handleEditArticle(data), + }} + /> + )} + + +
+
+
+
+) diff --git a/src/Cheddar/NavBar.jsx b/src/Cheddar/NavBar.jsx new file mode 100644 index 0000000..23fadfd --- /dev/null +++ b/src/Cheddar/NavBar.jsx @@ -0,0 +1,297 @@ +// Cheddar.NavBar + +const { + handleGoHomeButton, + handlePillNavigation, + brand, + pills, + navigationButtons, + isTest, + displayedTabId, + writersWhiteList, + handleFilterArticles, + filterParameter, + handleBackButton, + tabs, + widgets, +} = props + +function stateUpdate(obj) { + State.update(obj) +} +/* + ======================================================PILLS EXAMPLE==================================================== + *Note: the first pill allways has to be the first one displayed* + pills: [{ + id: string, + title: string, + }] + ============(When modified to be web app we should delete action to replace it with a propper State.update)============ + + ======================================================BRAND EXAMPLE==================================================== + brand: { + homePageId: string, + brandName: string, + logoHref: string, + logoRemWidth: number/string, + logoRemHeight: number/string, + } + + ============(When modified to be web app we should delete action to replace it with a propper State.update)============ + */ + +const loggedUserAccountId = context.accountId + +// const canLoggedUserCreateArticle = state.canLoggedUserCreateArticle; + +const logoRemWidth = brand.logoRemWidth ? brand.logoRemWidth + 'rem' : undefined +const logoRemHeight = brand.logoRemHeight + ? brand.logoRemHeight + 'rem' + : undefined + +if ( + !stateUpdate || + !(displayedTabId + '') || + !pills || + (brand && (!brand.logoHref || !(brand.homePageId + ''))) +) { + const crucialPropMissingMsg = 'The following crucial props are missing:' + return ( +
+

{crucialPropMissingMsg}

+
    + {!stateUpdate &&
  • stateUpdate
  • } + + {!(displayedTabId + '') && ( +
  • displayedTabId
  • + )} + + {!pills &&
  • pills
  • } + + {brand && !brand.logoHref && ( +
  • brand.logoHref
  • + )} + + {brand && !(brand.homePageId + '') && ( +
  • brand.homePageId
  • + )} +
+
+ ) +} + +//============================================Styled components================================================== +const BrandLogoContainer = styled.div` + width: ${logoRemWidth ?? '4rem'}; + height: ${logoRemHeight ?? '4rem'}; + cursor: pointer; +` + +const activeColor = '#ffd50d' + +const Pill = styled.div` + font-family: system-ui; + font-weight: 500; + font-size: 1.2rem; + line-height: 24px; + color: black; + cursor: pointer; + user-select: none; + + &:hover { + color: ${activeColor}; + } +` + +const StylessATag = styled.a` + &:hover { + text-decoration: none; + } +` + +const BackButton = styled.div` + cursor: pointer; +` + +const CallLibrary = styled.div` + display: none; +` +//============================================End styled components============================================== + +//=================================================Components==================================================== + +const renderButton = (button, i) => { + return ( + { + handlePillNavigation(button.id) + State.update({ + selectedButtonIndex: i, + }) + }, + text: button.title, + className: + state.selectedButtonIndex == i + ? 'primary light' + : 'primary dark', + }, + }} + /> + ) +} +//==============================================End components=================================================== + +//==================================================FUNCTIONS==================================================== + +function realHandleBackButton() { + State.update({ + selectedButtonIndex: undefined, + }) + + handleBackButton() +} + +function realHandleGoHome() { + State.update({ + selectedButtonIndex: undefined, + }) + handleGoHomeButton() +} + +//================================================END FUNCTIONS=================================================== +return ( + <> +
+
+ {brand && ( + + + + )} + + + { + // navigationButtons && + // loggedUserAccountId && + // canLoggedUserCreateArticle && + // navigationButtons.map((button, i) => { + // return !(button.id + "") || !button.title ? ( + //

Button passed wrong

+ // ) : ( + //
{renderButton(button, i)}
+ // ); + // }) + } +
+
+ + {(((filterParameter == 'tag' || filterParameter == 'author') && + displayedTabId == tabs.SHOW_ARTICLES_LIST.id) || + displayedTabId == tabs.SHOW_ARTICLE.id || + displayedTabId == tabs.ARTICLE_WORKSHOP.id || + displayedTabId == tabs.SHOW_ARTICLES_LIST_BY_AUTHORS.id) && ( + + + Back + + )} + +) diff --git a/src/Cheddar/Reactions.jsx b/src/Cheddar/Reactions.jsx new file mode 100644 index 0000000..f3f1047 --- /dev/null +++ b/src/Cheddar/Reactions.jsx @@ -0,0 +1,323 @@ +// Cheddar.Reactions + +const promise = new Promise((resolve, reject) => { + setTimeout(() => { + resolve([]) + }, 1) +}) +const { getReactions, createReaction } = VM.require( + 'chatter.cheddar.near/widget/lib.reactions' +) || { getReactions: () => promise, createReaction: () => {} } +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) || { getConfig: () => {} } + +const { + isTest, + authorForWidget, + elementReactedId, + widgets, + disabled, + baseActions, +} = props +// Don't forget to put space between emoji and text -> "❤️ Positive" +const initialEmoji = '🤍 Like' +// It is important that 'Heart' Positive emoji is first +const emojiArray = [ + '❤️ Positive', + '🙏 Thank you', + '💯 Definitely', + '👀 Thinking', + '🔥 Awesome', + '👍 Like', + '🙌 Celebrate', + '👏 Applause', + '⚡ Lightning', + '⋈ Bowtie', +] + +const [reactionsData, setReactionsData] = useState({ + reactionsStatistics: [], + userEmoji: undefined, +}) +const [showEmojiList, setShowEmojiList] = useState(false) +const [loading, setLoading] = useState(true) + +function loadReactions() { + getReactions(getConfig(isTest), elementReactedId, context.accountId).then( + (reactions) => { + setReactionsData(reactions) + setLoading(false) + } + ) +} + +useEffect(() => { + loadReactions() + setInterval(() => { + loadReactions() + }, 30000) +}, []) + +// ================= Mouse Handlers =============== + +function handleOnMouseEnter() { + if (!disabled) { + setShowEmojiList(true) + } +} + +function handleOnMouseLeave() { + setShowEmojiList(false) +} + +function onCommit() { + setLoading(true) + setTimeout(() => { + loadReactions() + }, 3000) +} + +function onCancel() { + setLoading(false) +} + +function reactListener(emojiToWrite) { + if (loading || disabled) { + return + } + setLoading(true) + + // decide to put unique emoji or white heart (unreaction emoji) + // const emojiToWrite = + // emojiMessage === initialEmoji ? emojiArray[0] : emojiMessage; + + const author = context.accountId + const result = createReaction( + getConfig(isTest), + emojiToWrite, + elementReactedId, + author, + onCommit, + onCancel + ) + if (result.error) { + console.error(result.data) + } +} + +// =============== CSS Styles =============== +const Button = styled.button` + min-width: fit-content; + background: transparent; + display: inline-flex; + align-items: center; + justify-content: start; + height: 2.5em; + padding: 6px 12px; + margin: 2px 0; + border: 0; + border-radius: 0.375rem; + ${!disabled && + `:hover { + background: #EBEBEB; + outline: 1px solid #C6C7C8; + }`} +` + +const SmallReactButton = styled.button` + background: transparent; + display: inline-flex; + align-items: center; + justify-content: start; + width: fit-content; + height: 2.5em; + padding: 6px 12px; + margin: 2px 0; + border: 0; + border-radius: 0.375rem; + ${!disabled && + `:hover { + background: #EBEBEB; + outline: 1px solid #C6C7C8; + }`} +` + +const SmallButton = styled.button` + position: relative; + border: 0; + background: transparent; + width: 35px; + height: 35px; + color: ${({ isHeart }) => (isHeart ? 'red' : '')}; +` + +const SmallButtonSpan = styled.span` + font-size: 19px; + :hover { + position: absolute; + font-size: 35px; + bottom: -5px; + width: 35px; + height: 40px; + transform: translateX(-50%) translateY(-50%); + } + + @media (max-width: 599px) { + ::before { + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.4); + content: ''; + } + :hover { + ::before { + position: absolute; + width: 100%; + height: 120%; + background-color: rgba(255, 255, 255, 0.4); + content: ''; + } + } + } +` + +// =============== NEW CSS Styles ===============!!!!!!!! +const EmojiWrapper = styled.div` + display: inline-block; + position: relative; + overflow: visible !important; + padding-left: 8px; +` + +const EmojiListWrapper = styled.div` + display: flex; + flex-wrap: wrap; + padding: 0.5rem; + + background: white; + border-radius: 1rem; + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; + position: absolute; + right: 0; + width: 370px; + max-width: 35vw; + flex-wrap: wrap; + display: ${({ show }) => (show ? 'flex' : 'none')}; + transform: translateY(-10%); + zindex: 2; +` + +const SpinnerContainer = styled.div` + height: 1rem; + width: 1rem; + margintop: 2px; +` + +const CallLibrary = styled.div` + display: none; +` + +// =============== NEW JSX ===============!!!!!!!! +const Overlay = () => { + return ( + + {emojiArray && + emojiArray.map((item) => { + return ( + reactListener(item)} + isHeart={index === 0} + > + +
+ {item.slice(2)} +
+ + } + > + + {item.slice(0, 2)} + +
+
+ ) + })} +
+ ) +} + +const Spinner = () => { + return ( + + ) +} + +const renderReaction = (item, isInButton) => { + return ( + ((item.accounts.includes(context.accountId) && isInButton) || + (!item.accounts.includes(context.accountId) && !isInButton)) && ( + + + + ) + ) +} + +return ( + <> + + {!disabled && ( + <> + {reactionsData.userEmoji ? ( + + {loading ? ( + + ) : ( + reactionsData.reactionsStatistics && + reactionsData.reactionsStatistics.map((item) => + renderReaction(item, true) + ) + )} + + ) : ( + + )} + + )} + + {reactionsData.reactionsStatistics && + reactionsData.reactionsStatistics.map((item) => + renderReaction(item, false) + )} + + +) diff --git a/src/Cheddar/UpVoteButton.jsx b/src/Cheddar/UpVoteButton.jsx new file mode 100644 index 0000000..f279bca --- /dev/null +++ b/src/Cheddar/UpVoteButton.jsx @@ -0,0 +1,142 @@ +//Cheddar.UpVoteButton +const { createUpVote, deleteUpVote } = VM.require( + 'chatter.cheddar.near/widget/lib.upVotes' +) || { createUpVote: () => {}, deleteUpVote: () => {} } +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) || { getConfig: () => {} } + +const { + isTest, + authorForWidget, + reactedElementData, + widgets, + disabled, + upVotes: articleUpVotes, + baseActions, + loadUpVotes, + loadingUpVotes, + setLoadingUpVotes, + setUpVotes, +} = props + +const data = reactedElementData + +let userVote = articleUpVotes + ? articleUpVotes.find((vote) => vote.accountId === context.accountId) + : undefined + +function getUpVoteButtonClass() { + if (userVote) { + return 'primary' + } else { + return 'primary outline' + } +} + +function onCommitUpVotes() { + setLoadingUpVotes(true) + setUpVotes([]) + setTimeout(() => { + loadUpVotes() + }, 3000) +} + +function onCancelUpVotes() { + setLoadingUpVotes(false) +} + +function handleUpVote() { + userVote + ? deleteUpVote( + getConfig(isTest), + userVote.value.metadata.articleId, + userVote.value.metadata.id, + onCommitUpVotes, + onCancelUpVotes + ) + : createUpVote( + getConfig(isTest), + data.value.metadata.id, + data.value.metadata.author, + onCommitUpVotes, + onCancelUpVotes + ) +} + +const IconContainer = styled.div` + transform: rotate(-90deg); +` + +const Icon = styled.i` + margin: 0px !important; +` + +const CallLibrary = styled.div` + display: none; +` + +const SpinnerContainer = styled.div` + height: 1.3rem; + width: 1.3rem; + margintop: 2px; +` + +return ( + <> +
+ {loadingUpVotes ? ( + + +
+ ), + size: 'sm', + }} + /> + ) : ( + + {`+${articleUpVotes.length}`} + + + + + ), + disabled: disabled, + className: `${getUpVoteButtonClass()}`, + size: 'sm', + onClick: handleUpVote, + }} + /> + )} + + +) diff --git a/src/HelloCV.jsx b/src/HelloCV.jsx index b7674e3..8fdd2d0 100644 --- a/src/HelloCV.jsx +++ b/src/HelloCV.jsx @@ -1,2 +1,2 @@ // This is a file to test bos-loader. Do not modify nor delete -return
Hello Community Voice 1
\ No newline at end of file +return
Hello Community Voice 1
diff --git a/src/NDC/AddComment.jsx b/src/NDC/AddComment.jsx deleted file mode 100644 index 76cb1be..0000000 --- a/src/NDC/AddComment.jsx +++ /dev/null @@ -1,474 +0,0 @@ -// NDC.AddComment -const { createComment, editComment } = VM.require( - "communityvoice.ndctools.near/widget/lib.comment" -); -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice"); - -const { - widgets, - isTest, - article, - onCloseModal, - originalComment, - isReplying, - username, - placement, - rootCommentId, - replyingTo, - baseActions, - editionData, - loadComments, - setLoadingComments, -} = props; - -const rootId = rootCommentId ?? article.value.metadata.id; //To render in the proper location - -const commentId = editionData ? editionData.value.metadata.id : undefined; //(OPTIONAL) to edit comment - -const isEdition = commentId !== undefined; - -const ModalCard = styled.div` - position: fixed; - z-index: 1; - left: 0; - top: 0; - width: 100%; - height: 100%; - display: flex; - justify-content: center; - align-items: center; - background: rgba(0, 0, 0, 0.7); - `; -const CommentCard = styled.div` - display: flex; - width: 400px; - padding: 20px; - flex-direction: column; - align-items: flex-start; - gap: 16px; - border-radius: 10px; - background: #fff; - border: 1px solid transparent; - margin-left: auto; - margin-right: auto; - margin-buttom: 50%; - @media only screen and (max-width: 480px) { - width: 90%; - } - `; -const H1 = styled.h1` - color: black; - font-size: 14px; - font-weight: 500; - `; -const Container = styled.div` - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 20px; - align-self: stretch; - `; -const CommentBody = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 16px; - align-self: stretch; - `; -const BComment = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 8px; - align-self: stretch; - `; -const BCommentmessage = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 8px; - align-self: stretch; - `; -const BCMHeader = styled.div` - display: flex; - width: 100%; - align-items: center; - gap: 8px; - `; -const BCMProfile = styled.div` - width: 28px; - height: 28px; - flex-shrink: 0; - flex-direction: row; - border-radius: 29px; - background: #d0d6d9; - text-align: center; - `; -const BCMProfileimg = styled.img` - width: 28px; - height: 28px; - flex-shrink: 0; - vertical-align: initial; - `; -const BCMProfileUsername = styled.label` - display: flex; - width: 100%; - flex-direction: column; - justify-content: center; - flex-shrink: 0; - color: #000; - font-size: 14px; - - font-style: normal; - font-weight: 500; - line-height: 120%; - `; -const BCMMessage = styled.div` - display: flex; - flex-direction: column; - align-self: stretch; - color: #686b6d; - font-size: 14px; - - font-style: normal; - font-weight: 400; - line-height: 120%; - `; -const BFooter = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - gap: 4px; - align-self: stretch; - `; -const BFootercont = styled.div` - display: flex; - align-items: center; - align-self: stretch; - `; -const BFootercontTime = styled.div` - display: flex; - align-items: center; - gap: 4px; - flex: 1 0 0; - `; -const BFCTimetext = styled.div` - display: flex; - height: 19.394px; - flex-direction: column; - justify-content: center; - flex: 1 0 0; - color: #000; - font-size: 14px; - - font-style: normal; - font-weight: 300; - line-height: normal; - `; -const BFCButton = styled.div` - display: flex; - justify-content: flex-end; - align-items: center; - gap: 4px; - `; -const BFCButtonitem = styled.button` - display: flex; - padding: 2px 12px; - align-items: center; - gap: 6px; - border-radius: 4px; - border-width: 1px; - border: solid 1px #9333ea; - - background-image: linear-gradient(#fff, #fff), - radial-gradient(circle at top left, #f0e1ce, #f0e1ce); - background-origin: border-box; - background-clip: padding-box, border-box; - `; -const BFCBIText = styled.label` - font-size: 12px; - - font-style: normal; - font-weight: 500; - line-height: 24px; - color: #9333ea; - cursor: pointer; - `; -const NewComment = styled.textarea` - width: 100%; - display: flex; - height: 100px; - padding: 9px 10px 0px 10px; - align-items: flex-start; - - gap: 10px; - align-self: stretch; - border-radius: 8px; - border: 1px solid #d0d6d9; - background: #fff; - - font-size: 12px; - - font-style: normal; - font-weight: 400; - line-height: 120%; - `; -const CommentFooter = styled.div` - display: flex; - flex-direction: row; - align-items: flex-end; - justify-content: end; - gap: 16px; - align-self: stretch; - `; -const CFCancel = styled.button` - display: flex; - width: 107px; - padding: 8px 12px; - justify-content: center; - align-items: center; - gap: 10px; - color: #9333ea; - border-radius: 10px; - border-width: 1px; - border: solid 1px #9333ea; - - background-image: linear-gradient(#fff, #fff), - radial-gradient(circle at top left, #f0e1ce, #f0e1ce); - background-origin: border-box; - background-clip: padding-box, border-box; - @media only screen and (max-width: 480px) { - width: 100%; - } - `; - -const CFSubmit = styled.button` - display: flex; - width: 107px; - padding: 8px 12px; - justify-content: center; - align-items: center; - gap: 10px; - color: #000; - display: flex; - width: 107px; - padding: 8px 12px; - justify-content: center; - align-items: center; - gap: 10px; - border-radius: 10px; - border-width: 1px; - border: solid 1px transparent; - - background-image: linear-gradient(#ffd50d, #ffd50d), - radial-gradient(circle at top left, #f0e1ce, #f0e1ce); - background-origin: border-box; - background-clip: padding-box, border-box; - @media only screen and (max-width: 480px) { - width: 100%; - } - `; - -const Spinner = styled.div` - height: 1rem; - width: 1rem; - `; - -const CallLibrary = styled.div` - display: none; - `; - -State.init({ - theme, - reply: "", - cancel: false, - e_message: "", -}); - -function getShouldDisplayOriginalComment() { - return ( - (!editionData && replyingTo) || - (editionData && - replyingTo && - editionData.value.metadata.id !== editionData.value.metadata.rootId) - ); -} - -function getInitialText() { - if (editionData) { - if (!state.reply || editionData.value.commentData.text === state.reply) { - return editionData.value.commentData.text; - } - } else if (replyingTo) { - return `@${replyingTo} `; - } else if (state.reply && state.reply !== editionData.value.commentData.text) { - return state.reply; - } else { - return "Reply here"; - } -} - -const SetText = (txt) => { - State.update({ shareText: txt }); -}; - -const renderSpinner = () => { - return ; -}; - -function onCommit() { - setLoadingComments && setLoadingComments(true) - setTimeout(() => { - loadComments && loadComments() - State.update({reply: "Reply here", showSpinner: false }); - onCloseModal(); - }, 3000); -} - -function onCancel() { - setLoadingComments(false) - State.update({ showSpinner: false }); -} - -function handleSubmitButton() { - if (state.showSpinner) { - return () => {}; - } else { - if (isEdition) { - return editCommentListener; - } else { - return addCommentListener; - } - } -} - -function addCommentListener() { - State.update({showSpinner: true }); - - createComment({ - config:getConfig(isTest), - author: context.accountId, - commentText: state.reply, - replyingTo: rootId, - articleId:article.value.metadata.id, - onCommit, - onCancel, - }); -} - -function editCommentListener() { - State.update({showSpinner: true }); - const comment = originalComment - comment.value.commentData.text=state.reply - - editComment({ - config: getConfig(isTest), - comment, - onCommit, - onCancel, - }); -} - -return ( - - -

- {isReplying - ? "Reply to comment" - : isEdition - ? "Edit comment" - : "Add a Comment"} -

- - {getShouldDisplayOriginalComment() && ( - <> - - - - - - - - - {replyingTo ? "@" + replyingTo : "@user.near"} - - - - {originalComment && originalComment.value.commentData.text} - - - - - - - - schedule - - - - -
- - )} -
- - State.update({ - reply: e, - }), - }} - /> -
- - - , - }, - }} - /> - -
-
-
-); diff --git a/src/NDC/AllArticlesSortByAuthors.jsx b/src/NDC/AllArticlesSortByAuthors.jsx deleted file mode 100644 index 5714ae8..0000000 --- a/src/NDC/AllArticlesSortByAuthors.jsx +++ /dev/null @@ -1,53 +0,0 @@ -//NDC.AllArticlesSortByAuthors - -const { - finalArticles, - tabs, - widgets, - handleOpenArticle, - handleFilterArticles, - authorForWidget, -} = props; - -const allFinalArticles = finalArticles; - -const articlesAuthors = - allFinalArticles.length && - Array.from(allFinalArticles, (article) => article.value.metadata.author); - -let authors = [...new Set(articlesAuthors)]; - -let articlesByAuthorsArray = []; -authors.map((author) => { - let thisAuthorArtciles = allFinalArticles.filter( - (article) => article.value.metadata.author == author - ); - articlesByAuthorsArray.push(thisAuthorArtciles); -}); - -return ( -
-
Total authors: {articlesByAuthorsArray.length}
- -
- {articlesByAuthorsArray && - articlesByAuthorsArray.map((authorArticlesArray) => { - const filter = { - filterBy: "author", - value: authorArticlesArray[0].value.metadata.author, - }; - return ( - - ); - })} -
-
-); diff --git a/src/NDC/ArticleHistory/Container.jsx b/src/NDC/ArticleHistory/Container.jsx deleted file mode 100644 index 538b3e8..0000000 --- a/src/NDC/ArticleHistory/Container.jsx +++ /dev/null @@ -1,77 +0,0 @@ -// NDC.ArticleHistory.Container - -const { - pathToCurrentArticle, - pathToPrevArticle, - currentBlockHeight, - currentVersionData, - allVersionsData, - prevBlockHeight, - widgets, -} = props; - -State.init({}); - -function getDatastring(time) { - const date = new Date(time); - return date.toDateString() + " " + date.toLocaleTimeString(); -} - -return ( -
-
- -
-
-
changes in block #{currentBlockHeight}
- - count inserted lines} - > - - {state.lineCountInserted} - - - - count deleted lines} - > - - {state.lineCountDeleted} - - -
-
- {getDatastring(currentVersionData.value.metadata.lastEditTimestamp)} -
-
-
-
- { - if ( - state.lineCountDeleted === undefined || - state.lineCountInserted === undefined - ) - State.update({ lineCountDeleted, lineCountInserted }); - }, - }} - /> -
-); diff --git a/src/NDC/ArticleHistory/Handler.jsx b/src/NDC/ArticleHistory/Handler.jsx deleted file mode 100644 index 6fd550d..0000000 --- a/src/NDC/ArticleHistory/Handler.jsx +++ /dev/null @@ -1,190 +0,0 @@ -//NDC.ArticleHistory.Handler -const addressForArticles = "wikiTest2Article"; - -const { - articleId, - isTest, - baseActions, - kanbanColumns, - widgets, - versions, -} = props; - -State.init({ - selectedTab: "code", - selectedBlockHeight: null, -}); - -//TODO Need the function getArticlesVersions in the lib.articles - -if (props.count) props.count(versions.length); - -if (!state.selectedBlockHeight && versions.length > 0) - state.selectedBlockHeight = versions[0].blockHeight; - -const renderBlockChangesLink = (version) => { - if (!version) return <>Loading...; - - const timeLastEdit = new Date(version.value.metadata.lastEditTimestamp); - - return ( -
- -
- ); -}; - -function renderWidgetCode(blockHeight) { - const currentVersionDisplayed = versions.find( - (version) => version.blockHeight === blockHeight - ); - const index = versions.findIndex((el) => el.blockHeight === blockHeight); - const prevBlockHeightObject = versions[index + 1]; - return ( - - ); -} - -function blockHeightToWidgetRender(blockHeight, allArticles) { - const index = versions.findIndex((el) => el.blockHeight === blockHeight); - return ; -} - -function articleHistoryHasndlerStateUpdate(obj) { - State.update(obj); -} - -//styles forked from calebjacob.near/widget/Activity -const Tabs = styled.div` - display: flex; - padding: 0 12px; - height: 48px; - border-bottom: 1px solid #ECEEF0; -`; - -const TabsButton = styled.button` - font-weight: 400; - font-size: 14px; - line-height: 17px; - padding: 0 12px; - position: relative; - color: ${(p) => (p.selected ? "#11181C" : "#687076")}; - background: none; - border: none; - outline: none; - - &:hover { - color: #11181C; - } - - &::after { - content: ''; - display: ${(p) => (p.selected ? "block" : "none")}; - position: absolute; - bottom: 0; - left: 12px; - right: 12px; - height: 3px; - background: #0091FF; - } -`; - -return ( - <> -
-

Article History

- {!versions ? ( -
incorrent widget path
- ) : ( -
-
-

{versions.length} Commits

-
- {versions - .slice(0, 5) - .map((height) => renderBlockChangesLink(height))} -
- {versions - .slice(5) - .map((height) => renderBlockChangesLink(height))} -
- {versions.length > 5 && ( - - )} -
-
- - - - State.update({ - selectedTab: "code", - }) - } - selected={state.selectedTab === "code"} - > - Code - - - - State.update({ - selectedTab: "render", - }) - } - selected={state.selectedTab === "render"} - > - Render - - - - {state.selectedTab === "code" && ( -
{renderWidgetCode(state.selectedBlockHeight)}
- )} - - {state.selectedTab === "render" && ( -
- {blockHeightToWidgetRender(state.selectedBlockHeight, versions)} -
- )} -
- )} -
- -); diff --git a/src/NDC/ArticleHistory/SecondContainer.jsx b/src/NDC/ArticleHistory/SecondContainer.jsx deleted file mode 100644 index ffbafb0..0000000 --- a/src/NDC/ArticleHistory/SecondContainer.jsx +++ /dev/null @@ -1,32 +0,0 @@ -// NDC.ArticleHistory.SecondContainer - -const { - pathToCurrentArticle, - pathToPrevArticle, - currentBlockHeight, - currentVersionData, - allVersionsData, - prevBlockHeight, -} = props; - -if (!pathToCurrentArticle || !pathToPrevArticle || !currentBlockHeight) - return "send pathToCurrentArticle and pathToPrevArticle and currentBlockHeight in props"; - -const currentCode = currentVersionData.value.articleData.body; - -if (currentCode === null) return "Loading"; - -const prevCode = prevBlockHeight - ? allVersionsData.find( - (versionData) => versionData.blockHeight == prevBlockHeight - ).body - : undefined; - -if (prevCode === null) return "Loading"; - -return ( - -); diff --git a/src/NDC/ArticleView.jsx b/src/NDC/ArticleView.jsx deleted file mode 100644 index 6bffde5..0000000 --- a/src/NDC/ArticleView.jsx +++ /dev/null @@ -1,926 +0,0 @@ -// NDC.ArticleView -const { getComments } = VM.require("communityvoice.ndctools.near/widget/lib.comment"); -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice"); -const { getUpVotes } = VM.require("communityvoice.ndctools.near/widget/lib.upVotes"); -const { getArticlesVersions } = VM.require("communityvoice.ndctools.near/widget/lib.article"); - - -const { - widgets, - isTest, - handleFilterArticles, - articleToRenderData, - authorForWidget, - handleEditArticle, - handleDeleteArticle, - handleShareButton, - baseActions, - kanbanColumns, - sharedData, - loggedUserHaveSbt -} = props; - -const accountId = articleToRenderData.value.metadata.author; -const id = - articleToRenderData.value.metadata.id ?? - `article/${articleToRenderData.value.metadata.author}/${articleToRenderData.value.metadata.createdTimestamp}`; - -if ( - !Array.isArray(articleToRenderData.value.articleData.tags) && - typeof articleToRenderData.value.articleData.tags === "object" -) { - articleToRenderData.value.articleData.tags = Object.keys(articleToRenderData.value.articleData.tags); -} - -articleToRenderData.value.articleData.tags = articleToRenderData.value.articleData.tags.filter( - (tag) => tag !== undefined && tag !== null -); - -const tabs = [ - { - id: "generalInfo", - title: "Post info", - icon: "bi bi-info-circle", - }, -]; - -//To slice the article body and show the showMore button just uncoment the sliceContent: true, un the State.init -State.init({ - tabSelected: tabs[0].id, - // sliceContent: true, -}); - -const [comments, setComments] = useState(undefined) -const [loadingComments, setLoadingComments] = useState(true) -const [versions, setVersions] = useState([]); - -if(versions.length === 0) { - try { - const versionsPromise = getArticlesVersions(getConfig(isTest), articleToRenderData.value.metadata.id); - versionsPromise.then((newVersions) => { - setVersions(newVersions) - }) - } catch (err) { - return console.error("Error in article history handler: ", err) - } -} - -function loadComments() { - const articleId = articleToRenderData.value.metadata.id - getComments(articleId, getConfig(isTest)).then((newComments) => { - setComments(newComments) - setLoadingComments(false) - }) -} - -useEffect(() => { - loadComments() - setInterval(() => { - loadComments() - }, 30000) -}, []) - -const [upVotes, setUpVotes] = useState(undefined) -const [loadingUpVotes, setLoadingUpVotes] = useState(true) - -function loadUpVotes() { - getUpVotes(getConfig(isTest),id).then((newVotes) => { - setUpVotes(newVotes) - setLoadingUpVotes(false) - }) -} - -useEffect(() => { - loadUpVotes() - setInterval(() => { - loadUpVotes() - }, 30000) -}, []) - -const timeLastEdit = new Date(articleToRenderData.value.metadata.lastEditTimestamp); - -const CursorPointer = styled.div` - margin-bottom: 0.5rem; - cursor: pointer; - `; - -const DetailContent = styled.div` - display: inline-flex; - flex-direction: column; - align-items: flex-start; - gap: 12px; - `; - -const TagContainer = styled.div` - display: flex; - flex-wrap: wrap; - align-items: flex-start; - gap: 4px; - margin-top: 1rem; - `; - -const HouseTagDiv = styled.div` - display: flex; - padding: 4px 8px; - justify-content: center; - align-items: center; - gap: 10px; - border-radius: 100px; - background: var( - --gradient-purple-gradient, - linear-gradient(90deg, #9333ea 0%, #4f46e5 100%) - ); - `; - -const HouseTagText = styled.p` - color: #fff; - font-size: 7px; - font-weight: 500; - line-height: 120%; - margin: 0px; - `; - -const TagDiv = styled.div` - display: flex; - justify-content: center; - padding: 4px 8px; - align-items: center; - gap: 10px; - border-radius: 100px; - border: solid 1px transparent; - border-radius: 80px; - background-image: linear-gradient(#eae5f7, #eae5f7), - radial-gradient(circle at top left, #9333ea 0%, #4f46e5 100%); - background-origin: border-box; - background-clip: padding-box, border-box; - `; - -const TagDivText = styled.p` - font-size: 8px; - margin: 0px; - font-weight: 500; - line-height: 120%; - background: linear-gradient(90deg, #9333ea 0%, #4f46e5 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - text-fill-color: transparent; - `; - -const NominationTitleContainer = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - `; - -const NominationTitle = styled.p` - display: flex; - flex-direction: column; - justify-content: center; - margin: 7px 0 0 0; - color: #000; - font-size: 18px; - font-weight: 500; - line-height: 120%; - `; -const UserLink = styled.a` - cursor: pointer; - &:hover { - text-decoration: none; - } - `; -const NominationUser = styled.p` - display: flex; - flex-direction: column; - justify-content: center; - color: #828688; - margin: 0 0 7px 0; - font-size: 14px; - line-height: 120%; - `; - -const UpvoteButtonDisabled = styled.button` - display: flex; - padding: 2px 12px; - align-items: center; - gap: 6px; - border-radius: 4px; - border: solid 1px transparent; - background: var(--buttons-disable, #c3cace); - cursor: default !important; - `; - -const UpvoteButton = styled.button` - display: flex; - padding: 2px 12px; - align-items: center; - gap: 6px; - border-radius: 4px; - border: solid 1px transparent; - background-image: linear-gradient(#f8f8f9, #f8f8f9), - radial-gradient( - circle at left top, - rgb(147, 51, 234) 0%, - rgb(79, 70, 229) 100% - ); - background-origin: border-box; - background-clip: padding-box, border-box; - `; - -const UpvoteCount = styled.p` - font-size: 12px; - font-weight: 500; - line-height: 24px; - margin: 0px; - background: linear-gradient(90deg, #9333ea 0%, #4f46e5 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - text-fill-color: transparent; - `; - -const Icon = styled.img` - width: 17px; - height: 17px; - `; -const BodyContainer = styled.div` - border-radius: 8px; - margin: 10px 0; - background: #F8F8F9; - padding: 20px; - `; - -const PlatformCard = styled.div` - display: flex; - border-radius: 6px; - background: background: "rgb(255 255 255 / 0%); - `; - -const PlatformContent = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 12px; - width: 100%; - `; - -const PlatformInfoDiv = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 8px; - `; - -const PlatformInfoHeader = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 2px; - `; - -const PlatInforHeadText = styled.p` - margin: 0px; - color: var(--000000, #000); - font-size: 10px; - font-weight: 500; - line-height: 120%; - `; - -const PlatInfoHeadSeparator = styled.hr` - height: 0px; - margin: 8px 0 0 0; - - border: 1px solid rgba(208, 214, 217, 1); - `; - -const KeyIssuesContainer = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 2px; - `; - -const KeyIssueTitle = styled.p` - font-size: 12px; - line-height: 120%; - margin: 0px; - font-weight: 500; - line-height: 18px; - text-align: left; - padding: 10px; - `; - -const KeyIssueDescription = styled.p` - color: #212427; - font-size: 12px; - line-height: 130%; - margin: 0px; - padding: 10px; - line-height: 18px; - text-align: justify; - `; - -const CandidateCard = styled.div` - display: flex; - padding: 20px; - align-items: center; - align-self: stretch; - border-radius: 6px; - background: #fff; - `; - -const CandidateContent = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - gap: 12px; - `; - -const ContentHeader = styled.div` - display: flex; - align-items: center; - gap: 12px; - align-self: stretch; - `; - -const ContentHeaderText = styled.p` - font-size: 18px; - font-weight: 500; - margin: 0px; - `; - -const CandidateInfoDiv = styled.div` - width: 100%; - padding: 16px; - background: white; - gap: 16px; - border-radius: 8px; - `; - -const CandidateInfoHeader = styled.div` - display: flex; - align-items: center; - gap: 8px; - align-self: stretch; - `; - -const CandidateImage = styled.img` - width: 32px; - height: 32px; - `; - -const CandidateInfoData = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 4px; - flex: 1 0 0; - `; - -const CandidateTagDiv = styled.div` - display: flex; - height: 20px; - padding: 4px 8px; - justify-content: center; - align-items: center; - gap: 10px; - border-radius: 100px; - border: 1px solid var(--secondary-warning, #f19d38); - background: #f0e1ce; - `; - -const CandidateTagText = styled.p` - color: var(--secondary-warning, #f19d38); - font-size: 10px; - font-weight: 500; - line-height: 120%; - margin: 0px; - `; - -const CandidateTime = styled.h6` - margin: 3px 0 0 0; - font-size: 10px; - font-weight: 500; - line-height: 120%; - color: #828688; - `; - -const CandidateTextInfo = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 4px; - align-self: stretch; - `; - -const SectionTitle = styled.h5` - font-size: 12px; - font-weight: 500; - line-height: 120%; - margin: 16px 0 0 0; - `; - -const SectionDescription = styled.p` - font-size: 12px; - line-height: 18px; - margin: 0px; - text-align: justify; - color: #828688; - `; - -const DescriptionSubtitle = styled.h5` - display: inline-block; - font-size: 12px; - line-height: 120%; - margin-right: 0.3rem; - `; - -const DescriptionInfoSpan = styled.span` - font-size: 12px; - line-height: 18px; - margin: 0px; - text-align: justify; - color: #828688; - `; - -const DeclarationCard = styled.div` - padding: 0px; - `; - -const CommentSection = styled.div` - display: flex; - padding: 20px; - flex-direction: column; - align-items: flex-start; - gap: 12px; - border-radius: 8px; - background: #f8f8f9; - `; - -const Container = styled.div` - display: flex; - margin-right: 5px; - justify-content: center; - `; - -const SecondContainer = styled.div` - background: #F8F8F9; - border-radius: 8px; - padding: 20px; - `; - -const H6 = styled.h6` - font-size: 14px; - margin-bottom: 0; - `; - -const Tab = styled.div` - font-weight: ${(props) => (props.active ? "600" : "500")}; - border-bottom: 2px solid; - border-color: ${(props) => - props.active ? "rgb(68, 152, 224)" : "#dee2e6"}; - color: ${(props) => (props.active ? "rgb(68, 152, 224)" : "#ababab")}; - cursor: pointer; - padding-bottom: 8px; - font-size: 14px; - - i { - &::before { - color: ${(props) => (props.active ? "rgb(68, 152, 224)" : "#ababab")}; - } - margin-right: 5px; - } - `; - -const TH = styled.th` - border: 1px solid rgba(208, 214, 217, 0.4) !important; - text-align: left !important; - padding: 15px 20px !important; - `; - -const CallLibrary = styled.div` - display: none; - `; - -const HeaderButtonsContainer = styled.div` - display: flex; - justify-content: flex-end; - gap: 0.5rem; - `; - -const EditDeleteButtonsContainer = styled.div` - display: flex; - gap: 0.5rem; - `; - -const AcordionContainer = styled.div`--bs-accordion-border-width: 0px!important;`; - -const NoMargin = styled.div`margin: 0 0.75rem;`; - -const AccordionBody = styled.div`padding: 0;`; - -//Get basic original comments info -const rootComments = comments ? - comments.filter( - (comment) => comment.value.metadata.rootId === id - ) -: - [] - -//Append answers to original comments -const articleComments = rootComments.map((rootComment) => { - let answers = comments.filter((comment) => { - return comment.value.metadata.rootId === rootComment.value.metadata.id; - }); - - return { - ...rootComment, - answers, - }; -}); - -function stateUpdate(obj) { - State.update(obj); -} - -function getUserName() { - const profile = data.authorProfile; - - return profile.name ?? getShortUserName(); -} - -const getShortUserName = () => { - const userId = accountId; - - if (userId.length === 64) return `${userId.slice(0, 4)}..${userId.slice(-4)}`; - const name = userId.slice(0, -5); // truncate .near - - return name.length > 20 ? `${name.slice(0, 20)}...` : name; -}; - -let displayedContent = state.sliceContent - ? articleToRenderData.value.articleData.body.slice(0, 1000) - : articleToRenderData.value.articleData.body; - -return ( - <> - {sharedData.sharedCommentId && ( - - Click to redirect to comment that mentioned you - - )} - - - -

- -

-
- -
- -
-
-
-
-
- - -
-
-
-
-
- - - {articleToRenderData.value.articleData.tags.length > 0 && - articleToRenderData.value.articleData.tags.map((tag) => { - const filter = { filterBy: "tag", value: tag }; - return ( - handleFilterArticles(filter)} - > - - - ); - })} - -
-
-
- - - , - onClick: () => - handleShareButton(true, { - key: "said", - type: "sharedArticleId", - value: articleToRenderData.value.metadata.id, - }), - }} - /> - - - {context.accountId == accountId && ( - - - Edit - -
- ), - className: `info outline mt-2`, - onClick: () => { - handleEditArticle(articleToRenderData) - } - }} - /> - - -
- ), - className: `danger outline mt-2`, - onClick: () => - handleDeleteArticle(articleToRenderData), - }} - /> - - )} -
-
-
-
- - - - - - {articleToRenderData.value.articleData.title} - - - - ( - - - #{hashtag} - - - ), - }} - /> - {state.sliceContent && - articleToRenderData.value.articleData.body.length > 1000 && ( - - Show more - - - ), - size: "sm", - className: "w-100", - onClick: () => { - State.update({ sliceContent: false }); - }, - }} - /> - )} - - - - - - - Comments - - - - {state.showModal && ( - State.update({ showModal: false }), - baseActions, - loadComments, - setLoadingComments, - }} - /> - )} - - Add comment - - - ), - disabled: - !context.accountId || - !loggedUserHaveSbt, - className: "info outline w-100 mt-4 mb-2", - onClick: () => { - State.update({ showModal: true }); - }, - }} - /> - {loadingComments ? - - : - articleComments.map((data) => ( - - )) - } - - - - - <> -
    - {tabs.map(({ id, title, icon }, i) => ( -
  • - State.update({ tabSelected: id })} - > - - {title} - -
  • - ))} -
-
- {state.tabSelected == "generalInfo" && ( - - -
- Created by: - - {articleToRenderData.authorProfile.name ?? - getShortUserName()} - -
-
- Edited on: - {timeLastEdit + ""} -
-
- Edit versions: - - { versions.length ?? 0 } - -
-
- )} -
- -
-
- -); diff --git a/src/NDC/ArticlesByAuthorCard.jsx b/src/NDC/ArticlesByAuthorCard.jsx deleted file mode 100644 index daac294..0000000 --- a/src/NDC/ArticlesByAuthorCard.jsx +++ /dev/null @@ -1,49 +0,0 @@ -//NDC.ArticlesByAuthorCard - -const { authorArticlesArray, filter, handleFilterArticles, widgets } = props; - -const CardContainer = styled.a` - color: black; - font-size: 16px; - line-height: 19.2px; - font-family: inherit; - box-shadow: 0px 0px 30px 0px #0000000D; - cursor: pointer; - with: fit-content; - min-width: 18rem; - display: flex; - flex-wrap: nowrap; - - &:hover { - color: white; - text-decoration: none; - background: linear-gradient(90deg, rgba(147,51,234,1) 0%, rgba(79,70,229,1) 100%); - } -`; - -const ImgContainer = styled.div` - border-radius: 20px; - overflow: hidden; -`; - -return ( -
- handleFilterArticles(filter)} - > - - - - {authorArticlesArray.length} articles - -
-); diff --git a/src/NDC/CommentView.jsx b/src/NDC/CommentView.jsx deleted file mode 100644 index 72d40fb..0000000 --- a/src/NDC/CommentView.jsx +++ /dev/null @@ -1,610 +0,0 @@ -// NDC.CommentView -const { deleteComment } = VM.require("communityvoice.ndctools.near/widget/lib.comment"); -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice"); - -const { - widgets, - data, - isTest, - authorForWidget, - isReply, - loggedUserHaveSbt, - orginalCommentData, - canLoggedUserCreateComment, - baseActions, - sharedData, - articleToRenderData, - loadComments, - setLoadingComments, -} = props; - -State.init({ - showModal: false, - hasReply: false, -}); - -function stateUpdate(obj) { - State.update(obj); -} - -const CallLibrary = styled.div` - display: none; -`; - -const CommentCard = styled.div` - margin-left: ${isReply ? "2rem" : "0"}; - display: flex; - padding: 14px 16px; - flex-direction: column; - align-items: flex-start; - gap: 12px; - border-radius: "10px"}; - background: ${ - sharedData.sharedCommentId === data.value.metadata.id - ? "rgba(194, 205, 255, 0.8)" - : "#fff" - }; - width: 100%; - `; - -const CommentCardHeader = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - width: 100%; - `; - -const CommentUserContent = styled.div` - display: flex; - align-items: center; - gap: 8px; - `; - -const CommentEdition = styled.div` - display: flex; - align-items: center; - gap: 8px; - `; - -const ProfileImageComment = styled.img` - width: 20px; - height: 20px; - flex-shrink: 0; - border-radius: 20px; - `; - -const CommentUser = styled.p` - color: #000; - font-size: 12px; - font-weight: 500; - line-height: 120%; - margin: 0px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - `; - -const ReplyCounterDiv = styled.div` - display: flex; - align-items: center; - gap: 4px; - `; - -const ReplyIconPurple = styled.img` - width: 14px; - height: 14px; - `; - -const ReplyCounterText = styled.p` - color: #000; - font-size: 10px; - font-weight: 500; - margin: 0px; - `; - -const CommentCardContent = styled.p` - color: #585b5c; - font-size: 12px; - line-height: 18px; - display: flex; - flex-direction: column; - margin: 0px; - `; - -const CommentCardLowerSection = styled.div` - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - gap: 4px; - width: 100%; - `; - -const TimestampCommentDiv = styled.div` - display: flex; - align-items: center; - gap: 4px; - flex: 1 0 0; - `; - -const TimestampIconComment = styled.img` - width: 12px; - height: 12px; - `; - -const TimestampTextComment = styled.span` - color: #000; - font-size: 10px; - font-weight: 300; - margin: 0px; - `; - -const EditedIndication = styled.span` - font-size: 12px; - margin: 0px; -`; - -const DeleteCommentButton = styled.button` - display: flex; - width: 28px; - padding: 2px 12px; - justify-content: center; - align-items: center; - gap: 6px; - align-self: stretch; - border-radius: 4px; - border: 1px solid #c23f38; - background: #f1d6d5; - `; - -const DeleteCommentIcon = styled.img` - width: 14px; - height: 14px; - flex-shrink: 0; - `; - -const ShareCommentButton = styled.button` - display: flex; - width: 28px; - height: 28px; - padding: 2px 12px; - justify-content: center; - align-items: center; - gap: 6px; - border-radius: 4px; - border: solid 1px transparent; - background-image: linear-gradient(white, white), - radial-gradient(circle at top left, #9333ea 0%, #4f46e5 100%); - background-origin: border-box; - background-clip: padding-box, border-box; - `; - -const ShareCommentIcon = styled.img` - width: 14px; - height: 14px; - flex-shrink: 0; - `; - -const ReplyCommentButtonDisabled = styled.div` - display: flex; - padding: 2px 12px; - cursor: not-allowed; - background: rgb(195, 202, 206); - color: rgb(130, 134, 136); - border: 0px; - align-items: center; - gap: 6px; - align-self: stretch; - border-radius: 4px; - `; - -const ReplyCommentButtonActive = styled.div` - cursor: pointer; - display: flex; - padding: 2px 12px; - align-items: center; - gap: 6px; - align-self: stretch; - border-radius: 4px; - background: var(--buttons-yellow-default, #ffd50d); - `; - -const ReplyCommentText = styled.p` - color: var(--primary-black, #000); - font-size: 12px; - font-weight: 500; - line-height: 24px; - margin: 0px; - `; - -const CommentReplySeparator = styled.hr` - height: 0px; - margin: 16px 0 16px 0; - border: 1px solid rgba(208, 214, 217, 1); - `; - -const ReplyContainer = styled.div` - display: flex; - width: 260px; - flex-direction: column; - align-items: flex-start; - gap: 12px; - margin: 0 0 0 35px; - `; - -const ReplyHeader = styled.div` - display: flex; - align-items: center; - gap: 8px; - align-self: stretch; - `; - -const ReplyContent = styled.p` - color: #828688; - font-size: 12px; - line-height: 120%; - margin: 0px; - `; - -const ReplyLowerSection = styled.div` - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - gap: 4px; - `; - -const ReplyButtonSection = styled.div` - display: flex; - justify-content: flex-end; - align-items: center; - gap: 4px; - `; - -const DeleteReplyButton = styled.button` - display: flex; - padding: 2px 12px; - align-items: center; - gap: 6px; - align-self: stretch; - border-radius: 4px; - border: 1px solid #c23f38; - background: #f1d6d5; - `; - -const DeleteReplyText = styled.p` - color: #c23f38; - font-size: 12px; - font-weight: 500; - line-height: 24px; - margin: 0px; - `; - -const AnswerContainer = styled.div` - width: 96%;; - `; - -const renderDeleteModal = () => { - const ModalCard = styled.div` - position: fixed; - z-index: 1; - left: 0; - top: 0; - width: 100%; - height: 100%; - display: flex; - justify-content: center; - align-items: center; - background: rgba(0, 0, 0, 0.7); - `; - const ModalContainer = styled.div` - display: flex; - width: 400px; - padding: 20px; - flex-direction: column; - align-items: flex-start; - gap: 16px; - border-radius: 10px; - background: #fff; - border: 1px solid transparent; - margin-left: auto; - margin-right: auto; - margin-buttom: 50%; - @media only screen and (max-width: 480px) { - width: 90%; - } - `; - const Container = styled.div` - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 20px; - align-self: stretch; - `; - const Footer = styled.div` - display: flex; - flex-direction: row; - align-items: flex-end; - justify-content: end; - gap: 16px; - align-self: stretch; - `; - - return ( - - - -

Delete this comment?

-
- - -
-
-
-
- ); -}; - -function onCommitDeleteComment() { - State.update({ - showDeleteModal: false, - }); - setLoadingComments(true) - setTimeout(() => { - loadComments() - }, 3000); -} - -function closeDeleteCommentModal() { - State.update({ - showDeleteModal: false, - }); -} - -function deleteCommentListener() { - State.update({ saving: true }); - - deleteComment({ - config: getConfig(isTest), - commentId: data.value.metadata.id, - articleId: data.value.metadata.articleId, - rootId: data.value.metadata.rootId, - onCommit: onCommitDeleteComment, - onCancel: closeDeleteCommentModal, - }); -} - -function handleDeleteComment() { - State.update({ - showDeleteModal: true, - }); -} - -function closeModal() { - State.update({ showModal: false }); -} - -function getProperRootId(isEdition) { - if (isEdition) { - return data.value.metadata.rootId; - } - - if (data.answers) { - return data.value.metadata.id; - } else { - return data.value.metadata.rootId; - } -} - -function handleEditComment() { - State.update({ - showModal: true, - editionData: data, - rootId: getProperRootId(true), - }); -} - -function handleReplyListener() { - if (!loggedUserHaveSbt) { - return; - } - - State.update({ - showModal: true, - editionData: undefined, - rootId: getProperRootId(false), - }); -} - -return ( - <> - {state.showDeleteModal && renderDeleteModal()} - - - - - - - {context.accountId == data.accountId && ( - - - Edit - - - ), - className: `info outline mt-2`, - onClick: () => handleEditComment(), - }} - /> - - - - ), - className: `danger outline mt-2`, - onClick: () => handleDeleteComment(), - }} - /> - - )} - - - ( - - - #{hashtag} - - - ), - }} - /> - - - - - - {new Date(data.value.metadata.lastEditTimestamp).toDateString()} - - {data.isEdition && ( - (edited) - )} - -
- {state.showModal && ( - - )} - {( - <> - - Reply - -
- ), - disabled: !loggedUserHaveSbt, - size: "sm", - className: "info outline", - onClick: handleReplyListener, - }} - /> - - )} - - -
-
- {!isReply && ( - <> - {data.answers.length > 0 && ( - - )} - {data.answers.map((answer) => { - return ( - - - - ); - })} - - )} - -); diff --git a/src/NDC/CompactPost.jsx b/src/NDC/CompactPost.jsx deleted file mode 100644 index 7408225..0000000 --- a/src/NDC/CompactPost.jsx +++ /dev/null @@ -1,310 +0,0 @@ -// NDC.CompactPost - -const { - widgets, - article, - kanbanColumns, - handleOpenArticle, - handleFilterArticles, - handleShareButton, - colLabel, - baseActions, -} = props; - -State.init({ - showModal: false, -}); - -function compactPostStateUpdate(obj) { - State.update(obj); -} - -function onCommit() { - State.update({ showModal: false }); -} - -function onCancel() { - State.update({ showModal: false }); -} - -function moveArticleListener() { - //TODO add createArticle function -} - -function getArticleData() { - let newTags = article.value.articleData.tags.filter((tag) => { - let lowerCaseTag = tag.toLowerCase().replace(` `, "-"); - - const lowercaseLabels = []; - kanbanColumns.forEach((col) => - lowercaseLabels.push(col.toLowerCase().replace(` `, "-")) - ); - - return !lowercaseLabels.includes(lowerCaseTag); - }); - - newTags.push(state.newLabel.toLowerCase().replace(` `, "-")); - - let newArticleData = article; - newArticleData.tags = newTags; - - return newArticleData; -} - -const Spinner = () => { - return ( - - - - ); -}; - -const ModalContainer = styled.div` - position: fixed; - display: flex; - justify-content: center; - align-items: center; - top: 0; - left: 0; - height: 100%; - width: 100%; - backdrop-filter: blur(10px); - z-index: 1; - `; - -const ChangeLabelMainContainer = styled.div` - display: flex; - flex-direction: column; - background: white; - padding: 1rem; - border-radius: 12px; - border: 1px solid black; - `; - -const ClosePopUpContainer = styled.div` - display: flex; - flex-direction: row-reverse; - `; - -const CloseIcon = styled.div` - cursor: pointer; - `; - -function handleLabelSelection(selectedLabel) { - State.update({ - newLabel: selectedLabel.replace(` `, "-"), - }); -} - -function closeModal() { - State.update({ showModal: false }); -} - -const modal = ( - - - - - - { - return { title: label, value: col }; - }), - onChange: handleLabelSelection, - }} - /> - - {state.saving ? ( - - ) : ( - <> - Move - - - )} - - ), - }} - /> - - -); - -const header = ( -
- -
- -
-
- , - onClick: () => - handleShareButton(true, { - key: "sb", - type: "sharedBlockheight", - value: article.value.metadata.blockHeight, - }), - }} - /> -
-
-
-
-
-); - -const borders = { - Idea: "border-secondary", - Comment: "border-secondary", - Submission: "border-secondary", - Attestation: "border-secondary", - Sponsorship: "border-secondary", -}; - -const CursorPointer = styled.p` - cursor: pointer; - `; - -function toggleShowModal() { - State.update({ showModal: !state.showModal }); -} - -const articleTags = article.value.articleData.tags ? ( -
- {article.value.articleData.tags.map((tag) => { - const filter = { filterBy: "tag", value: tag }; - return ( - handleFilterArticles(filter)}> - - - ); - })} -
-) : null; - -const articleTitle = ( -
-
-
{article.value.articleData.title}
-
-
-); - -const footerActionButtons = ( -
- - Move - -
- ), - disabled: context.accountId !== article.value.articleData.author, - size: "sm", - className: "info outline w-25", - onClick: toggleShowModal, - }} - /> - - View - - - ), - size: "sm", - className: "info mx-1 w-25", - onClick: () => { - handleOpenArticle(article); - }, - }} - /> - -); - -const CallLibrary = styled.div` - display: block; - `; - -const Card = styled.div` - user-select: none; - &:hover { - box-shadow: rgba(3, 102, 214, 0.3) 0px 0px 0px 3px; - } - - `; - -const LimitedMarkdown = styled.div` - max-height: 6em; - `; - -// Should make sure the posts under the currently top viewed post are limited in size. -const descriptionArea = ( - - - -); - -return ( - <> - {state.showModal && modal} - - {header} -
- {articleTitle} - {descriptionArea} - {articleTags} - {footerActionButtons} -
-
- -); diff --git a/src/NDC/Forum.jsx b/src/NDC/Forum.jsx deleted file mode 100644 index 7a80ca3..0000000 --- a/src/NDC/Forum.jsx +++ /dev/null @@ -1,756 +0,0 @@ -// NDC.Forum -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice"); -const { getArticles, deleteArticle } = VM.require("communityvoice.ndctools.near/widget/lib.article"); -const { isValidUser } = VM.require("communityvoice.ndctools.near/widget/lib.SBT"); -//===============================================INITIALIZATION===================================================== -let { - isTest, - accountId, - authorForWidget, - widgets, - brand, - baseActions, - kanbanColumns, - kanbanRequiredTags, - kanbanExcludedTags, - handleChangeCategory, - categories, - category, - sharedData, -} = props; - -const [searchInputValue, setSearchInputValue] = useState(""); - -accountId = context.accountId; - -function getInitialFilter() { - if (sharedData.sharedBlockheight) { - return { - parameterName: "getPost", - parameterValue: sharedData.sharedBlockheight, - }; - } else if (sharedData.sharedTag) { - return { - parameterName: "tag", - parameterValue: sharedData.sharedTag, - }; - } else if (authorShared) { - return { - parameterName: "author", - parameterValue: authorShared, - }; - } else if (sharedData.sharedArticleId) { - return { - parameterName: "articleId", - parameterValue: sharedData.sharedArticleId, - }; - } else { - return { - parameterName: "", - }; - } -} - -const [articlesToRender, setArticlesToRender] = useState([]); -// loggedUserHaveSbt and canLoggedUserCreateArticle probably have the same behaviour. Check -const [loggedUserHaveSbt, setLoggedUserHaveSbt] = useState(false) -const [canLoggedUserCreateArticle, setCanLoggedUserCreateArticle] = -useState(false); -const [showShareModal, setShowShareModal] = useState(false); -const [sharedElement, setSharedElement] = useState(undefined); -const [showShareSearchModal, setShowShareSearchModal] = useState(false); -const [sharingSearch, setSharingSearch] = useState(false); -const [linkCopied, setLinkCopied] = useState(false); -const [filterBy, setFilterBy] = useState(getInitialFilter()); -const [loadingArticles, setLoadingArticles] = useState(true) - -function loadArticles(category) { - const userFilters = { category: category }; - console.log("Reloading categories", category) - getArticles(getConfig(isTest), userFilters).then((newArticles) => { - setArticlesToRender(newArticles) - setLoadingArticles(false) - }) -} - -useEffect(() => { - setLoadingArticles(true) - loadArticles(category); - const intervalId = setInterval(() => { - loadArticles(category); - }, 30000); - return () => clearInterval(intervalId); -}, [category]); - -useEffect(() => { - isValidUser(context.accountId,getConfig(isTest, context.networkId)).then(isValid=>{ - setLoggedUserHaveSbt(isValid) - setCanLoggedUserCreateArticle(isValid) - }) - //TODO change isValidUser name to getIsValidUser -}, [context.accountId]) - -accountId = context.accountId; - -const tabs = { - SHOW_ARTICLES_LIST: { id: 0 }, - SHOW_ARTICLE: { id: 1 }, - ARTICLE_WORKSHOP: { id: 2 }, - SHOW_ARTICLES_LIST_BY_AUTHORS: { id: 3 }, - //SHOW_KANBAN_VIEW: { id: 4 }, -}; - -function getInitialTabId() { - if (sharedData.sharedBlockheight || sharedData.sharedArticleId) { - return tabs.SHOW_ARTICLE.id; - } else { - return tabs.SHOW_ARTICLES_LIST.id; - } -} - -State.init({ - displayedTabId: getInitialTabId(), - articleToRenderData: {}, - authorsProfiles: [], - firstRender: !isNaN(sharedData.sharedBlockheight) || typeof sharedData.sharedArticleId === "string", -}); - -//=============================================END INITIALIZATION=================================================== - -//==================================================CONSTS========================================================== - -const profile = props.profile ?? Social.getr(`${accountId}/profile`); - -if (profile === null) { - return "Loading"; -} - -let authorProfile = {}; -if (filterBy.parameterName == "author") { - authorProfile = Social.getr(`${filterBy.parameterValue}/profile`); -} - -const navigationPills = [ - { id: tabs.SHOW_ARTICLES_LIST.id, title: "Articles" }, - { id: tabs.SHOW_ARTICLES_LIST_BY_AUTHORS.id, title: "Authors" }, - // { id: tabs.SHOW_KANBAN_VIEW.id, title: "Kanban" }, -]; - -const navigationButtons = [ - // { id: tabs.ARTICLE_WORKSHOP.id, title: "+Create article" }, -]; - -const initialBodyAtCreation = state.editArticleData.value.articleData.body; - -//=================================================END CONSTS======================================================= - -//=================================================GET DATA========================================================= -const finalArticles = state.articles; - -function filterArticlesByTag(tag, articles) { - return articles.filter((article) => { - return article.value.articleData.tags.includes(tag); - }); -} - -function filterArticlesByAuthor(author, articles) { - return articles.filter((article) => { - return article.value.metadata.author === author; - }); -} - -function filterOnePostByBlockHeight(blockHeight, articles) { - if (articles) { - return articles.filter((article) => article.blockHeight === blockHeight); - } else { - return []; - } -} - -function filterOnePostByArticleId(articleId, articles) { - if (articles) { - return articles.filter( - (article) => article.value.metadata.id === articleId - ); - } else { - return []; - } -} - -if (filterBy.parameterName === "tag") { - setArticlesToRender(filterArticlesByTag( - filterBy.parameterValue, - articlesToRender - )); -} else if (filterBy.parameterName === "author") { - setArticlesToRender(filterArticlesByAuthor( - filterBy.parameterValue, - articlesToRender - )); -} else if (filterBy.parameterName === "getPost") { - setArticlesToRender(filterOnePostByBlockHeight( - filterBy.parameterValue, - articlesToRender - )); - - if (articlesToRender.length > 0) { - State.update({ articleToRenderData: articlesToRender[0] }); - } -} else if (filterBy.parameterName === "articleId") { - setArticlesToRender(filterOnePostByArticleId( - filterBy.parameterValue, - articlesToRender - )); - if (articlesToRender.length > 0) { - State.update({ articleToRenderData: articlesToRender[0] }); - } -} -//===============================================END GET DATA======================================================= - -//=============================================STYLED COMPONENTS==================================================== -const AppContainer = styled.div` - max-width: 1800px; - margin: 0 auto; -`; - -const SecondContainer = styled.div` - margin: 0 2rem; -`; - -const ShareInteractionGeneralContainer = styled.div` - position: fixed; - display: flex; - justify-content: center; - align-items: center; - top: 0; - left: 0; - height: 100vh; - width: 100vw; - backdrop-filter: blur(10px); - z-index: 1; -`; - -const ShareInteractionMainContainer = styled.div` - display: flex; - flex-direction: column; - background: white; - padding: 1rem; - border-radious: 12px; -`; - -const ClosePopUpContainer = styled.div` - display: flex; - flex-direction: row-reverse; -`; - -const CloseIcon = styled.div` - cursor: pointer; -`; - -const PopUpDescription = styled.p` - color: #474d55; -`; - -const ShowLinkShared = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - background-color: #f2f6fa; - padding: 1rem 2rem; - border-radius: 17px; -`; - -const LinkShared = styled.span` - color: #0065ff; - word-wrap: anywhere; -`; - -const ClipboardContainer = styled.div` - display: flex; - flex-direction: column; - align-items: center; - margin-left: 0.5rem; - min-width: 2.5rem; -`; - -const ClipboardIcon = styled.i` - color: ${state.linkCopied ? "#0065FF" : "black"}; - transition: color 0.3s linear; - cursor: pointer; -`; - -const CopiedFeedback = styled.span` - font-size: 0.7rem; - color: #6c757d; -`; - -const SmallButton = styled.button` - position: relative; - border: 0; - background: transparent; - width: 35px; - height: 35px; -`; -//===========================================END STYLED COMPONENTS================================================== - -//================================================COMPONENTS======================================================== -const renderShareInteraction = () => { - return ( - - - - { - setShowShareSearchModal(false); - setShowShareModal(false); - setLinkCopied(false); - setSharedElement(undefined); - setSharingSearch(false); - }} - > - -

Share

- - {sharedElement.value - ? "Use this link to share the article" - : sharingSearch - ? "Use this link to share the search" - : "Can't share yet. Reload the app and try again."} - - - {(sharedElement.value || sharingSearch) && ( - {getLink()} - )} - - {(sharedElement.value || sharingSearch) && ( - { - clipboard.writeText(getLink()); - setLinkCopied(true); - }} - /> - )} - {linkCopied && Copied!} - - -
-
- ); -}; - -const renderDeleteModal = () => { - const ModalCard = styled.div` - position: fixed; - z-index: 1; - left: 0; - top: 0; - width: 100%; - height: 100%; - display: flex; - justify-content: center; - align-items: center; - background: rgba(0, 0, 0, 0.7); - `; - const ModalContainer = styled.div` - display: flex; - width: 400px; - padding: 20px; - flex-direction: column; - align-items: flex-start; - gap: 16px; - border-radius: 10px; - background: #fff; - border: 1px solid transparent; - margin-left: auto; - margin-right: auto; - margin-buttom: 50%; - @media only screen and (max-width: 480px) { - width: 90%; - } - `; - const Container = styled.div` - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 20px; - align-self: stretch; - `; - const Footer = styled.div` - display: flex; - flex-direction: row; - align-items: flex-end; - justify-content: end; - gap: 16px; - align-self: stretch; - `; - - return ( - - - -

Delete this post?

-
- - -
-
-
-
- ); -}; - -const getCategoriesSelectorLabel = () => { - return ( - <> - Post & Filter by Categories - - - -

Topics for Community SBT Holders.

- - } - > - -
-
- - ); -}; -//==============================================END COMPONENTS====================================================== - -//=================================================FUNCTIONS======================================================== -function onCommitDeletArticle() { - setArticlesToRender([]) - setTimeout(() => { - loadArticles() - }, 3000); - setFilterBy({ parameterName: "", parameterValue: {} }); - State.update({ - showDeleteModal: false, - deleteArticleData: undefined, - displayedTabId: tabs.SHOW_ARTICLES_LIST.id, - articleToRenderData: undefined, - editArticleData: undefined, - }); -} - -function deletePostListener() { - State.update({ saving: true }); - const article = state.deleteArticleData; - deleteArticle( - getConfig(isTest), - article.value.metadata.id, - onCommitDeletArticle, - closeDeleteArticleModal - ); -} - -function getValidEditArticleDataTags() { - let tags = state.editArticleData.value.articleData.tags ?? []; - let newFormatTags = {}; - - tags && - tags.map((tag) => { - newFormatTags[tag] = ""; - }); - return newFormatTags; -} - -const initialCreateState = { - title: state.editArticleData.value.articleData.title ?? "", - articleBody: - state.editArticleData.value.articleData.body ?? initialBodyAtCreation, - tags: state.editArticleData.value.articleData.tags - ? getValidEditArticleDataTags() - : {}, - libsCalls: { comment: {}, article: {}, emojis: {}, upVotes: {} }, -}; - -function handleOpenArticle(articleToRenderData) { - State.update({ - displayedTabId: tabs.SHOW_ARTICLE.id, - articleToRenderData, - editArticleData: undefined, - }); -} - -function handleEditArticle(articleData) { - State.update({ - displayedTabId: tabs.ARTICLE_WORKSHOP.id, - editArticleData: articleData, - }); -} - -function handleDeleteArticle(articleData) { - State.update({ - showDeleteModal: true, - deleteArticleData: articleData, - }); -} - -function closeDeleteArticleModal() { - State.update({ - showDeleteModal: false, - deleteArticleData: undefined, - }); -} - -function handleFilterArticles(filter) { - setFilterBy({ - parameterName: filter.filterBy, - parameterValue: filter.value, - }); - State.update({ - displayedTabId: tabs.SHOW_ARTICLES_LIST.id, - editArticleData: undefined, - }); -} - -function handleBackButton() { - loadArticles() - if (props.editArticleData) { - setFilterBy({ - parameterName: "", - parameterValue: undefined, - handleBackClicked: true, - }); - State.update({ - displayedTabId: tabs.SHOW_ARTICLE.id, - editArticleData: undefined, - firstRender: false, - }); - } else { - setFilterBy({ - parameterName: "", - parameterValue: undefined, - handleBackClicked: true, - }); - State.update({ - displayedTabId: tabs.SHOW_ARTICLES_LIST.id, - articleToRenderData: {}, - editArticleData: undefined, - firstRender: false, - }); - } -} - -function handleGoHomeButton() { - setFilterBy({ parameterName: "", parameterValue: {} }); - State.update({ - displayedTabId: tabs.SHOW_ARTICLES_LIST.id, - articleToRenderData: {}, - editArticleData: undefined, - }); - loadArticles() -} - -function handlePillNavigation(navegateTo) { - State.update({ displayedTabId: navegateTo, editArticleData: undefined }); -} - -function handleShareButton(showShareModal, sharedElement) { - setShowShareModal(showShareModal) - setSharedElement(sharedElement) -} - -function handleShareSearch(showShareSearchModal, newSearchInputValue) { - //showShareSearchModal is a boolean - setShowShareSearchModal(showShareSearchModal); - setSharingSearch(true); - setSearchInputValue(newSearchInputValue ?? ""); -} - -function getLink() { - const baseUrl = `https://near.org/${widgets.thisForum}?${ - isTest && "isTest=true" - }` - if (sharingSearch) { - const link = `${baseUrl}${ - filterBy.parameterName === "tag" - ? `&st=${filterBy.parameterValue}` - : "" - }${ - searchInputValue !== "" ? `&ss=${searchInputValue}` : "" - }`; - return link; - } else { - const link = `${baseUrl}&${sharedElement.key}=${sharedElement.value}`; - return link; - } -} - -function handleOnCommitArticle(articleId) { - setTimeout(() => { - const userFilters = {id: articleId, sbt: undefined} - getArticles(getConfig(isTest), userFilters).then((newArticles) => { - if(newArticles && newArticles.length > 0){ - State.update({ - displayedTabId: tabs.SHOW_ARTICLE.id, - articleToRenderData: newArticles[0] - }); - } - }) - }, 3000); -} -let category2=category -//===============================================END FUNCTIONS====================================================== -return ( - - - {state.showDeleteModal && renderDeleteModal()} - {(showShareModal || showShareSearchModal) && renderShareInteraction()} - - {state.displayedTabId == tabs.SHOW_ARTICLES_LIST.id && ( -
- { - category2=e - handleChangeCategory(e) - - }, - options: categories - }} - /> -
- )} - {state.displayedTabId == tabs.SHOW_ARTICLES_LIST.id && ( - loadingArticles? - - : - - )} - {state.articleToRenderData.value.articleData.title && - state.displayedTabId == tabs.SHOW_ARTICLE.id && ( - - )} - - {state.displayedTabId == tabs.SHOW_ARTICLES_LIST_BY_AUTHORS.id && ( - - )} - - {state.displayedTabId == tabs.ARTICLE_WORKSHOP.id && ( - - )} -
-
-); diff --git a/src/NDC/Forum/AllArticlesList.jsx b/src/NDC/Forum/AllArticlesList.jsx deleted file mode 100644 index 27f8faa..0000000 --- a/src/NDC/Forum/AllArticlesList.jsx +++ /dev/null @@ -1,284 +0,0 @@ -// NDC.Forum.AllArticlesList -const { arrayIncludesIgnoreCase } = VM.require("communityvoice.ndctools.near/widget/lib.strings") - -//===============================================INITIALIZATION===================================================== - -let { - isTest, - stateUpdate, - articlesToRender, - tabs, - widgets, - addressForArticles, - handleFilterArticles, - handleOpenArticle, - authorForWidget, - initialCreateState, - editArticleData, - handleEditArticle, - showCreateArticle, - handleShareButton, - handleShareSearch, - loggedUserHaveSbt, - filterBy, - baseActions, - handleOnCommitArticle, - sharedSearchInputValue, - category, - sharedData, -} = props; - -if(!articlesToRender) return <> - -const [searchInputValue, setSearchInputValue] = useState( - sharedSearchInputValue ?? "" -); - -function filterArticlesBySearch(articles, searchInputValue) { - if(!searchInputValue || searchInputValue === "") return articles - return articles.filter((article) => { - const { title, body } = article.value.articleData - const { author } = article.value.metadata - const arr = [ title, body, author ] - if (arr.some((item) => item === undefined)) return false - - return arrayIncludesIgnoreCase(arr, searchInputValue) - }); -} - -const articlesFilteredBySearch = filterArticlesBySearch(articlesToRender, searchInputValue); - -const fiveDaysTimeLapse = 5 * 24 * 60 * 60 * 1000; -const newestArticlesWithUpVotes = articlesFilteredBySearch - .filter((article) => article.value.metadata.lastEditTimestamp > Date.now() - fiveDaysTimeLapse) - // .sort((a, b) => b.timeLastEdit - a.timeLastEdit); - -const olderArticlesWithUpVotes = articlesFilteredBySearch - .filter((article) => article.value.metadata.lastEditTimestamp < Date.now() - fiveDaysTimeLapse) - // .sort((a, b) => b.upVotes.length - a.upVotes.length); - -const sortedArticlesToRender = [ - ...newestArticlesWithUpVotes, - ...olderArticlesWithUpVotes, -]; - -//=============================================END INITIALIZATION=================================================== - -//===================================================CONSTS========================================================= - -const AcordionContainer = styled.div` - --bs-accordion-border-width: 0px !important; -`; - -const NoMargin = styled.div` - margin: 0 0.75rem; -`; - -const AccordionBody = styled.div` - padding: 0; -`; - -const ArticlesListContainer = styled.div` - background-color: rgb(248, 248, 249); - margin: 0; -`; - -const CallLibrary = styled.div` - display: none; -`; - -const IconCursorPointer = styled.i` - cursor: pointer; -`; - -const ShareSearchRow = styled.div` - display: flex; - justify-content: flex-start; - align-content: center; - margin-bottom: 1rem; - margin-top: 1rem; -`; - -const ShareSearchText = styled.h6` - margin-bottom: 0; - margin-left: 1rem; - margin-right: 1rem; -`; - -const SearchResult = styled.span` - margin-left: 0.5rem; - font-size: small; -`; - -//=================================================END CONSTS======================================================= - -//==================================================FUNCTIONS======================================================= - -function getDateLastEdit(timestamp) { - const date = new Date(Number(timestamp)); - const dateString = { - date: date.toLocaleDateString(), - time: date.toLocaleTimeString(), - }; - return dateString; -} - -function handleSearch(e) { - setSearchInputValue(e.target.value); -} - -//================================================END FUNCTIONS===================================================== -return ( - <> - {loggedUserHaveSbt ? ( - <> - - -

- -

-
- - - -
-
-
- - ) : ( -
You can't post since you don't own any SBT
- )} - - {searchInputValue && - searchInputValue !== "" && - sortedArticlesToRender.length > 0 && ( - - {`Found ${sortedArticlesToRender.length} articles searching for "${searchInputValue}"`} - - )} - - Share search -
, - onClick: () => handleShareSearch(true, searchInputValue), - }} - /> - -
Previous articles will be loaded in the following days
- - {filterBy.parameterName === "tag" && ( -
-
Filter by tag:
-
- - handleFilterArticles({ filterBy: "", value: "" })} - > -
-
- )} - - {sortedArticlesToRender.length > 0 ? ( - sortedArticlesToRender.map((article, i) => { - const authorProfileCall = Social.getr( - `${article.value.metadata.author}/profile` - ); - - if (authorProfileCall) { - article.authorProfile = authorProfileCall; - } - - // If some widget posts data different than an array it will be ignored - if (!Array.isArray(article.value.articleData.tags)) - article.value.articleData.tags = []; - return ( -
- -
- ); - }) - ) : ( -
{`No articles ${ - searchInputValue !== "" - ? `have been found searching for ${searchInputValue}` - : "uploaded yet" - }`}
- )} -
-
- -); diff --git a/src/NDC/Forum/Create.jsx b/src/NDC/Forum/Create.jsx deleted file mode 100644 index 36e358d..0000000 --- a/src/NDC/Forum/Create.jsx +++ /dev/null @@ -1,359 +0,0 @@ -//NDC.Forum.Create -const { createArticle, editArticle, buildArticle } = VM.require("communityvoice.ndctools.near/widget/lib.article") -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice") - - -const { - isTest, - addressForArticles, - authorForWidget, - stateUpdate, - initialBody, - initialCreateState, - editArticleData, - widgets, - handleFilterArticles, - handleEditArticle, - handlerStateUpdate, - canLoggedUserCreateArticles, - baseActions, - handleOnCommitArticle, - category -} = props; - -const errTextNoBody = "ERROR: no article Body", - errTextNoId = "ERROR: no article Id", - errTextDublicatedId = "ERROR: there is article with such name"; - -State.init({ - ...initialCreateState, - initialBody: props.initialBody ?? "", - tags:[] -}); - -function createStateUpdate(obj) { - State.update(obj); -} - -const tagsArray = - editArticleData && !state.tagsModified ? editArticleData.value.articleData.tags : state.tags; - -const accountId = context.accountId; - -function getRealArticleId() { - if (editArticleData) { - return ( - editArticleData.value.metadata.id ?? - `article/${editArticleData.value.metadata.author}/${editArticleData.value.metadata.createdTimestamp}` - ); - } else { - return `article/${accountId}/${Date.now()}`; - } -} - -function getArticleData() { - const args = { - title: editArticleData.value.articleData.title ?? state.title, - author: editArticleData.value.metadata.author ?? accountId, - lastEditor: accountId, - timeLastEdit: Date.now(), - timeCreate: editArticleData.value.metadata.createdTimestamp ?? Date.now(), - body: state.articleBody, - version: editArticleData ? editArticleData.value.metadata.versionKey + 1 : 0, - navigation_id: null, - tags: tagsArray ?? [], - id: getRealArticleId(), - category: editArticleData.value.articleData.category ?? category, - }; - return args; -} - -function onCommit(articleId) { - State.update({ - title: "", - clearArticleId: true, - tags: [], - clearTags: true, - articleBody: "", - clearArticleBody: true, - initalBody: "", - // showCreatedArticle: true, - showPreview: false, - saving: true, - }); - - //if (!Array.isArray(article.tags)) article.tags = Object.keys(article.tags); - - handleOnCommitArticle(articleId); -} - -function onCancel() { - State.update({ - createdArticle: undefined, - saving: false, - }); -} - -const handleCreate = () => { - const {title, body, tags, category} = getArticleData() - - const articleData = { title, body, tags, category} - - const metadataHelper = { - author: context.accountId, - } - createArticle(getConfig(isTest), articleData, metadataHelper, (id) => onCommit(id), onCancel) -} - -const handleEdit = () => { - const {title, body, tags, id, category} = getArticleData() - - const articleData = { title, body, tags, category } - - const articleMetadata = editArticleData.value.metadata - - editArticle(getConfig(isTest), articleData, articleMetadata, ()=>onCommit(id), onCancel) -} - -function getInitialMarkdownBody() { - if ( - editArticleData && - (!state.articleBody || state.articleBody === editArticleData.value.articleData.body) - ) { - return editArticleData.value.articleData.body; - } else if (state.articleBody && state.articleBody !== editArticleData.value.articleData.body) { - return state.articleBody; - } else { - return state.initialBody == "" || !state.initialBody - ? "Post content (markdown supported)" - : state.initialBody; - } -} - -function switchShowPreview() { - State.update({ - showPreview: !state.showPreview, - initialBody: state.articleBody, - }); -} - -const GeneralContainer = styled.div` - background-color: rgb(248, 248, 249); - margin: 0; - `; - -const Button = styled.button` - margin: 0px 1rem; - display: inline-block; - text-align: center; - vertical-align: middle; - cursor: pointer; - user-select: none; - transition: color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out; - - border: 2px solid transparent; - font-weight: 500; - padding: 0.3rem 0.5rem; - background-color: #010A2D; - border-radius: 12px; - color: white; - text-decoration: none; - - &:hover { - color: #010A2D; - background-color: white; - } - `; - -const CreationContainer = styled.div` - background-color: rgb(230, 230, 230); - border-radius: 20px; - padding: 1rem 0; - position: relative; - `; - -const SecondContainer = styled.div` - min-width: 360px; - background-color: white; - padding: 1rem; - `; - -const BoxShadow = styled.div` - box-shadow: rgba(140, 149, 159, 0.1) 0px 4px 28px 0px; - `; - -const SpinnerContainer = styled.div` - height: 1rem; - width: 1rem; - marginTop: 2px; - `; - -const Spinner = () => { - return ( - - - - ); -}; - -const initialTagsObject = {}; - -Array.isArray(tagsArray) && - tagsArray.forEach((tag) => { - initialTagsObject[tag] = true; - }); - -if(state.saving){ - return ( - - ) -} - -return ( -
- - -
- - {state.showPreview ? ( - {}, - handleFilterArticles: () => {}, - authorForWidget, - handleShareButton: () => {}, - baseActions, - switchShowPreview, - isPreview: state.showPreview - }} - /> - ) : ( -
-
- - State.update(obj), - filterText: (e) => e.target.value, - placeholder: "Post title (case-sensitive)", - editable: editArticleData, - }} - /> -
-
- -
- - State.update({ - articleBody, - clearArticleBody: false, - }), - clearArticleBody: state.clearArticleBody, - }} - /> -
-
-
- State.update(obj), - initialTagsObject, - placeholder: "Input tags", - setTagsObject: (tags) => { - // state.tags = Object.keys(tags); - State.update({ - tagsModified: true, - tags: Object.keys(tags), - }); - }, - }} - /> -
-
- )} -
- - ), - }} - /> - - {state.saving ? ( - - ) : ( - <> - - {editArticleData ? "Save edition" : "Post"} - - - - )} -
- ), - }} - /> -
- -
- - - -); diff --git a/src/NDC/GeneralCard.jsx b/src/NDC/GeneralCard.jsx deleted file mode 100644 index 3740561..0000000 --- a/src/NDC/GeneralCard.jsx +++ /dev/null @@ -1,607 +0,0 @@ -// NDC.GeneralCard -const { getUpVotes } = VM.require("communityvoice.ndctools.near/widget/lib.upVotes") -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice"); - -//===============================================INITIALIZATION===================================================== - -const { - widgets, - isTest, - data, - handleOpenArticle, - handleFilterArticles, - addressForArticles, - authorForWidget, - handleShareButton, - handleEditArticle, - baseActions, - switchShowPreview, - isPreview, - loggedUserHaveSbt -} = props; - -if (!Array.isArray(data.value.articleData.tags) && typeof data.value.articleData.tags === "object") { - data.value.articleData.tags = Object.keys(data.value.articleData.tags); -} - -data.value.articleData.tags = data.value.articleData.tags.filter((tag) => tag !== undefined && tag !== null); - -const tags = data.value.articleData.tags; -const accountId = data.value.metadata.author; -const title = data.value.articleData.title; -const content = data.value.articleData.body; -const timeLastEdit = data.value.metadata.lastEditTimestamp; -const id = data.value.metadata.id ?? `${data.author}-${data.metadata.createdTiemestamp}`; -const [upVotes, setUpVotes] = useState(undefined) -const [loadingUpVotes, setLoadingUpVotes] = useState(true) - -function loadUpVotes() { - getUpVotes(getConfig(isTest),id).then((newVotes) => { - setUpVotes(newVotes) - setLoadingUpVotes(false) - }) -} - -useEffect(() => { - loadUpVotes() - setInterval(() => { - loadUpVotes() - }, 30000) -}, []) - - -function stateUpdate(obj) { - State.update(obj); -} - -State.init({ - verified: true, - start: true, - voted: false, - sliceContent: true, -}); -//=============================================END INITIALIZATION=================================================== - -//===================================================CONSTS========================================================= - -//=================================================END CONSTS======================================================= - -//==================================================FUNCTIONS======================================================= -function getPublicationDate(creationTimestamp) { - if (creationTimestamp == 0) { - return "Creation timestamp passed wrong"; - } - return new Date(creationTimestamp).toDateString(); -} - -function getUserName() { - const profile = data.authorProfile; - - return profile.name ?? getShortUserName(); -} - -const getShortUserName = () => { - const userId = accountId; - - if (userId.length === 64) return `${userId.slice(0, 4)}..${userId.slice(-4)}`; - const name = userId.slice(0, -5); // truncate .near - - return name.length > 20 ? `${name.slice(0, 20)}...` : name; -}; - -function toggleShowModal() { - State.update({ showModal: !state.showModal }); -} - -//================================================END FUNCTIONS===================================================== - -//==============================================STYLED COMPONENTS=================================================== - -const CardContainer = styled.div` - box-shadow: rgba(140, 149, 159, 0.1) 0px 4px 28px 0px; - `; - -const Card = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - padding: 16px; - gap: 16px; - background: rgba(140, 149, 159, 0.1) 0px 4px 28px 0px; - border-radius: 10px; - `; -const HeaderCard = styled.div` - display: flex; - flex-direction: row; - align-items: center; - padding: 0px; - gap: 12px; - width: 100%; - `; - -const profilePictureStyles = { - width: "45px", - height: "45px", - borderRadius: "50%", -}; -const HeaderContent = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - padding: 0px; - gap: 4px; - width: 70%; - `; -const HeaderButtonsContainer = styled.div` - display: flex; - gap: 0.5rem; - `; -const HeaderContentText = styled.div` - display: flex; - width: 100%; - flex-direction: column; - align-items: flex-start; - padding: 0px; - cursor: pointer; - `; -const NominationName = styled.p` - font-weight: 500; - font-size: 14px; - margin: 0; - align-items: center; - color: #000000; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - `; -const NominationUser = styled.p` - font-style: normal; - font-weight: 400; - font-size: 12px; - margin: 0px; - line-height: 120%; - display: flex; - align-items: center; - color: #828688; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - `; - -const KeyIssues = styled.div` - display: flex; - flex-direction: row; - align-items: flex-start; - padding: 12px; - gap: 12px; - background: #ffffff; - border: 1px solid rgb(248, 248, 249); - border-radius: 6px; - width: 100%; - `; -const KeyIssuesContent = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - padding: 0px; - gap: 12px; - width: 100%; - `; -const KeyIssuesHeader = styled.div` - display: flex; - flex-direction: row; - align-items: flex-start; - padding: 0px; - gap: 12px; - `; -const KeyIssuesTitle = styled.p` - font-style: normal; - font-weight: 700; - font-size: 14px; - line-height: 120%; - margin-bottom: 0; - `; -const KeyIssuesContainer = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - padding: 0px; - gap: 8px; - overflow-y: scroll; - max-height: 250px; - width: 100%; - border: 1px solid rgb(248, 248, 249); - border-radius: var(--bs-border-radius-lg) !important; - `; - -const ArticleBodyContainer = styled.div` - margin: 0 0.5rem 0.5rem 0.5rem; - `; - -const LowerSection = styled.div` - display: flex; - width: 100%; - flex-direction: column; - justify-content: center; - align-items: flex-start; - gap: 8px; - `; -const LowerSectionContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - gap: 12px; - align-self: stretch; - `; -const ButtonsLowerSection = styled.div` - display: flex; - flex-direction: row; - align-items: center; - padding: 0px; - width: 100%; - `; -const TextLowerSectionContainer = styled.div` - display: flex; - flex-direction: row; - align-items: center; - padding: 0px; - gap: 4px; - width: 239px; - height: 24px; - cursor: pointer; - - flex-grow: 1; - `; -const TimestampText = styled.div` - font-style: italic; - font-weight: 300; - font-size: 11px; - line-height: 14px; - margin: 0px; - gap: 2px; - color: #000000; - - b { - font-weight: 600; - } - `; -const ButtonsContainer = styled.div` - display: flex; - flex-direction: row; - align-items: center; - padding: 0px; - gap: 4px; - width: 87px; - height: 28px; - `; -const TagSection = styled.div` - display: flex; - align-items: flex-start; - gap: 4px; - flex-wrap: wrap; - overflow: hidden; - cursor: pointer; - `; - -const Element = styled.div` - width: 150px; - display: flex; - justify-content: space-between; - align-items: center; - cursor: pointer; - padding: 10px; - - &:hover { - border-radius: 6px; - background: #f8f8f9; - } - `; - -const CallLibrary = styled.div` - display: none; - `; -//============================================END STYLED COMPONENTS================================================= - -//=================================================MORE STYLES====================================================== - -const profileImageStyles = { - width: profilePictureStyles.width, - height: profilePictureStyles.height, - borderRadius: profilePictureStyles.borderRadius, - overflow: "hidden", -}; - -//===============================================END MORE STYLES==================================================== - -//=================================================COMPONENTS======================================================= - -const inner = ( -
- -
-); - -const renderTags = () => { - return ( - <> - {tags && - tags.map((tag) => { - const filter = { filterBy: "tag", value: tag }; - - return ( -
handleFilterArticles(filter)}> - {tag && ( - - )} -
- ); - })} - - ); -}; - -const renderArticleBody = () => { - let displayedContent = state.sliceContent ? content.slice(0, 1000) : content; - return ( - - ( - - - #{hashtag} - - - ), - }} - /> - {state.sliceContent && content.length > 1000 && ( - { - State.update({ sliceContent: false }); - }, - icon: , - }, - }} - /> - )} - - ); -}; - -//===============================================END COMPONENTS==================================================== - -//===================================================RENDER======================================================== - -return ( - - - {state.showModal && ( - - )} - -
- - { - // - // { - // handleOpenArticle(data); - // }} - // > - // {getUserName()} - // {getShortUserName()} - // - // - } -
- - - , - onClick: () => - handleShareButton(true, { - key: "said", - type: "sharedArticleId", - value: data.value.metadata.id, - }), - }} - /> - -
- - { - handleOpenArticle(data); - }} - > - {title} - - - - {renderArticleBody()} - - - - {tags.length > 0 && ( - - - - Tags - -
- {renderTags()} -
-
-
- )} - - { - handleOpenArticle(data); - }} - > - - - {getPublicationDate(timeLastEdit)} - by - {author} - - - - -
-
- - - Add comment - - -
- ), - disabled: !loggedUserHaveSbt, - size: "sm", - className: "info outline w-25", - onClick: isPreview - ? () => {} - : toggleShowModal, - }} - /> - - View - -
- ), - size: "sm", - className: "info w-25", - onClick: () => handleOpenArticle(data), - }} - /> - {context.accountId === data.author && ( - - Edit - - - ), - className: `info w-25`, - onClick: () => - isPreview - ? switchShowPreview() - : handleEditArticle(data), - }} - /> - )} - - -
-
-
-
-); diff --git a/src/NDC/NavBar.jsx b/src/NDC/NavBar.jsx deleted file mode 100644 index 65de90c..0000000 --- a/src/NDC/NavBar.jsx +++ /dev/null @@ -1,288 +0,0 @@ -// NDC.NavBar - -const { - handleGoHomeButton, - handlePillNavigation, - brand, - pills, - navigationButtons, - isTest, - displayedTabId, - writersWhiteList, - handleFilterArticles, - filterParameter, - handleBackButton, - tabs, - widgets, -} = props; - -function stateUpdate(obj) { - State.update(obj); -} -/* - ======================================================PILLS EXAMPLE==================================================== - *Note: the first pill allways has to be the first one displayed* - pills: [{ - id: string, - title: string, - }] - ============(When modified to be web app we should delete action to replace it with a propper State.update)============ - - ======================================================BRAND EXAMPLE==================================================== - brand: { - homePageId: string, - brandName: string, - logoHref: string, - logoRemWidth: number/string, - logoRemHeight: number/string, - } - - ============(When modified to be web app we should delete action to replace it with a propper State.update)============ - */ - -const loggedUserAccountId = context.accountId; - -// const canLoggedUserCreateArticle = state.canLoggedUserCreateArticle; - -const logoRemWidth = brand.logoRemWidth - ? brand.logoRemWidth + "rem" - : undefined; -const logoRemHeight = brand.logoRemHeight - ? brand.logoRemHeight + "rem" - : undefined; - -if ( - !stateUpdate || - !(displayedTabId + "") || - !pills || - (brand && (!brand.logoHref || !(brand.homePageId + ""))) -) { - const crucialPropMissingMsg = "The following crucial props are missing:"; - return ( -
-

{crucialPropMissingMsg}

-
    - {!stateUpdate &&
  • stateUpdate
  • } - - {!(displayedTabId + "") && ( -
  • displayedTabId
  • - )} - - {!pills &&
  • pills
  • } - - {brand && !brand.logoHref && ( -
  • brand.logoHref
  • - )} - - {brand && !(brand.homePageId + "") && ( -
  • brand.homePageId
  • - )} -
-
- ); -} - -//============================================Styled components================================================== -const BrandLogoContainer = styled.div` - width: ${logoRemWidth ?? "4rem"}; - height: ${logoRemHeight ?? "4rem"}; - cursor: pointer; - `; - -const activeColor = "#9333EA"; - -const Pill = styled.div` - font-family: system-ui; - font-weight: 500; - font-size: 1.2rem; - line-height: 24px; - color: black; - cursor: pointer; - user-select: none; - - &:hover { - color: ${activeColor}; - } - `; - -const StylessATag = styled.a` - &:hover { - text-decoration: none; - } - `; - -const BackButton = styled.div` - cursor: pointer; - `; - -const CallLibrary = styled.div` - display: none; - `; -//============================================End styled components============================================== - -//=================================================Components==================================================== - -const renderButton = (button, i) => { - return ( - { - handlePillNavigation(button.id); - State.update({ - selectedButtonIndex: i, - }); - }, - text: button.title, - className: - state.selectedButtonIndex == i ? "primary light" : "primary dark", - }, - }} - /> - ); -}; -//==============================================End components=================================================== - -//==================================================FUNCTIONS==================================================== - -function realHandleBackButton() { - State.update({ - selectedButtonIndex: undefined, - }); - - handleBackButton(); -} - -function realHandleGoHome() { - State.update({ - selectedButtonIndex: undefined, - }); - handleGoHomeButton(); -} - -//================================================END FUNCTIONS=================================================== -return ( - <> -
-
- {brand && ( - - - - )} - - - { - // navigationButtons && - // loggedUserAccountId && - // canLoggedUserCreateArticle && - // navigationButtons.map((button, i) => { - // return !(button.id + "") || !button.title ? ( - //

Button passed wrong

- // ) : ( - //
{renderButton(button, i)}
- // ); - // }) - } -
-
- - {(((filterParameter == "tag" || filterParameter == "author") && - displayedTabId == tabs.SHOW_ARTICLES_LIST.id) || - displayedTabId == tabs.SHOW_ARTICLE.id || - displayedTabId == tabs.ARTICLE_WORKSHOP.id || - displayedTabId == tabs.SHOW_ARTICLES_LIST_BY_AUTHORS.id) && ( - - - Back - - )} - -); diff --git a/src/NDC/Reactions.jsx b/src/NDC/Reactions.jsx deleted file mode 100644 index 6ffb110..0000000 --- a/src/NDC/Reactions.jsx +++ /dev/null @@ -1,302 +0,0 @@ -// NDC.Reactions -const { getReactions, createReaction } = VM.require("communityvoice.ndctools.near/widget/lib.reactions") -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice") - -const { - isTest, - authorForWidget, - elementReactedId, - widgets, - disabled, - baseActions, -} = props; -// Don't forget to put space between emoji and text -> "❤️ Positive" -const initialEmoji = "🤍 Like"; -// It is important that 'Heart' Positive emoji is first -const emojiArray = [ - "❤️ Positive", - "🙏 Thank you", - "💯 Definitely", - "👀 Thinking", - "🔥 Awesome", - "👍 Like", - "🙌 Celebrate", - "👏 Applause", - "⚡ Lightning", - "⋈ Bowtie", -]; - -const [reactionsData, setReactionsData] = useState({reactionsStatistics: [], userEmoji: undefined}) -const [showEmojiList, setShowEmojiList] = useState(false) -const [loading, setLoading] = useState(true) - -function loadReactions() { - getReactions( getConfig(isTest), elementReactedId, context.accountId).then((reactions) => { - setReactionsData(reactions) - setLoading(false) - }) -} - -useEffect(() => { - loadReactions() - setInterval(() => { - loadReactions() - }, 30000) -}, []) - -// ================= Mouse Handlers =============== - -function handleOnMouseEnter() { - if (!disabled) { - setShowEmojiList(true) - } -} - -function handleOnMouseLeave() { - setShowEmojiList(false) -} - -function onCommit() { - setLoading(true) - setTimeout(() => { - loadReactions() - }, 3000); -} - -function onCancel() { - setLoading(false) -} - -function reactListener(emojiToWrite) { - if (loading || disabled) { - return; - } - setLoading(true) - - // decide to put unique emoji or white heart (unreaction emoji) - // const emojiToWrite = - // emojiMessage === initialEmoji ? emojiArray[0] : emojiMessage; - - const author = context.accountId - const result = createReaction(getConfig(isTest), emojiToWrite, elementReactedId, author, onCommit, onCancel) - if(result.error) { - console.error(result.data) - } - -} - -// =============== CSS Styles =============== -const Button = styled.button` - min-width: fit-content; - background: transparent; - display: inline-flex; - align-items: center; - justify-content: start; - height: 2.5em; - padding: 6px 12px; - margin: 2px 0; - border: 0; - border-radius: .375rem; - ${ - !disabled && - `:hover { - background: #EBEBEB; - outline: 1px solid #C6C7C8; - }` - } - - `; - -const SmallReactButton = styled.button` - background: transparent; - display: inline-flex; - align-items: center; - justify-content: start; - width: fit-content; - height: 2.5em; - padding: 6px 12px; - margin: 2px 0; - border: 0; - border-radius: .375rem; - ${ - !disabled && - `:hover { - background: #EBEBEB; - outline: 1px solid #C6C7C8; - }` - } - `; - -const SmallButton = styled.button` - position: relative; - border: 0; - background: transparent; - width: 35px; - height: 35px; - color: ${({ isHeart }) => (isHeart ? "red" : "")}; - `; - -const SmallButtonSpan = styled.span` - font-size: 19px; - :hover{ - position: absolute; - font-size: 35px; - bottom: -5px; - width: 35px; - height: 40px; - transform: translateX(-50%) translateY(-50%); - } - - @media (max-width: 599px) { - ::before { - position: absolute; - width: 100%; - height: 100%; - background-color: rgba(255, 255, 255, .4); - content: "";} - :hover{ - ::before { - position: absolute; - width: 100%; - height: 120%; - background-color: rgba(255, 255, 255, .4); - content: "";} - } - - } - `; - -// =============== NEW CSS Styles ===============!!!!!!!! -const EmojiWrapper = styled.div` - display: inline-block; - position: relative; - overflow: visible !important; - padding-left: 8px; - `; - -const EmojiListWrapper = styled.div` - display: flex; - flex-wrap: wrap; - padding: 0.5rem; - - background: white; - border-radius: 1rem; - box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.15) !important; - position: absolute; - right: 0; - width: 370px; - max-width: 35vw; - flex-wrap: wrap; - display: ${({ show }) => (show ? "flex" : "none")}; - transform: translateY(-10%); - zIndex: 2; - `; - -const SpinnerContainer = styled.div` - height: 1rem; - width: 1rem; - marginTop: 2px; - `; - -const CallLibrary = styled.div` - display: none; - `; - -// =============== NEW JSX ===============!!!!!!!! -const Overlay = () => { - return ( - - {emojiArray && - emojiArray.map((item) => { - return ( - reactListener(item)} - isHeart={index === 0} - > - -
- {item.slice(2)} -
- - } - > - {item.slice(0, 2)} -
-
- ); - })} -
- ); -}; - -const Spinner = () => { - return ( - - - - ); -}; - -const renderReaction = (item, isInButton) => { - return ( - ((item.accounts.includes(context.accountId) && isInButton) || - (!item.accounts.includes(context.accountId) && !isInButton)) && ( - - - - ) - ); -}; - -return ( - <> - - {!disabled && ( - <> - {reactionsData.userEmoji ? ( - - {loading ? - - : - reactionsData.reactionsStatistics && - reactionsData.reactionsStatistics.map((item) => - renderReaction(item, true) - ) - } - - ) : ( - - )} - - )} - - {reactionsData.reactionsStatistics && - reactionsData.reactionsStatistics.map((item) => - renderReaction(item, false) - )} - - -); diff --git a/src/NDC/UpVoteButton.jsx b/src/NDC/UpVoteButton.jsx deleted file mode 100644 index 8f691a5..0000000 --- a/src/NDC/UpVoteButton.jsx +++ /dev/null @@ -1,122 +0,0 @@ -//NDC.UpVoteButton -const { createUpVote, deleteUpVote } = VM.require("communityvoice.ndctools.near/widget/lib.upVotes") -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice") - -const { - isTest, - authorForWidget, - reactedElementData, - widgets, - disabled, - upVotes: articleUpVotes, - baseActions, - loadUpVotes, - loadingUpVotes, - setLoadingUpVotes, - setUpVotes, -} = props; - -const data = reactedElementData; - -let userVote = articleUpVotes? articleUpVotes.find((vote) => vote.accountId === context.accountId) : undefined; - -function getUpVoteButtonClass() { - if (userVote) { - return "info"; - } else { - return "info outline"; - } -} - -function onCommitUpVotes() { - setLoadingUpVotes(true) - setUpVotes([]) - setTimeout(() => { - loadUpVotes() - }, 3000); -} - -function onCancelUpVotes() { - setLoadingUpVotes(false) -} - -function handleUpVote() { - userVote? - deleteUpVote( - getConfig(isTest), - userVote.value.metadata.articleId, - userVote.value.metadata.id, - onCommitUpVotes, - onCancelUpVotes - ) - : - createUpVote( - getConfig(isTest), - data.value.metadata.id, - data.value.metadata.author, - onCommitUpVotes, - onCancelUpVotes - ) -} - -const IconContainer = styled.div` - transform: rotate(-90deg); - `; - -const Icon = styled.i` - margin: 0px !important; - `; - -const CallLibrary = styled.div` - display: none; - `; - - -const SpinnerContainer = styled.div` - height: 1.3rem; - width: 1.3rem; - marginTop: 2px; - `; - -return ( - <> -
- {loadingUpVotes ? - - - - -
, - size: "sm", - }} - /> - : - - {`+${articleUpVotes.length}`} - - - - - ), - disabled: disabled, - className: `${getUpVoteButtonClass()}`, - size: "sm", - onClick: handleUpVote, - }} - /> - } - - -); diff --git a/src/Profile/ShortInlineBlock.jsx b/src/Profile/ShortInlineBlock.jsx index f90ea70..2ac8f09 100644 --- a/src/Profile/ShortInlineBlock.jsx +++ b/src/Profile/ShortInlineBlock.jsx @@ -1,63 +1,66 @@ -const accountId = props.accountId; -const profile = props.profile ?? Social.getr(`${accountId}/profile`); +const accountId = props.accountId +const profile = props.profile ?? Social.getr(`${accountId}/profile`) -const maxNameLength = props.maxNameLength; +const maxNameLength = props.maxNameLength -const widgets = props.widgets; +const widgets = props.widgets -let name = profile.name; +let name = profile.name -const maxWidth = props.maxWidth ?? "60%"; +const maxWidth = props.maxWidth ?? '60%' const inner = ( -
- -
-
- {name - ? name - : maxNameLength - ? accountId.slice(0, maxNameLength) + "..." - : accountId} -
-
- - @{accountId} - -
+
+ +
+
+ {name + ? name + : maxNameLength + ? accountId.slice(0, maxNameLength) + '...' + : accountId} +
+
+ + @{accountId} + +
+
-
-); +) return ( -
- {props.tooltip ? ( - - ) : ( - inner - )} -
-); +
+ {props.tooltip ? ( + + ) : ( + inner + )} +
+) diff --git a/src/TagsEditor.jsx b/src/TagsEditor.jsx index 37c4dee..38a05c3 100644 --- a/src/TagsEditor.jsx +++ b/src/TagsEditor.jsx @@ -1,87 +1,90 @@ -const tagsPattern = "*/profile/tags/*"; -const placeholder = props.placeholder ?? "Tags"; -const initialTagsObject = props.initialTagsObject || {}; +const tagsPattern = '*/profile/tags/*' +const placeholder = props.placeholder ?? 'Tags' +const initialTagsObject = props.initialTagsObject || {} -const tagsOptions = {}; +const tagsOptions = {} const normalizeTag = (tag) => - tag - .replaceAll(/[- \.]/g, "_") - .replaceAll(/[^\w]+/g, "") - .replaceAll(/_+/g, "-") - .replace(/^-+/, "") - .replace(/-+$/, "") - .toLowerCase() - .trim("-"); + tag + .replaceAll(/[- \.]/g, '_') + .replaceAll(/[^\w]+/g, '') + .replaceAll(/_+/g, '-') + .replace(/^-+/, '') + .replace(/-+$/, '') + .toLowerCase() + .trim('-') -const tagsCount = {}; +const tagsCount = {} const processTagsObject = (obj) => { - Object.entries(obj).forEach((kv) => { - if (typeof kv[1] === "object") { - processTagsObject(kv[1]); - } else { - const tag = normalizeTag(kv[0]); - tagsCount[tag] = (tagsCount[tag] || 0) + 1; - } - }); -}; + Object.entries(obj).forEach((kv) => { + if (typeof kv[1] === 'object') { + processTagsObject(kv[1]) + } else { + const tag = normalizeTag(kv[0]) + tagsCount[tag] = (tagsCount[tag] || 0) + 1 + } + }) +} const getTags = () => { - processTagsObject(tagsOptions); - const tags = Object.entries(tagsCount); - tags.sort((a, b) => b[1] - a[1]); - return tags.map((t) => ({ - name: t[0], - count: t[1], - })); -}; + processTagsObject(tagsOptions) + const tags = Object.entries(tagsCount) + tags.sort((a, b) => b[1] - a[1]) + return tags.map((t) => ({ + name: t[0], + count: t[1], + })) +} if (!state.allTags) { - initState({ - allTags: getTags(), - tags: Object.keys(initialTagsObject).map((tag) => ({ - name: normalizeTag(tag), - })), - originalTags: Object.fromEntries( - Object.keys(initialTagsObject).map((tag) => [tag, null]) - ), - id: `tags-selector-${Date.now()}`, - }); + initState({ + allTags: getTags(), + tags: Object.keys(initialTagsObject).map((tag) => ({ + name: normalizeTag(tag), + })), + originalTags: Object.fromEntries( + Object.keys(initialTagsObject).map((tag) => [tag, null]) + ), + id: `tags-selector-${Date.now()}`, + }) } const setTags = (tags) => { - props.forceClear && props.stateUpdate({ clearTags: false }); - tags = tags.map((o) => { - o.name = normalizeTag(o.name); - return o; - }); - State.update({ tags }); - if (props.setTagsObject) { - props.setTagsObject( - Object.assign({}, Object.fromEntries(tags.map((tag) => [tag.name, ""]))) - ); - } -}; + props.forceClear && props.stateUpdate({ clearTags: false }) + tags = tags.map((o) => { + o.name = normalizeTag(o.name) + return o + }) + State.update({ tags }) + if (props.setTagsObject) { + props.setTagsObject( + Object.assign( + {}, + Object.fromEntries(tags.map((tag) => [tag.name, ''])) + ) + ) + } +} return ( - <> - - {props.debug && ( -
- Debugging tags: -
{JSON.stringify(state.tags)}
-
- )} - -); + <> + + {props.debug && ( +
+ Debugging tags: +
{JSON.stringify(state.tags)}
+
+ )} + +) diff --git a/src/config/CommunityVoice.jsx b/src/config/CommunityVoice.jsx index da59051..29879a8 100644 --- a/src/config/CommunityVoice.jsx +++ b/src/config/CommunityVoice.jsx @@ -1,20 +1,21 @@ -function getConfig(isTest,networkId) { - const componentsOwner = "communityvoice.ndctools.near"; - const authorForWidget = "communityvoice.ndctools.near"; - const configWidget = "home"; +function getConfig(isTest, networkId) { + const componentsOwner = 'chatter.cheddar.near' + const authorForWidget = 'chatter.cheddar.near' + const configWidget = 'home' + return { isTest, networkId, baseActions: { - article: "communityVoiceArticle", - upVote: "communityVoiceUpVote", - reaction: "communityVoiceReaction", - comment: "communityVoiceComment", + article: 'cheddarChatterArticle', + upVote: 'cheddarChatterUpVote', + reaction: 'cheddarChatterReaction', + comment: 'cheddarChatterComment', }, componentsOwner, authorForWidget, - forumURL: `${authorForWidget}/widget/${configWidget}` + forumURL: `${authorForWidget}/widget/${configWidget}`, } } -return { getConfig } \ No newline at end of file +return { getConfig } diff --git a/src/home.jsx b/src/home.jsx index 499f7fe..22db89e 100644 --- a/src/home.jsx +++ b/src/home.jsx @@ -1,136 +1,145 @@ // Community voice -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice"); -const { getCategories } = VM.require("communityvoice.ndctools.near/widget/lib.categories"); +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) || { getConfig: () => {} } +const { getCategories } = VM.require( + 'chatter.cheddar.near/widget/lib.categories' +) + +if (typeof getCategories !== 'function') { + return <> +} let { - isTest, - accountId, - sb: sharedBlockheight, - st: sharedTag, - said: sharedArticleId, - scid: sharedCommentId, - ss: sharedSearch, -} = props; + isTest, + accountId, + sb: sharedBlockheight, + st: sharedTag, + said: sharedArticleId, + scid: sharedCommentId, + ss: sharedSearch, +} = props -State.init({categories:getCategories(),category:getCategories().value}) +State.init({ categories: getCategories(), category: getCategories().value }) const handleChangeCategory = (category) => { - State.update({category}) + State.update({ category }) } const sharedData = { - sharedBlockheight: sharedBlockheight ? Number(sharedBlockheight) : undefined, - sharedTag, - sharedArticleId, - sharedCommentId, - sharedSearch, -} + sharedBlockheight: sharedBlockheight + ? Number(sharedBlockheight) + : undefined, + sharedTag, + sharedArticleId, + sharedCommentId, + sharedSearch, +} -const componentsOwner = "communityvoice.ndctools.near"; -const authorForWidget = "communityvoice.ndctools.near"; -const configWidget = "home"; +const componentsOwner = 'chatter.cheddar.near' +const authorForWidget = 'chatter.cheddar.near' +const configWidget = 'home' const widgets = { - thisForum: `${authorForWidget}/widget/${configWidget}`, + thisForum: `${authorForWidget}/widget/${configWidget}`, - views: { - editableWidgets: { - ndcForum: `${componentsOwner}/widget/NDC.Forum`, - create: `${componentsOwner}/widget/NDC.Forum.Create`, - header: `${componentsOwner}/widget/NDC.NavBar`, - showArticlesList: `${componentsOwner}/widget/NDC.Forum.AllArticlesList`, - showArticlesListSortedByAuthors: `${componentsOwner}/widget/NDC.AllArticlesSortByAuthors`, - articlesByAuthorCard: `${componentsOwner}/widget/NDC.ArticlesByAuthorCard`, - generalCard: `${componentsOwner}/widget/NDC.GeneralCard`, - articleView: `${componentsOwner}/widget/NDC.ArticleView`, - reactions: `${componentsOwner}/widget/NDC.Reactions`, - addComment: `${componentsOwner}/widget/NDC.AddComment`, - commentView: `${componentsOwner}/widget/NDC.CommentView`, - upVoteButton: `${componentsOwner}/widget/NDC.UpVoteButton`, - profileShortInlineBlock: `${componentsOwner}/widget/Profile.ShortInlineBlock`, - tagsEditor: `${componentsOwner}/widget/TagsEditor`, - kanbanBoard: `${componentsOwner}/widget/NDC.KanbanBoard`, - compactPost: `${componentsOwner}/widget/NDC.CompactPost`, - articleHistory: `${componentsOwner}/widget/NDC.ArticleHistory.Handler`, - articleHistoryFirstContainer: `${componentsOwner}/widget/NDC.ArticleHistory.Container`, - articleHistorySecondContainer: `${componentsOwner}/widget/NDC.ArticleHistory.SecondContainer`, - }, - standardWidgets: { - fasterTextInput: `f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb/widget/fasterTextInput`, - markownEditorIframe: `f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb/widget/MarkdownEditorIframe`, - styledComponents: "rubycop.near/widget/NDC.StyledComponents", - newStyledComponents: { - Element: { - Badge: "nearui.near/widget/Element.Badge", - User: "nearui.near/widget/Element.User", - }, - Feedback: { - Spinner: "nearui.near/widget/Feedback.Spinner", + views: { + editableWidgets: { + ndcForum: `${componentsOwner}/widget/Cheddar.Forum`, + create: `${componentsOwner}/widget/Cheddar.Forum.Create`, + header: `${componentsOwner}/widget/Cheddar.NavBar`, + showArticlesList: `${componentsOwner}/widget/Cheddar.Forum.AllArticlesList`, + showArticlesListSortedByAuthors: `${componentsOwner}/widget/Cheddar.AllArticlesSortByAuthors`, + articlesByAuthorCard: `${componentsOwner}/widget/Cheddar.ArticlesByAuthorCard`, + generalCard: `${componentsOwner}/widget/Cheddar.GeneralCard`, + articleView: `${componentsOwner}/widget/Cheddar.ArticleView`, + reactions: `${componentsOwner}/widget/Cheddar.Reactions`, + addComment: `${componentsOwner}/widget/Cheddar.AddComment`, + commentView: `${componentsOwner}/widget/Cheddar.CommentView`, + upVoteButton: `${componentsOwner}/widget/Cheddar.UpVoteButton`, + profileShortInlineBlock: `${componentsOwner}/widget/Profile.ShortInlineBlock`, + tagsEditor: `${componentsOwner}/widget/TagsEditor`, + kanbanBoard: `${componentsOwner}/widget/Cheddar.KanbanBoard`, + compactPost: `${componentsOwner}/widget/Cheddar.CompactPost`, + articleHistory: `${componentsOwner}/widget/Cheddar.ArticleHistory.Handler`, + articleHistoryFirstContainer: `${componentsOwner}/widget/Cheddar.ArticleHistory.Container`, + articleHistorySecondContainer: `${componentsOwner}/widget/Cheddar.ArticleHistory.SecondContainer`, }, - Input: { - Button: "nearui.near/widget/Input.Button", - Checkbox: "nearui.near/widget/Input.Checkbox", - Select: "nearui.near/widget/Input.Select", + standardWidgets: { + fasterTextInput: `f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb/widget/fasterTextInput`, + markownEditorIframe: `devhub.near/widget/devhub.components.molecule.SimpleMDE`, + styledComponents: 'rubycop.near/widget/NDC.StyledComponents', + newStyledComponents: { + Element: { + Badge: 'nearui.near/widget/Element.Badge', + User: 'nearui.near/widget/Element.User', + }, + Feedback: { + Spinner: 'nearui.near/widget/Feedback.Spinner', + }, + Input: { + Button: 'nearui.near/widget/Input.Button', + Checkbox: 'nearui.near/widget/Input.Checkbox', + Select: 'nearui.near/widget/Input.Select', + }, + }, + socialMarkdown: 'mob.near/widget/SocialMarkdown', + profileOverlayTrigger: 'mob.near/widget/Profile.OverlayTrigger', + profileImage: 'mob.near/widget/ProfileImage', + wikiOnSocialDB_TooltipProfiles: `testwiki.near/widget/WikiOnSocialDB_TooltipProfiles`, + navBarImg: 'mob.near/widget/Image', }, - }, - socialMarkdown: "mob.near/widget/SocialMarkdown", - profileOverlayTrigger: "mob.near/widget/Profile.OverlayTrigger", - profileImage: "mob.near/widget/ProfileImage", - wikiOnSocialDB_TooltipProfiles: `testwiki.near/widget/WikiOnSocialDB_TooltipProfiles`, - navBarImg: "mob.near/widget/Image", }, - }, - libs: { - libSBT: `communityvoice.ndctools.near/widget/lib.SBT`, - libComment: `communityvoice.ndctools.near/widget/lib.comment`, - libArticle: `communityvoice.ndctools.near/widget/lib.article`, - libReactions: `communityvoice.ndctools.near/widget/lib.reactions`, - libUpVotes: `communityvoice.ndctools.near/widget/lib.upVotes`, - libNotifications: `communityvoice.ndctools.near/widget/lib.notifications`, - }, -}; + libs: { + libSBT: `chatter.cheddar.near/widget/lib.SBT`, + libComment: `chatter.cheddar.near/widget/lib.comment`, + libArticle: `chatter.cheddar.near/widget/lib.article`, + libReactions: `chatter.cheddar.near/widget/lib.reactions`, + libUpVotes: `chatter.cheddar.near/widget/lib.upVotes`, + libNotifications: `chatter.cheddar.near/widget/lib.notifications`, + }, +} const brand = { - brandName: "Community Voice", - logoHref: - "https://ipfs.near.social/ipfs/bafkreifhkslni6dlocxya35vjft3fefk2am5uzkagmjjzobdjqlhrnbjz4", - logoRemWidth: 12, - logoRemHeight: 4, -}; + brandName: 'Cheddar Chatter', + logoHref: + 'https://ipfs.near.social/ipfs/bafkreifsjardvezou35knapfyrfadz3otmujgvbbrbzfcwkjogc2ky35lu', + logoRemWidth: 12, + logoRemHeight: 4, +} const baseActions = { - commentBaseAction: "sayALotComment", - articlesBaseAction: "sayALotArticle", - upVoteBaseAction: "sayALotUpVote", - reactionBaseAction: "sayALotReaction", -}; + commentBaseAction: 'sayALotComment', + articlesBaseAction: 'sayALotArticle', + upVoteBaseAction: 'sayALotUpVote', + reactionBaseAction: 'sayALotReaction', +} -const kanbanColumns = ["Open", "Claimed", "In Work", "Closed"]; +const kanbanColumns = ['Open', 'Claimed', 'In Work', 'Closed'] -const kanbanRequiredTags = []; -const kanbanExcludedTags = []; +const kanbanRequiredTags = [] +const kanbanExcludedTags = [] return ( - <> - {"${REPL_ACCOUNT}" === "communityvoice.ndctools.near" &&

This is a dev environment

} - - -); + <> + + +) diff --git a/src/lib/Communities.jsx b/src/lib/Communities.jsx index 08c9fdc..fe3f2ea 100644 --- a/src/lib/Communities.jsx +++ b/src/lib/Communities.jsx @@ -1,10 +1,14 @@ -const { camelCaseToUserReadable, isValidUrl } = VM.require("communityvoice.ndctools.near/widget/lib.strings"); -const { generateMetadata, updateMetadata } = VM.require("communityvoice.ndctools.near/widget/lib.metadata"); +const { camelCaseToUserReadable, isValidUrl } = VM.require( + 'chatter.cheddar.near/widget/lib.strings' +) +const { generateMetadata, updateMetadata } = VM.require( + 'chatter.cheddar.near/widget/lib.metadata' +) -const baseAction = "cv_communities"; +const baseAction = 'cv_communities' const testAction = `test_${baseAction}` const prodAction = `dev_${baseAction}` -const version = "0.0.1" +const version = '0.0.1' let isTest = false @@ -12,20 +16,22 @@ function getCommunitiesTypes() { return [ { id: 0, - title: "Public", - description: "Anyone can view, post and comment." + title: 'Public', + description: 'Anyone can view, post and comment.', }, { id: 1, - title: "Restricted", - description: "Anyone can view this community, but only approved members can post." + title: 'Restricted', + description: + 'Anyone can view this community, but only approved members can post.', }, { id: 2, - title: "Private access", - description: "Only approved users can view and post in this community." - } -] + title: 'Private access', + description: + 'Only approved users can view and post in this community.', + }, + ] } function getAction() { @@ -39,31 +45,42 @@ function setIsTest(value) { function validateCommunityData(communityData) { const expectedStringProperties = [ - "name", - "description", - "backgroundImage", - "profileImage" + 'name', + 'description', + 'backgroundImage', + 'profileImage', ] - const expectedUrlProperties = [ - "backgroundImage", - "profileImage" - ] - const isTypeOk = 0 <= communityData.type && communityData.type < getCommunitiesTypes().length + const expectedUrlProperties = ['backgroundImage', 'profileImage'] + const isTypeOk = + 0 <= communityData.type && + communityData.type < getCommunitiesTypes().length const errArrMessage = [] // String properties - errArrMessage.push(...expectedStringProperties.map((currentProperty) => { - const isValidProperty = communityData[currentProperty] - if (!isValidProperty) return `Missing ${camelCaseToUserReadable(currentProperty)}` - return undefined - }).filter((str) => str !== undefined)) + errArrMessage.push( + ...expectedStringProperties + .map((currentProperty) => { + const isValidProperty = communityData[currentProperty] + if (!isValidProperty) + return `Missing ${camelCaseToUserReadable(currentProperty)}` + return undefined + }) + .filter((str) => str !== undefined) + ) // Url properties - errArrMessage.push(...expectedUrlProperties.map((currentProperty) => { - const isValidProperty = isValidUrl(communityData[currentProperty]) - if (!isValidProperty) return `Invalid url for ${camelCaseToUserReadable(currentProperty)}` - return undefined - }).filter((str) => str !== undefined)) + errArrMessage.push( + ...expectedUrlProperties + .map((currentProperty) => { + const isValidProperty = isValidUrl( + communityData[currentProperty] + ) + if (!isValidProperty) + return `Invalid url for ${camelCaseToUserReadable(currentProperty)}` + return undefined + }) + .filter((str) => str !== undefined) + ) if (!isTypeOk) { - errArrMessage.push("Type should be between 0 and 2") + errArrMessage.push('Type should be between 0 and 2') } return errArrMessage } @@ -72,44 +89,44 @@ function composeData(communityData, metadata) { let data = { index: { [getAction()]: JSON.stringify({ - key: "main", + key: 'main', value: { communityData, metadata, }, }), }, - }; + } - return data; + return data } function composeDeleteData(communityData) { let data = { index: { [getAction()]: JSON.stringify({ - key: "main", + key: 'main', value: { communityData: { - id: communityData.id + id: communityData.id, }, - isDelete: true + isDelete: true, }, }), }, - }; + } - return data; + return data } function executeSaveCommunity(communityData, metadata, onCommit, onCancel) { - const newData = composeData(communityData, metadata); + const newData = composeData(communityData, metadata) Social.set(newData, { force: true, onCommit, onCancel, - }); -}; + }) +} function executeDeleteCommunity(communityData, onCommit, onCancel) { const newData = composeDeleteData(communityData) @@ -118,48 +135,52 @@ function executeDeleteCommunity(communityData, onCommit, onCancel) { force: true, onCommit, onCancel, - }); + }) } /** - * - * @param {*} communityData + * + * @param {*} communityData * @param {*} ownerId Context doesn't seem to work on imported widgets - * @param {*} onCommit - * @param {*} onCancel - * @returns + * @param {*} onCommit + * @param {*} onCancel + * @returns */ function createCommunity(communityData, ownerId, onCommit, onCancel) { - const errors = validateCommunityData(communityData); + const errors = validateCommunityData(communityData) if (!ownerId) { - return { error: true, data: ["Owner id not shared"] } + return { error: true, data: ['Owner id not shared'] } } if (errors && errors.length) { return { error: true, data: errors } } if (communityData.id) { - return { error: true, data: ["There is already a community with this id"] } + return { + error: true, + data: ['There is already a community with this id'], + } } communityData.id = `cd/${ownerId}/${Date.now()}` const metadata = generateMetadata() executeSaveCommunity(communityData, metadata, onCommit, onCancel) - const result = "Community created successfully" - return { error: false, data: result }; + const result = 'Community created successfully' + return { error: false, data: result } } /** - * + * * @returns It might return first null and then an empty array and finally an array containing the index structure of communities */ function getCommunities() { const action = getAction() - const communities = Social.index(action, "main", { - order: "desc", - subscribe, - // limit: 10, - }) || [] + const communities = + Social.index(action, 'main', { + order: 'desc', + subscribe, + // limit: 10, + }) || [] return processCommunities(communities) } @@ -180,15 +201,22 @@ function filterValidCommunities(communitiesIndexes) { function filterAccountIdWithCommunityId(communitiesIndexes) { return communitiesIndexes.filter((communityIndex) => { - return communityIndex.value.communityData.id.startsWith(communityIndex.accountId) + return communityIndex.value.communityData.id.startsWith( + communityIndex.accountId + ) }) } function getLatestEdit(communitiesIndexes) { return communitiesIndexes.filter((communityIndex, index) => { - return communitiesIndexes.findIndex((communityIndex2) => { - return communityIndex.value.communityData.id === communityIndex2.value.communityData.id - }) === index + return ( + communitiesIndexes.findIndex((communityIndex2) => { + return ( + communityIndex.value.communityData.id === + communityIndex2.value.communityData.id + ) + }) === index + ) }) } @@ -199,29 +227,35 @@ function removeDeleted(communitiesIndexes) { } function editCommunity(communityIndex, onCommit, onCancel) { - const communityData = communityIndex.value.communityData; - const errors = validateCommunityData(communityData); + const communityData = communityIndex.value.communityData + const errors = validateCommunityData(communityData) if (errors && errors.length) { return { error: true, data: errors } } if (!communityData.id) { - return { error: true, data: ["Community id not provided"] } + return { error: true, data: ['Community id not provided'] } } const metadata = updateMetadata(communityIndex.value.metadata) executeSaveCommunity(communityData, metadata, onCommit, onCancel) - const result = "Community edited successfully" - return { error: false, data: result }; + const result = 'Community edited successfully' + return { error: false, data: result } } function deleteCommunity(communityData, onCommit, onCancel) { if (!communityData.id) { - return { error: true, data: ["Community id not provided"] } + return { error: true, data: ['Community id not provided'] } } executeDeleteCommunity(communityData, onCommit, onCancel) - const result = "Community removed successfully" - return { error: false, data: result }; + const result = 'Community removed successfully' + return { error: false, data: result } } -return { setIsTest, createCommunity, getCommunities, editCommunity, deleteCommunity } \ No newline at end of file +return { + setIsTest, + createCommunity, + getCommunities, + editCommunity, + deleteCommunity, +} diff --git a/src/lib/SBT.jsx b/src/lib/SBT.jsx index cf340d6..72cce1b 100644 --- a/src/lib/SBT.jsx +++ b/src/lib/SBT.jsx @@ -1,75 +1,81 @@ const testnetSBTWhitelist = [ - { - value: "fractal-v2.i-am-human.testnet - class 1", - title: "Fractal", - default: true, - }, - { - value: "community-v2.i-am-human.testnet - class 1", - title: "Community", - }, + { + value: 'fractal-v2.i-am-human.testnet - class 1', + title: 'Fractal', + default: true, + }, + { + value: 'community-v2.i-am-human.testnet - class 1', + title: 'Community', + }, ] const mainnetSBTWhitelist = [ - { - value: "fractal.i-am-human.near - class 1", - title: "General", - default: true, - }, - { value: "community.i-am-human.near - class 1", title: "OG" }, - { value: "community.i-am-human.near - class 2", title: "Contributor" }, - { - value: "community.i-am-human.near - class 3", - title: "Core Contributor", - }, - { value: "elections.ndc-gwg.near - class 2", title: "HoM" }, - { value: "elections.ndc-gwg.near - class 3", title: "CoA" }, - { value: "elections.ndc-gwg.near - class 4", title: "TC" }, - { value: "public", title: "Public" }, + { + value: 'fractal.i-am-human.near - class 1', + title: 'General', + default: true, + }, + { value: 'community.i-am-human.near - class 1', title: 'OG' }, + { value: 'community.i-am-human.near - class 2', title: 'Contributor' }, + { + value: 'community.i-am-human.near - class 3', + title: 'Core Contributor', + }, + { value: 'elections.ndc-gwg.near - class 2', title: 'HoM' }, + { value: 'elections.ndc-gwg.near - class 3', title: 'CoA' }, + { value: 'elections.ndc-gwg.near - class 4', title: 'TC' }, + { value: 'public', title: 'Public' }, ] const getRegistryContract = (config) => { - return config.networkId === "mainnet" - ? "registry.i-am-human.near" - : "registry-v2.i-am-human.testnet"; + return config.networkId === 'mainnet' + ? 'registry.i-am-human.near' + : 'registry-v2.i-am-human.testnet' } - + function getSBTWhiteList(config) { - return config.networkId === "mainnet" ? mainnetSBTWhitelist : testnetSBTWhitelist; + return config.networkId === 'mainnet' + ? mainnetSBTWhitelist + : testnetSBTWhitelist } function isValidUser(accountId, config) { - const userSBTs = getUserSBTs(accountId,config).then(userSBTs => { - const SBTWhiteList = getSBTWhiteList(config) - - const isValid = SBTWhiteList.some(sbt => { - const data = sbt.value.split(" - class "); - const sbtsData = { name: data[0], classNumber: Number(data[1]) }; - - const SBTsHaveMatched = userSBTs.some((userSbt) => { - return ( - userSbt[0] === sbtsData.name && - userSbt[1].find( - (sbtExtraData) => - sbtExtraData.metadata.class === sbtsData.classNumber - ) - ); - }); - - return SBTsHaveMatched - }) + const userSBTs = getUserSBTs(accountId, config).then((userSBTs) => { + const SBTWhiteList = getSBTWhiteList(config) + + const isValid = SBTWhiteList.some((sbt) => { + const data = sbt.value.split(' - class ') + const sbtsData = { name: data[0], classNumber: Number(data[1]) } + + const SBTsHaveMatched = userSBTs.some((userSbt) => { + return ( + userSbt[0] === sbtsData.name && + userSbt[1].find( + (sbtExtraData) => + sbtExtraData.metadata.class === sbtsData.classNumber + ) + ) + }) - return isValid - }); + return SBTsHaveMatched + }) - return userSBTs + return isValid + }) + + return userSBTs } - + function getUserSBTs(accountId, config) { - const userSBTsPromise = Near.asyncView(getRegistryContract(config), "sbt_tokens_by_owner", { - account: accountId, - }); + const userSBTsPromise = Near.asyncView( + getRegistryContract(config), + 'sbt_tokens_by_owner', + { + account: accountId, + } + ) - return userSBTsPromise; + return userSBTsPromise } -return { isValidUser, getUserSBTs } \ No newline at end of file +return { isValidUser, getUserSBTs } diff --git a/src/lib/article.jsx b/src/lib/article.jsx index 55ce479..9e0b55c 100644 --- a/src/lib/article.jsx +++ b/src/lib/article.jsx @@ -1,392 +1,407 @@ -const { getUserSBTs, getSBTWhiteList } = VM.require("communityvoice.ndctools.near/widget/lib.SBT"); +const { getUserSBTs, getSBTWhiteList } = VM.require( + 'communityvoice.ndctools.near/widget/lib.SBT' +) const { generateMetadata, updateMetadata, buildDeleteMetadata } = VM.require( - "communityvoice.ndctools.near/widget/lib.metadata" -); + 'communityvoice.ndctools.near/widget/lib.metadata' +) const { normalizeObjectWithMetadata } = VM.require( - "communityvoice.ndctools.near/widget/lib.normalization" -); -const { camelCaseToUserReadable } = VM.require("communityvoice.ndctools.near/widget/lib.strings"); + 'communityvoice.ndctools.near/widget/lib.normalization' +) +const { camelCaseToUserReadable } = VM.require( + 'communityvoice.ndctools.near/widget/lib.strings' +) const { extractMentions, getNotificationData } = VM.require( - "communityvoice.ndctools.near/widget/lib.notifications" -); + 'communityvoice.ndctools.near/widget/lib.notifications' +) -const currentVersion = "v0.0.5"; +const currentVersion = 'v0.0.1' -let config = {}; +let config = {} function setConfig(value) { - config = value; + config = value } function getConfig() { - return config; + return config } function getAction(version) { - const baseAction = getConfig().baseActions.article; - const versionData = version ? versions[version] : versions[currentVersion]; - const action = baseAction + versionData.actionSuffix; - return getConfig().isTest ? `test_${action}` : action; + const baseAction = getConfig().baseActions.article + const versionData = version ? versions[version] : versions[currentVersion] + const action = baseAction + versionData.actionSuffix + return getConfig().isTest ? `test_${action}` : action } function setIsTest(value) { - isTest = value; + isTest = value } function getArticlesVersions(config, articleId) { - setConfig(config); - return getArticlesHistoryNormalized(articleId) + setConfig(config) + return getArticlesHistoryNormalized(articleId) } - function getArticles(config, filters) { - setConfig(config); - return getArticlesNormalized(filters); + setConfig(config) + return getArticlesNormalized(filters) } function filterFakeAuthors(articleData, articleIndexData) { - if (articleData.author === articleIndexData.accountId) { - return articleData; - } + if (articleData.author === articleIndexData.accountId) { + return articleData + } } function getArticleNormalized(articleIndex, action) { - const articleVersionIndex = Object.keys(versions).findIndex((versionName) => { - const versionData = versions[versionName]; - return ( - (versionData.validBlockHeightRange[0] <= articleIndex.blockHeight && - articleIndex.blockHeight < versionData.validBlockHeightRange[1]) || - versionData.validBlockHeightRange[1] === undefined - ); - }); - - const articleVersionKey = Object.keys(versions)[articleVersionIndex]; - // const action = versions[articleVersionKey].action - const key = "main"; - - return asyncFetch(" https://api.near.social/get", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - keys: [`${articleIndex.accountId}/${action}/${key}`], - blockHeight: articleIndex.blockHeight, - }), - }).then((response) => { - //ERROR: Check the structure of the response to define "article" - let article = JSON.parse( - response.body[articleIndex.accountId][action][key] - ); - article.blockHeight = articleIndex.blockHeight; - article.articleIndex = articleIndex; - Object.keys(versions).forEach((versionName, index) => { - if (articleVersionIndex >= index) { - const versionData = versions[versionName]; - article = versionData.normalizationFunction(article); - } - }); - return article; - }); + const articleVersionIndex = Object.keys(versions).findIndex( + (versionName) => { + const versionData = versions[versionName] + return ( + (versionData.validBlockHeightRange[0] <= + articleIndex.blockHeight && + articleIndex.blockHeight < + versionData.validBlockHeightRange[1]) || + versionData.validBlockHeightRange[1] === undefined + ) + } + ) + + const articleVersionKey = Object.keys(versions)[articleVersionIndex] + // const action = versions[articleVersionKey].action + const key = 'main' + + return asyncFetch(' https://api.near.social/get', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + keys: [`${articleIndex.accountId}/${action}/${key}`], + blockHeight: articleIndex.blockHeight, + }), + }).then((response) => { + //ERROR: Check the structure of the response to define "article" + let article = JSON.parse( + response.body[articleIndex.accountId][action][key] + ) + article.blockHeight = articleIndex.blockHeight + article.articleIndex = articleIndex + Object.keys(versions).forEach((versionName, index) => { + if (articleVersionIndex >= index) { + const versionData = versions[versionName] + article = versionData.normalizationFunction(article) + } + }) + return article + }) } function normalizeArticleData(articleData) { - return normalizeObjectWithMetadata(articleData, versions); + return normalizeObjectWithMetadata(articleData, versions) } function getArticleBlackListByBlockHeight() { - return [ - 91092435, 91092174, 91051228, 91092223, 91051203, 98372095, 96414482, - 96412953, 103131250, 106941548, 103053147, 102530777, - ]; + return [ + 91092435, 91092174, 91051228, 91092223, 91051203, 98372095, 96414482, + 96412953, 103131250, 106941548, 103053147, 102530777, + ] } function getArticleBlackListByArticleId() { - return [ - "blaze.near-1690410074090", - "blaze.near-1690409577184", - "blaze.near-1690803928696", - "blaze.near-1690803872147", - "blaze.near-1690574978421", - "f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691703303485", - "f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691702619510", - "f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691702487944", - "f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691707918243", - "f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691707889297", - "blaze.near-1697211386373", - "silkking.near-1696797896796", - "silkking.near-1696797784589", - "silkking.near-1696797350661", - "silkking.near-1696797276482", - "silkking.near-1696797155012", - "silkking.near-1696796793605", - "silkking.near-1696796543546", - "silkking.near-1696795954175", - "silkking.near-1696794571874", - "silkking.near-1696792789177", - "zarmade.near-1690578803015", - ]; + return [ + 'blaze.near-1690410074090', + 'blaze.near-1690409577184', + 'blaze.near-1690803928696', + 'blaze.near-1690803872147', + 'blaze.near-1690574978421', + 'f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691703303485', + 'f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691702619510', + 'f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691702487944', + 'f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691707918243', + 'f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691707889297', + 'blaze.near-1697211386373', + 'silkking.near-1696797896796', + 'silkking.near-1696797784589', + 'silkking.near-1696797350661', + 'silkking.near-1696797276482', + 'silkking.near-1696797155012', + 'silkking.near-1696796793605', + 'silkking.near-1696796543546', + 'silkking.near-1696795954175', + 'silkking.near-1696794571874', + 'silkking.near-1696792789177', + 'zarmade.near-1690578803015', + ] } function filterInvalidArticlesIndexes(articlesData) { - return articlesData - .filter((articleData) => articleData.value.metadata.id) // Has id - .filter((articleData) => { - const splittedId = articleData.value.metadata.id.split("-"); - splittedId.pop(); - - return splittedId.join("-") === articleData.accountId; - }) // id begins with same accountId as index object - .filter( - (articleData) => - !getArticleBlackListByBlockHeight().includes(articleData.blockHeight) // Blockheight is not in blacklist - ) - .filter( - (articleData) => - !getArticleBlackListByArticleId().includes(articleData.value.id) // Article id is not in blacklist - ); + return articlesData + .filter((articleData) => articleData.value.metadata.id) // Has id + .filter((articleData) => { + const splittedId = articleData.value.metadata.id.split('-') + splittedId.pop() + + return splittedId.join('-') === articleData.accountId + }) // id begins with same accountId as index object + .filter( + (articleData) => + !getArticleBlackListByBlockHeight().includes( + articleData.blockHeight + ) // Blockheight is not in blacklist + ) + .filter( + (articleData) => + !getArticleBlackListByArticleId().includes(articleData.value.id) // Article id is not in blacklist + ) } function getLatestEdits(articles) { - return articles.filter((articleData, index) => { - const latestEditForThisArticleIndex = articles.findIndex( - (newArticle) => - newArticle.value.metadata.id === articleData.value.metadata.id - ); - return index === latestEditForThisArticleIndex; - }); + return articles.filter((articleData, index) => { + const latestEditForThisArticleIndex = articles.findIndex( + (newArticle) => + newArticle.value.metadata.id === articleData.value.metadata.id + ) + return index === latestEditForThisArticleIndex + }) } function applyUserFilters(articles, filters) { - const { id, category, authors, tags } = filters; - if (id) { - articles = articles.filter((article) => { - return article.value.metadata.id === id; - }); - } - if (category && category !== "all_categories") { - articles = articles.filter((article) => { - return article.value.articleData.category === category; - }); - } - if(authors && authors.length > 0) { - articles = articles.filter((article) => { - return authors.includes(article.value.metadata.author); - }); - } - if (tags && tags.length > 0) { - articles = articles.filter((article) => { - return tags.some((tag) => article.value.articleData.tags.includes(tag)); - }); - } - return articles; + const { id, category, authors, tags } = filters + if (id) { + articles = articles.filter((article) => { + return article.value.metadata.id === id + }) + } + if (category && category !== 'all_categories') { + articles = articles.filter((article) => { + return article.value.articleData.category === category + }) + } + if (authors && authors.length > 0) { + articles = articles.filter((article) => { + return authors.includes(article.value.metadata.author) + }) + } + if (tags && tags.length > 0) { + articles = articles.filter((article) => { + return tags.some((tag) => + article.value.articleData.tags.includes(tag) + ) + }) + } + return articles } function isActive(article) { - return article.value.metadata && !article.value.metadata.isDelete; + return article.value.metadata && !article.value.metadata.isDelete } function getArticlesHistoryNormalized(articleId) { - const articlesDataPromises = Object.keys(versions).map((version) => { - // const action = versions[version].action; - const action = getAction(version); - const articles = getArticlesIndexes(action, "main"); + const articlesDataPromises = Object.keys(versions).map((version) => { + // const action = versions[version].action; + const action = getAction(version) + const articles = getArticlesIndexes(action, 'main') - return articles; - }); + return articles + }) - return Promise.all(articlesDataPromises).then((articlesVersionArray) => { - const articles = articlesVersionArray.flat(); - const filteredArticles = applyUserFilters(articles, {id: articleId}); + return Promise.all(articlesDataPromises).then((articlesVersionArray) => { + const articles = articlesVersionArray.flat() + const filteredArticles = applyUserFilters(articles, { id: articleId }) - return filteredArticles; - }); + return filteredArticles + }) } function normalizeArticle(article) { - const versionsKeys = Object.keys(versions); - const versionIndex = article.versionIndex - - for (let i = versionIndex; i < versionsKeys.length; i++) { - const version = versions[versionsKeys[i]]; - article = version.normalizationFunction(article); - } - delete article.versionIndex - - return article; -} + const versionsKeys = Object.keys(versions) + const versionIndex = article.versionIndex -function getArticlesNormalized(userFilters) { - const articlesDataPromises = Object.keys(versions).map((version, versionIndex) => { - // const action = versions[version].action; - const action = getAction(version); - const articles = getArticlesIndexes(action, "main").then((articles) => { - return articles.map((article) => { - article.versionIndex = versionIndex - return article - }); - }); + for (let i = versionIndex; i < versionsKeys.length; i++) { + const version = versions[versionsKeys[i]] + article = version.normalizationFunction(article) + } + delete article.versionIndex - return articles; - }); + return article +} - return Promise.all(articlesDataPromises).then((articlesVersionArray) => { - const articles = articlesVersionArray.flat(); - const normalizedArticles = articles.map((article) => - normalizeArticle(article) - ); - const latestActiveEdits = getLatestEdits(normalizedArticles); - const activeArticles = latestActiveEdits.filter(isActive); - const filteredArticles = applyUserFilters(activeArticles, userFilters); +function getArticlesNormalized(userFilters) { + const articlesDataPromises = Object.keys(versions).map( + (version, versionIndex) => { + // const action = versions[version].action; + const action = getAction(version) + const articles = getArticlesIndexes(action, 'main').then( + (articles) => { + return articles.map((article) => { + article.versionIndex = versionIndex + return article + }) + } + ) + + return articles + } + ) + return Promise.all(articlesDataPromises).then((articlesVersionArray) => { + const articles = articlesVersionArray.flat() + const normalizedArticles = articles.map((article) => + normalizeArticle(article) + ) + const latestActiveEdits = getLatestEdits(normalizedArticles) + const activeArticles = latestActiveEdits.filter(isActive) + const filteredArticles = applyUserFilters(activeArticles, userFilters) - return filteredArticles; - }); + return filteredArticles + }) } function getArticlesIndexes(action, key) { - const indexUrl = "https://api.near.social/index"; - const articlesPromise = asyncFetch(indexUrl, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - action, - key, - options: { - order: "desc", - }, - }), - }).then((response) => response.body); - - return articlesPromise; + const indexUrl = 'https://api.near.social/index' + const articlesPromise = asyncFetch(indexUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action, + key, + options: { + order: 'desc', + }, + }), + }).then((response) => response.body) + + return articlesPromise } function normalizeOldToV_0_0_1(article) { - article.realArticleId = `${article.author}-${article.timeCreate}`; - article.sbts = ["public"]; + article.realArticleId = `${article.author}-${article.timeCreate}` + article.sbts = ['public'] - return article; + return article } function normalizeFromV0_0_1ToV0_0_2(article) { - article.title = article.articleId; - article.id = article.realArticleId; - if (article.sbts[0] !== "public") { - article.sbts[0] = article.sbts[0] + " - class 1"; - } // There is only one article that is not public and only has class 1 + article.title = article.articleId + article.id = article.realArticleId + if (article.sbts[0] !== 'public') { + article.sbts[0] = article.sbts[0] + ' - class 1' + } // There is only one article that is not public and only has class 1 - delete article.articleId; - delete article.realArticleId; + delete article.articleId + delete article.realArticleId - return article; + return article } function normalizeFromV0_0_2ToV0_0_3(article) { - if (!Array.isArray(article.tags) && typeof article.tags === "object") { - article.tags = Object.keys(article.tags); - } + if (!Array.isArray(article.tags) && typeof article.tags === 'object') { + article.tags = Object.keys(article.tags) + } - if (article.tags) { - article.tags = article.tags.filter( - (tag) => tag !== undefined && tag !== null - ); - } else { - article.tags = []; - } + if (article.tags) { + article.tags = article.tags.filter( + (tag) => tag !== undefined && tag !== null + ) + } else { + article.tags = [] + } - //Add day-month-year tag if it doesn't exists yet by request - const creationDate = new Date(article.timeCreate); + //Add day-month-year tag if it doesn't exists yet by request + const creationDate = new Date(article.timeCreate) - const dateTag = `${creationDate.getDate()}-${ - creationDate.getMonth() + 1 - }-${creationDate.getFullYear()}`; + const dateTag = `${creationDate.getDate()}-${ + creationDate.getMonth() + 1 + }-${creationDate.getFullYear()}` - if (!article.tags.includes(dateTag)) article.tags.push(dateTag); + if (!article.tags.includes(dateTag)) article.tags.push(dateTag) - if (article.blockHeight < 105654020 && article.sbts.includes("public")) { - article.sbts = ["fractal.i-am-human.near - class 1"]; - } + if (article.blockHeight < 105654020 && article.sbts.includes('public')) { + article.sbts = ['fractal.i-am-human.near - class 1'] + } - return article; + return article } function normalizeFromV0_0_4ToV0_0_5(article) { - if(!isActive(article)) return article - article.value.articleData.category = "uncategorized" - return article; + if (!isActive(article)) return article + article.value.articleData.category = 'uncategorized' + return article } -function normalizeFromV0_0_5ToV0_0_6(article) { // change to normalizeFromV0_0_5ToV0_0_6 - return article; +function normalizeFromV0_0_5ToV0_0_6(article) { + // change to normalizeFromV0_0_5ToV0_0_6 + return article } // EDIT: set versions you want to handle, considering their action to Social.index and the way to transform to one version to another (normalization) const versions = { - // old: { - // normalizationFunction: normalizeOldToV_0_0_1, - // actionSuffix: "", - // validBlockHeightRange: [0, 102530777], - // }, - // "v0.0.1": { - // normalizationFunction: normalizeFromV0_0_1ToV0_0_2, - // actionSuffix: `_v0.0.1`, - // validBlockHeightRange: [102530777, 103053147], - // }, - // "v0.0.2": { - // normalizationFunction: normalizeFromV0_0_2ToV0_0_3, - // actionSuffix: `_v0.0.2`, - // validBlockHeightRange: [103053147, Infinity], - // }, - // "v0.0.3": { - // normalizationFunction: normalizeFromV0_0_3ToV0_0_4, - // actionSuffix: `_v0.0.3`, - // validBlockHeightRange: [Infinity, Infinity], - // }, - "v0.0.4": { - normalizationFunction: normalizeFromV0_0_4ToV0_0_5, - actionSuffix: `_v0.0.4`, - validBlockHeightRange: [0, 115688831], - }, - "v0.0.5": { - normalizationFunction: normalizeFromV0_0_5ToV0_0_6, - actionSuffix: `_v0.0.5`, - validBlockHeightRange: [115688831, Infinity], - }, -}; + // old: { + // normalizationFunction: normalizeOldToV_0_0_1, + // actionSuffix: "", + // validBlockHeightRange: [0, 102530777], + // }, + 'v0.0.1': { + normalizationFunction: (article) => article, + actionSuffix: `_v0.0.1`, + validBlockHeightRange: [0, Infinity], + }, + // "v0.0.2": { + // normalizationFunction: normalizeFromV0_0_2ToV0_0_3, + // actionSuffix: `_v0.0.2`, + // validBlockHeightRange: [103053147, Infinity], + // }, + // "v0.0.3": { + // normalizationFunction: normalizeFromV0_0_3ToV0_0_4, + // actionSuffix: `_v0.0.3`, + // validBlockHeightRange: [Infinity, Infinity], + // }, + 'v0.0.4': { + normalizationFunction: normalizeFromV0_0_4ToV0_0_5, + actionSuffix: `_v0.0.4`, + validBlockHeightRange: [0, 115688831], + }, + 'v0.0.5': { + normalizationFunction: normalizeFromV0_0_5ToV0_0_6, + actionSuffix: `_v0.0.5`, + validBlockHeightRange: [115688831, Infinity], + }, +} function validateArticleData(article) { - const expectedStringProperties = ["title", "body"]; - const expectedArrayProperties = ["tags"]; - const errArrMessage = []; - // String properties - errArrMessage.push( - ...expectedStringProperties - .filter((currentProperty) => { - const isValidProperty = - !article[currentProperty] || - typeof article[currentProperty] !== "string"; - return isValidProperty; - }) - .map( - (currentProperty) => - `Missing ${camelCaseToUserReadable(currentProperty)} or not a string` - ) - ); - // Array properties - errArrMessage.push( - ...expectedArrayProperties - .filter((currentProperty) => { - return !Array.isArray(article[currentProperty]); - }) - .map( - (currentProperty) => - `Article ${camelCaseToUserReadable( - currentProperty - )}'s is not an array` - ) - ); - - return errArrMessage; + const expectedStringProperties = ['title', 'body'] + const expectedArrayProperties = ['tags'] + const errArrMessage = [] + // String properties + errArrMessage.push( + ...expectedStringProperties + .filter((currentProperty) => { + const isValidProperty = + !article[currentProperty] || + typeof article[currentProperty] !== 'string' + return isValidProperty + }) + .map( + (currentProperty) => + `Missing ${camelCaseToUserReadable(currentProperty)} or not a string` + ) + ) + // Array properties + errArrMessage.push( + ...expectedArrayProperties + .filter((currentProperty) => { + return !Array.isArray(article[currentProperty]) + }) + .map( + (currentProperty) => + `Article ${camelCaseToUserReadable( + currentProperty + )}'s is not an array` + ) + ) + + return errArrMessage } /** @@ -394,189 +409,193 @@ function validateArticleData(article) { * @param {*} metadata */ function validateMetadata(metadata) { - const expectedStringProperties = ["id", "author"]; - const expectedNumberProperties = ["createdTimestamp"]; - - const errArrMessage = []; - // String properties - errArrMessage.push( - ...expectedStringProperties - .filter((currentProperty) => { - const isValidProperty = - !metadata[currentProperty] || - typeof metadata[currentProperty] !== "string"; - return isValidProperty; - }) - .map( - (currentProperty) => - `Missing ${camelCaseToUserReadable(currentProperty)} or not a string` - ) - ); - // Array properties - errArrMessage.push( - ...expectedNumberProperties - .filter((currentProperty) => { - return ( - !metadata[currentProperty] || - typeof metadata[currentProperty] !== "number" - ); - }) - .map( - (currentProperty) => - `Property ${camelCaseToUserReadable( - currentProperty - )}'s is not an array` - ) - ); - - return errArrMessage; + const expectedStringProperties = ['id', 'author'] + const expectedNumberProperties = ['createdTimestamp'] + + const errArrMessage = [] + // String properties + errArrMessage.push( + ...expectedStringProperties + .filter((currentProperty) => { + const isValidProperty = + !metadata[currentProperty] || + typeof metadata[currentProperty] !== 'string' + return isValidProperty + }) + .map( + (currentProperty) => + `Missing ${camelCaseToUserReadable(currentProperty)} or not a string` + ) + ) + // Array properties + errArrMessage.push( + ...expectedNumberProperties + .filter((currentProperty) => { + return ( + !metadata[currentProperty] || + typeof metadata[currentProperty] !== 'number' + ) + }) + .map( + (currentProperty) => + `Property ${camelCaseToUserReadable( + currentProperty + )}'s is not an array` + ) + ) + + return errArrMessage } function validateNewArticle(articleData) { - const errorArray = validateArticleData(articleData); - return errorArray; + const errorArray = validateArticleData(articleData) + return errorArray } function validateEditArticle(articleData, previousMetadata) { - const errorArray = validateArticleData(articleData); - if (!previousMetadata.id) { - errorArray.push(`Trying to edit article with no article id`); - } - return errorArray; + const errorArray = validateArticleData(articleData) + if (!previousMetadata.id) { + errorArray.push(`Trying to edit article with no article id`) + } + return errorArray } function composeData(article) { - let data = { - index: { - [getAction()]: JSON.stringify({ - key: "main", - value: { - ...article, + let data = { + index: { + [getAction()]: JSON.stringify({ + key: 'main', + value: { + ...article, + }, + }), }, - }), - }, - }; + } - if(article.metadata.isDelete) return data + if (article.metadata.isDelete) return data - const mentions = extractMentions(article.articleData.body); + const mentions = extractMentions(article.articleData.body) - if (mentions.length > 0) { - const dataToAdd = getNotificationData( - getConfig(), - "mention", - mentions, - article.metadata - ); + if (mentions.length > 0) { + const dataToAdd = getNotificationData( + getConfig(), + 'mention', + mentions, + article.metadata + ) - data.post = dataToAdd.post; - data.index.notify = dataToAdd.index.notify; - } + data.post = dataToAdd.post + data.index.notify = dataToAdd.index.notify + } - return data; + return data } function composeDeleteData(articleId) { - const deleteMetadata = buildDeleteMetadata(articleId); - let data = { - index: { - [getAction()]: JSON.stringify({ - key: "main", - value: { - metadata: { - ...deleteMetadata, - }, + const deleteMetadata = buildDeleteMetadata(articleId) + let data = { + index: { + [getAction()]: JSON.stringify({ + key: 'main', + value: { + metadata: { + ...deleteMetadata, + }, + }, + }), }, - }), - }, - }; - return data; + } + return data } function executeSaveArticle(article, onCommit, onCancel) { - const newData = composeData(article); - Social.set(newData, { - force: true, - onCommit, - onCancel, - }); -} - -function buildArticle(articleData, userMetadataHelper){ - const metadataHelper = { - ...userMetadataHelper, - idPrefix: "article", - versionKey: currentVersion, - }; - const metadata = generateMetadata(metadataHelper); - return { - articleData, - metadata, - }; + const newData = composeData(article) + Social.set(newData, { + force: true, + onCommit, + onCancel, + }) +} + +function buildArticle(articleData, userMetadataHelper) { + const metadataHelper = { + ...userMetadataHelper, + idPrefix: 'article', + versionKey: currentVersion, + } + const metadata = generateMetadata(metadataHelper) + return { + articleData, + metadata, + } } function createArticle( - config, - articleData, - userMetadataHelper, - onCommit, - onCancel + config, + articleData, + userMetadataHelper, + onCommit, + onCancel ) { - setConfig(config); - const errors = validateNewArticle(articleData); - if (errors && errors.length) { - return { error: true, data: errors }; - } - - const article = buildArticle(articleData,userMetadataHelper) - const result = executeSaveArticle(article, () => onCommit(article.metadata.id), onCancel); - return { error: false, data: result }; + setConfig(config) + const errors = validateNewArticle(articleData) + if (errors && errors.length) { + return { error: true, data: errors } + } + + const article = buildArticle(articleData, userMetadataHelper) + const result = executeSaveArticle( + article, + () => onCommit(article.metadata.id), + onCancel + ) + return { error: false, data: result } } function editArticle( - config, - newArticleData, - previousMetadata, - onCommit, - onCancel + config, + newArticleData, + previousMetadata, + onCommit, + onCancel ) { - setConfig(config); - const errors = validateEditArticle(newArticleData, previousMetadata); - if (errors && errors.length) { - return { error: true, data: errors }; - } + setConfig(config) + const errors = validateEditArticle(newArticleData, previousMetadata) + if (errors && errors.length) { + return { error: true, data: errors } + } - const newMetadata = updateMetadata(previousMetadata, currentVersion); + const newMetadata = updateMetadata(previousMetadata, currentVersion) - const article = { - articleData: newArticleData, - metadata: newMetadata, - }; - const result = executeSaveArticle(article, onCommit, onCancel); - return { error: false, data: result }; + const article = { + articleData: newArticleData, + metadata: newMetadata, + } + const result = executeSaveArticle(article, onCommit, onCancel) + return { error: false, data: result } } function deleteArticle(config, articleId, onCommit, onCancel) { - setConfig(config); + setConfig(config) - const deleteMetadata = buildDeleteMetadata(articleId); - const article = { - metadata: deleteMetadata, - }; - executeSaveArticle(article, onCommit, onCancel); + const deleteMetadata = buildDeleteMetadata(articleId) + const article = { + metadata: deleteMetadata, + } + executeSaveArticle(article, onCommit, onCancel) } return { - createArticle, - getArticles, - buildArticle, - editArticle, - deleteArticle, - getArticlesIndexes, - getLatestEdits, - getAction, - filterFakeAuthors, - getArticleBlackListByArticleId, - getArticleBlackListByBlockHeight, - getArticlesVersions, -}; + createArticle, + getArticles, + buildArticle, + editArticle, + deleteArticle, + getArticlesIndexes, + getLatestEdits, + getAction, + filterFakeAuthors, + getArticleBlackListByArticleId, + getArticleBlackListByBlockHeight, + getArticlesVersions, +} diff --git a/src/lib/categories.jsx b/src/lib/categories.jsx index 8c79684..55dbb65 100644 --- a/src/lib/categories.jsx +++ b/src/lib/categories.jsx @@ -1,77 +1,31 @@ function getCategories() { return [ { - title: "All categories", - color: "transparent", - value: "all_categories", + title: 'All categories', + color: 'transparent', + value: 'all_categories', }, { - title: "Uncategorized", - color: "#b3b5b4", - value: "uncategorized", + title: 'Chatter', + color: '#f7941d', + value: 'chatter', }, { - title: "Creatives", - color: "#f7941d", - value: "creatives", + title: 'Voting', + color: '#92278f', + value: 'voting', }, { - title: "Community", - color: "#92278f", - value: "community", + title: 'Games', + color: '#08c', + value: 'games', }, { - title: "Open Web Sandbox", - color: "#08c", - value: "open_web_sandbox", - }, - { - title: "Education", - color: "#9eb83b", - value: "education", - }, - { - title: "Ecosystem", - color: "#bf1e2e", - value: "ecosystem", - }, - { - title: "Development", - color: "#0e76bd", - value: "development", - }, - { - title: "Marketing", - color: "#f1592a", - value: "marketing", - }, - { - title: "Regional hubs", - color: "#a461ef", - value: "regional_hubs", - }, - { - title: "NEAR Digital Collective", - color: "#652d90", - value: "near_digital_collective", - }, - { - title: "NEAR Gaming", - color: "#652d90", - value: "near_gaming", - }, - { - title: "Staking", - color: "#231f20", - value: "staking", - }, - { - title: "Potlock", - color: "#ed207b", - value: "potlock", + title: 'Contests', + color: '#9eb83b', + value: 'contests', }, ] - } -return { getCategories } \ No newline at end of file +return { getCategories } diff --git a/src/lib/comment.jsx b/src/lib/comment.jsx index a508fcf..03f0766 100644 --- a/src/lib/comment.jsx +++ b/src/lib/comment.jsx @@ -1,393 +1,412 @@ -const { getFromIndex } = VM.require("communityvoice.ndctools.near/widget/lib.socialDbIndex"); +const promise = new Promise((resolve, reject) => { + setTimeout(() => { + resolve([]) + }, 1) +}) + +const { getFromIndex } = VM.require( + 'chatter.cheddar.near/widget/lib.socialDbIndex' +) || { getFromIndex: () => promise } const { normalize, normalizeId } = VM.require( - "communityvoice.ndctools.near/widget/lib.normalization" -); + 'chatter.cheddar.near/widget/lib.normalization' +) || { normalize: () => {}, normalizeId: () => {} } const { generateMetadata, updateMetadata, buildDeleteMetadata } = VM.require( - "communityvoice.ndctools.near/widget/lib.metadata" -); + 'chatter.cheddar.near/widget/lib.metadata' +) || { + generateMetadata: () => {}, + updateMetadata: () => {}, + buildDeleteMetadata: () => {}, +} const { extractMentions, getNotificationData } = VM.require( - "communityvoice.ndctools.near/widget/lib.notifications" -); + 'chatter.cheddar.near/widget/lib.notifications' +) || { extractMentions: () => {}, getNotificationData: () => {} } -let config = {}; +let config = {} -const currentVersion = "v0.0.4"; +const currentVersion = 'v0.0.1' function getSplittedCommentIdV0_0_3(commentId) { - const commentIdWithoutPrefix = commentId.slice(2); - const prefix = "c-"; + const commentIdWithoutPrefix = commentId.slice(2) + const prefix = 'c-' - const oldFormatID = prefix + commentIdWithoutPrefix; + const oldFormatID = prefix + commentIdWithoutPrefix - const newCommentID = normalizeId(oldFormatID, "comment"); + const newCommentID = normalizeId(oldFormatID, 'comment') - const splitCommentId = newCommentID.split("/"); + const splitCommentId = (newCommentID ?? '').split('/') - return splitCommentId; - // } + return splitCommentId + // } } function normalizeOldToV_0_0_1(comment) { - return comment; + return comment } function normalizeFromV0_0_1ToV0_0_2(comment) { - return comment; + return comment } function normalizeFromV0_0_2ToV0_0_3(comment) { - comment.value.comment.rootId = comment.value.comment.originalCommentId; - delete comment.value.comment.originalCommentId; - delete comment.value.comment.id; + comment.value.comment.rootId = comment.value.comment.originalCommentId + delete comment.value.comment.originalCommentId + delete comment.value.comment.id - return comment; + return comment } function normalizeFromV0_0_3ToV0_0_4(comment) { - const now = Date.now(); - - // const splitCommentId = getSplittedCommentIdV0_0_3(comment.value.metadata.id); - const splitCommentId = getSplittedCommentIdV0_0_3( - comment.value.comment.commentId - ); - - comment.value.commentData = {text: comment.value.comment.text}; - const author = splitCommentId[1]; - comment.value.metadata = { - id: splitCommentId.join("/"), - author, - createdTimestamp: now, - lastEditTimestamp: now, - rootId: comment.value.comment.rootId, - versionKey: "v0.0.4", - }; - - delete comment.value.comment.commentId; - delete comment.value.comment.rootId; - delete comment.value.comment.timestamp; - delete comment.value.comment.text; - delete comment.value.comment; - delete comment.isEdition; - - return comment; + const now = Date.now() + + // const splitCommentId = getSplittedCommentIdV0_0_3(comment.value.metadata.id); + const splitCommentId = getSplittedCommentIdV0_0_3( + comment.value.comment.commentId + ) + + comment.value.commentData = { text: comment.value.comment.text } + const author = splitCommentId[1] + comment.value.metadata = { + id: splitCommentId.join('/'), + author, + createdTimestamp: now, + lastEditTimestamp: now, + rootId: comment.value.comment.rootId, + versionKey: 'v0.0.4', + } + + delete comment.value.comment.commentId + delete comment.value.comment.rootId + delete comment.value.comment.timestamp + delete comment.value.comment.text + delete comment.value.comment + delete comment.isEdition + + return comment } function normalizeFromV0_0_4ToV0_0_5(comment) { - return comment; + return comment } const versions = { - old: { - normalizationFunction: normalizeOldToV_0_0_1, - suffixAction: "", - }, - "v1.0.1": { - normalizationFunction: normalizeFromV0_0_1ToV0_0_2, - suffixAction: `-v1.0.1`, - }, - "v0.0.2": { - normalizationFunction: normalizeFromV0_0_2ToV0_0_3, - suffixAction: `_v0.0.2`, - }, - "v0.0.3": { - normalizationFunction: normalizeFromV0_0_3ToV0_0_4, - suffixAction: `_v0.0.3`, - }, - "v0.0.4": { - normalizationFunction: normalizeFromV0_0_4ToV0_0_5, - suffixAction: `_v0.0.4`, - }, -}; + // old: { + // normalizationFunction: normalizeOldToV_0_0_1, + // suffixAction: '', + // }, + 'v0.0.1': { + normalizationFunction: (comment) => comment, + suffixAction: `-v0.0.1`, + }, + // 'v0.0.2': { + // normalizationFunction: normalizeFromV0_0_2ToV0_0_3, + // suffixAction: `_v0.0.2`, + // }, + // 'v0.0.3': { + // normalizationFunction: normalizeFromV0_0_3ToV0_0_4, + // suffixAction: `_v0.0.3`, + // }, + // 'v0.0.4': { + // normalizationFunction: normalizeFromV0_0_4ToV0_0_5, + // suffixAction: `_v0.0.4`, + // }, +} function setConfig(newConfig) { - config = newConfig; + config = newConfig } function getConfig() { - return config; + return config } function fillAction(version, config) { - const baseAction = config.baseActions.comment; - const filledAction = baseAction + version.suffixAction; - return config.isTest ? `test_${filledAction}` : filledAction; + const baseAction = config.baseActions.comment + const filledAction = baseAction + version.suffixAction + return config.isTest ? `test_${filledAction}` : filledAction } function getCommentBlackListByBlockHeight() { - return [98588599, 115199907, 115238101]; + return [98588599, 115199907, 115238101] } function filterInvalidComments(comments) { - return comments - .filter( - (comment) => - comment.blockHeight && - !getCommentBlackListByBlockHeight().includes(comment.blockHeight) // Comment is not in blacklist - ) - .filter((comment) => { - return ( - comment.accountId === - getUserNameFromCommentId( - comment.value.metadata.id ?? comment.value.comment.commentId + return (comments ?? []) + .filter( + (comment) => + comment.blockHeight && + !getCommentBlackListByBlockHeight().includes( + comment.blockHeight + ) // Comment is not in blacklist ) - ); - }); + .filter((comment) => { + return ( + comment.accountId === + getUserNameFromCommentId( + comment.value.metadata.id ?? comment.value.comment.commentId + ) + ) + }) } function getUserNameFromCommentId(commentId) { - let userName; - if (commentId.startsWith("c/") || commentId.startsWith("comment/")) { - const splittedCommentId = commentId.split("/"); - userName = splittedCommentId[1]; - } else if (commentId.startsWith("c_")) { - const userNamePlusTimestamp = commentId.split("c_")[1]; + let userName + if (commentId.startsWith('c/') || commentId.startsWith('comment/')) { + const splittedCommentId = commentId.split('/') + userName = splittedCommentId[1] + } else if (commentId.startsWith('c_')) { + const userNamePlusTimestamp = commentId.split('c_')[1] - const splittedUserNamePlusTimestamp = userNamePlusTimestamp.split("-"); + const splittedUserNamePlusTimestamp = userNamePlusTimestamp.split('-') - splittedUserNamePlusTimestamp.pop(); + splittedUserNamePlusTimestamp.pop() - userName = splittedUserNamePlusTimestamp.join("-"); - } + userName = splittedUserNamePlusTimestamp.join('-') + } - return userName; + return userName } function processComments(comments) { - const lastEditionComments = comments.filter((comment) => { - const firstCommentWithThisCommentId = comments.find((compComment) => { - return compComment.value.metadata.id === comment.value.metadata.id; - }); - - return ( - JSON.stringify(firstCommentWithThisCommentId) === JSON.stringify(comment) - ); - }); - - const lastEditionCommentsWithoutDeletedOnes = lastEditionComments.filter( - (comment) => !comment.value.metadata.isDelete - ); - - const lastEditionCommentsWithEditionMark = - lastEditionCommentsWithoutDeletedOnes.map((comment) => { - const commentsWithThisCommentId = comments.filter((compComment) => { - return ( - comment.value.metadata.id === compComment.value.metadata.commentId - ); - }); + const lastEditionComments = (comments ?? []).filter((comment) => { + const firstCommentWithThisCommentId = comments.find((compComment) => { + return compComment.value.metadata.id === comment.value.metadata.id + }) - if (commentsWithThisCommentId.length > 1) { - comment.value.metadata.isEdition = true; - } + return ( + JSON.stringify(firstCommentWithThisCommentId) === + JSON.stringify(comment) + ) + }) - return comment; - }); + const lastEditionCommentsWithoutDeletedOnes = lastEditionComments.filter( + (comment) => !comment.value.metadata.isDelete + ) - return lastEditionCommentsWithEditionMark; + const lastEditionCommentsWithEditionMark = + lastEditionCommentsWithoutDeletedOnes.map((comment) => { + const commentsWithThisCommentId = (comments ?? []).filter( + (compComment) => { + return ( + comment.value.metadata.id === + compComment.value.metadata.commentId + ) + } + ) + + if (commentsWithThisCommentId.length > 1) { + comment.value.metadata.isEdition = true + } + + return comment + }) + + return lastEditionCommentsWithEditionMark } function getComments(articleId, config) { - setConfig(config); - const commentsByVersionPromise = Object.keys(versions).map( - (version, index, arr) => { - const action = fillAction(versions[version], config); + setConfig(config) + const commentsByVersionPromise = Object.keys(versions).map( + (version, index, arr) => { + const action = fillAction(versions[version], config) - return getFromIndex(action, articleId, "asc").then((comments) => { - const validComments = filterInvalidComments(comments); + return getFromIndex(action, articleId, 'asc').then((comments) => { + const validComments = filterInvalidComments(comments) - const normalizedComments = validComments.map((comment) => { - return normalize(comment, versions, index); - }); + const normalizedComments = validComments.map((comment) => { + return normalize(comment, versions, index) + }) - return normalizedComments; - }); - } - ); + return normalizedComments + }) + } + ) - return Promise.all(commentsByVersionPromise).then((commentsByVersion) => { - return processComments(commentsByVersion.flat()); - }); + return Promise.all(commentsByVersionPromise).then((commentsByVersion) => { + return processComments(commentsByVersion.flat()) + }) } function getAction(parameterVersion, parameterConfig) { - //parameterVersion and parameterCconfig are optative for testing - const baseAction = - parameterConfig.baseActions.comment ?? getConfig().baseActions.comment; + //parameterVersion and parameterCconfig are optative for testing + const baseAction = + parameterConfig.baseActions.comment ?? getConfig().baseActions.comment - const versionData = parameterVersion - ? versions[parameterVersion] - : versions[currentVersion]; + const versionData = parameterVersion + ? versions[parameterVersion] + : versions[currentVersion] - const action = baseAction + versionData.suffixAction; + const action = baseAction + versionData.suffixAction - return parameterConfig.isTest || getConfig().isTest - ? `test_${action}` - : action; + return parameterConfig.isTest || getConfig().isTest + ? `test_${action}` + : action } function composeCommentData(comment, version, config) { - // if (comment.metadata.replyingTo) { - // //We add the following so the user been replied get's a notification - // comment.commentData.text = `@${comment.metadata.replyingTo} ${comment.commentData.text}`; - // } - - let data = { - index: { - [getAction(version, config)]: JSON.stringify({ - key: comment.metadata.articleId, - value: { - type: "md", - ...comment, + // if (comment.metadata.replyingTo) { + // //We add the following so the user been replied get's a notification + // comment.commentData.text = `@${comment.metadata.replyingTo} ${comment.commentData.text}`; + // } + + let data = { + index: { + [getAction(version, config)]: JSON.stringify({ + key: comment.metadata.articleId, + value: { + type: 'md', + ...comment, + }, + }), }, - }), - }, - }; + } + + if (comment.metadata.isDelete) return data - if(comment.metadata.isDelete) return data - - const mentions = comment.commentData.isDelete ? [] : extractMentions(comment.commentData.text); + const mentions = comment.commentData.isDelete + ? [] + : extractMentions(comment.commentData.text) - const articleIdSplitted = comment.metadata.articleId.split("/"); - const articleAuthor = articleIdSplitted[1]; + const articleIdSplitted = comment.metadata.articleId.split('/') + const articleAuthor = articleIdSplitted[1] - const dataToAdd = getNotificationData( - getConfig(), - mentions.length > 0 ? "mentionOnComment" : "comment", - mentions, - comment.metadata, - {author: articleAuthor} - ); + const dataToAdd = getNotificationData( + getConfig(), + mentions.length > 0 ? 'mentionOnComment' : 'comment', + mentions, + comment.metadata, + { author: articleAuthor } + ) - data.post = dataToAdd.post; - data.index.notify = dataToAdd.index.notify; - + data.post = dataToAdd.post + data.index.notify = dataToAdd.index.notify - return data; + return data } function executeSaveComment( - comment, - onCommit, - onCancel, - parameterVersion, - parameterConfig -) { - //parameterVersion and parameterConfig are optative for testing - const newData = composeCommentData( comment, - parameterVersion ?? currentVersion, - parameterConfig ?? config - ); - - Social.set(newData, { - force: true, onCommit, onCancel, - }); + parameterVersion, + parameterConfig +) { + //parameterVersion and parameterConfig are optative for testing + const newData = composeCommentData( + comment, + parameterVersion ?? currentVersion, + parameterConfig ?? config + ) - return comment.metadata.id; + Social.set(newData, { + force: true, + onCommit, + onCancel, + }) + + return comment.metadata.id } function createComment(props) { - const { - config, - author, - commentText, - replyingTo, - articleId, - onCommit, - onCancel, - } = props; - - setConfig(config); - - const metadataHelper = { - author, - idPrefix: "comment", - versionKey: currentVersion, - }; + const { + config, + author, + commentText, + replyingTo, + articleId, + onCommit, + onCancel, + } = props + + setConfig(config) + + const metadataHelper = { + author, + idPrefix: 'comment', + versionKey: currentVersion, + } - let metadata = generateMetadata(metadataHelper); - metadata.articleId = articleId; - metadata.rootId = replyingTo; + let metadata = generateMetadata(metadataHelper) + metadata.articleId = articleId + metadata.rootId = replyingTo - const comment = { - commentData: { text: commentText }, - metadata, - }; + const comment = { + commentData: { text: commentText }, + metadata, + } - const result = executeSaveComment(comment, onCommit, onCancel); + const result = executeSaveComment(comment, onCommit, onCancel) - return { error: !result, data: result }; + return { error: !result, data: result } } function editComment(props) { - const { config, comment, onCommit, onCancel } = props; + const { config, comment, onCommit, onCancel } = props - setConfig(config); + setConfig(config) - //TODO ask Dani abaut the second parameter in this case - // let metadata = updateMetadata(comment.metadata, currentVersion); - let metadata = updateMetadata(comment.value.metadata, currentVersion); - metadata.isEdition = true; + //TODO ask Dani abaut the second parameter in this case + // let metadata = updateMetadata(comment.metadata, currentVersion); + let metadata = updateMetadata(comment.value.metadata, currentVersion) + metadata.isEdition = true - //=========================================================================================================================================================================== - // interface comment { - // commentData: {text: string}, - // metadata - // } - //=========================================================================================================================================================================== + //=========================================================================================================================================================================== + // interface comment { + // commentData: {text: string}, + // metadata + // } + //=========================================================================================================================================================================== - const newComment = { - commentData: { text: comment.value.commentData.text }, - metadata, - }; + const newComment = { + commentData: { text: comment.value.commentData.text }, + metadata, + } - const result = executeSaveComment(newComment, onCommit, onCancel); + const result = executeSaveComment(newComment, onCommit, onCancel) - return { error: !result, data: result }; + return { error: !result, data: result } } function deleteComment(props) { - const { config, commentId, articleId, rootId, onCommit, onCancel } = props; + const { config, commentId, articleId, rootId, onCommit, onCancel } = props - setConfig(config); + setConfig(config) - let metadata = buildDeleteMetadata(commentId); - metadata.articleId = articleId; - metadata.rootId = rootId; + let metadata = buildDeleteMetadata(commentId) + metadata.articleId = articleId + metadata.rootId = rootId - const comment = { - metadata, - }; + const comment = { + metadata, + } - const result = executeSaveComment(comment, onCommit, onCancel); + const result = executeSaveComment(comment, onCommit, onCancel) - return { error: !result, data: result }; + return { error: !result, data: result } } return { - getComments, - createComment, - editComment, - deleteComment, - functionsToTest: { - normalizeOldToV_0_0_1, - normalizeFromV0_0_1ToV0_0_2, - normalizeFromV0_0_2ToV0_0_3, - normalizeFromV0_0_3ToV0_0_4, - setConfig, - getConfig, - fillAction, - getCommentBlackListByBlockHeight, - filterInvalidComments, - getUserNameFromCommentId, - processComments, getComments, - getSplittedCommentIdV0_0_3, - composeCommentData, createComment, editComment, deleteComment, - }, -}; + functionsToTest: { + normalizeOldToV_0_0_1, + normalizeFromV0_0_1ToV0_0_2, + normalizeFromV0_0_2ToV0_0_3, + normalizeFromV0_0_3ToV0_0_4, + setConfig, + getConfig, + fillAction, + getCommentBlackListByBlockHeight, + filterInvalidComments, + getUserNameFromCommentId, + processComments, + getComments, + getSplittedCommentIdV0_0_3, + composeCommentData, + createComment, + editComment, + deleteComment, + }, +} diff --git a/src/lib/errorParser.jsx b/src/lib/errorParser.jsx index e2d1018..1cbc0b0 100644 --- a/src/lib/errorParser.jsx +++ b/src/lib/errorParser.jsx @@ -1,9 +1,9 @@ function parseError(error) { - if(!Array.isArray(error)) return error - + if (!Array.isArray(error)) return error + return error.map((err, index) => { return
{err}
}) } -return { parseError } \ No newline at end of file +return { parseError } diff --git a/src/lib/metadata.jsx b/src/lib/metadata.jsx index 2ace7b6..b5a5978 100644 --- a/src/lib/metadata.jsx +++ b/src/lib/metadata.jsx @@ -1,29 +1,29 @@ function generateMetadata(metadataHelper) { - const { idPrefix, author, versionKey } = metadataHelper; - const now = Date.now(); - return { - id: `${idPrefix}/${author}/${now}`, - author, - createdTimestamp: now, - lastEditTimestamp: now, - versionKey, // Check `const versions` -> Object.keys(versions) - }; + const { idPrefix, author, versionKey } = metadataHelper + const now = Date.now() + return { + id: `${idPrefix}/${author}/${now}`, + author, + createdTimestamp: now, + lastEditTimestamp: now, + versionKey, // Check `const versions` -> Object.keys(versions) + } } function updateMetadata(previousMetadata, versionKey) { - return { - ...previousMetadata, - lastEditTimestamp: Date.now(), - versionKey, - }; + return { + ...previousMetadata, + lastEditTimestamp: Date.now(), + versionKey, + } } function buildDeleteMetadata(id) { - return { - id, - isDelete: true, - deleteTimestamp: Date.now(), - }; + return { + id, + isDelete: true, + deleteTimestamp: Date.now(), + } } -return { generateMetadata, updateMetadata, buildDeleteMetadata }; +return { generateMetadata, updateMetadata, buildDeleteMetadata } diff --git a/src/lib/nadaBot.jsx b/src/lib/nadaBot.jsx new file mode 100644 index 0000000..9c7a7d7 --- /dev/null +++ b/src/lib/nadaBot.jsx @@ -0,0 +1,48 @@ +function isHuman(accountId) { + return true + // console.log( + // Near.view('v1.nadabot.near', 'is_human', { account_id: 'tortum.near' }) + // ) + // return !accountId + // ? false + // : Near.view('v1.nadabot.near', 'is_human', { account_id: accountId }) +} + +const Container = styled.div` + button[disabled] button { + cursor: not-allowed; + } +` + +const HumanityWrapperButton = ({ children, accountId, ...props }) => { + const isDisabled = !isHuman(accountId) + const elemet = ( + + ) + + return isDisabled ? ( + + You cannot perform this action because you are not a + verified NADA bot human. + + } + > + {elemet} + + ) : ( + elemet + ) +} +return { isHuman, HumanityWrapperButton } diff --git a/src/lib/normalization.jsx b/src/lib/normalization.jsx index 0b107e9..f5c45a0 100644 --- a/src/lib/normalization.jsx +++ b/src/lib/normalization.jsx @@ -15,18 +15,18 @@ function normalizeObjectWithMetadata(object, versions) { } /** - * + * * @param {*} oldFormatId examples "c-acc.near-165198498" "uv-acc.near-841981914" * @param {*} idPrefix examples "article" "comments" - * @returns + * @returns */ function normalizeId(oldFormatId, idPrefix) { - const split = oldFormatId.split("-") + const split = (oldFormatId ?? '').split('-') const createdAt = split[split.length - 1] split.pop() split.shift() // Removes first - const accountId = split.join("-") + const accountId = split.join('-') return `${idPrefix}/${accountId}/${createdAt}` } -return { normalize, normalizeObjectWithMetadata, normalizeId } \ No newline at end of file +return { normalize, normalizeObjectWithMetadata, normalizeId } diff --git a/src/lib/notifications.jsx b/src/lib/notifications.jsx index 80c996d..f032bdc 100644 --- a/src/lib/notifications.jsx +++ b/src/lib/notifications.jsx @@ -1,97 +1,103 @@ //lib.notifications function extractMentions(text) { - const mentionRegex = - /@((?:(?:[a-z\d]+[-_])*[a-z\d]+\.)*(?:[a-z\d]+[-_])*[a-z\d]+)/gi; - mentionRegex.lastIndex = 0; - const accountIds = new Set(); - for (const match of text.matchAll(mentionRegex)) { - if ( - !/[\w`]/.test(match.input.charAt(match.index - 1)) && - !/[/\w`]/.test(match.input.charAt(match.index + match[0].length)) && - match[1].length >= 2 && - match[1].length <= 64 - ) { - accountIds.add(match[1].toLowerCase()); + const mentionRegex = + /@((?:(?:[a-z\d]+[-_])*[a-z\d]+\.)*(?:[a-z\d]+[-_])*[a-z\d]+)/gi + mentionRegex.lastIndex = 0 + const accountIds = new Set() + for (const match of text.matchAll(mentionRegex)) { + if ( + !/[\w`]/.test(match.input.charAt(match.index - 1)) && + !/[/\w`]/.test(match.input.charAt(match.index + match[0].length)) && + match[1].length >= 2 && + match[1].length <= 64 + ) { + accountIds.add(match[1].toLowerCase()) + } } - } - return [...accountIds]; + return [...accountIds] } function joinPeoplesName(usersToNotify) { - if (usersToNotify.length === 1) return `@${usersToNotify[0]}`; + if (usersToNotify.length === 1) return `@${usersToNotify[0]}` - return usersToNotify.reduce((acc, user, index) => { - if(index === usersToNotify.length - 1) { - acc += ` and `; - } else if(index !== 0) { - acc += `, `; - } + return usersToNotify.reduce((acc, user, index) => { + if (index === usersToNotify.length - 1) { + acc += ` and ` + } else if (index !== 0) { + acc += `, ` + } - return `${acc}@${user}` - }, ""); + return `${acc}@${user}` + }, '') } -function getNotificationData(config, notificationType, usersToNotify, metadata, extraParams) { - const { author } = extraParams; +function getNotificationData( + config, + notificationType, + usersToNotify, + metadata, + extraParams +) { + const { author } = extraParams - const baseURL = `https://near.org/${config.forumURL}?${config.isTest ? "isTest=true" : ""}` + const baseURL = `https://near.org/${config.forumURL}?${config.isTest ? 'isTest=true' : ''}` - const notificationTypeText = { - mention: { - text: `I have mentioned ${joinPeoplesName(usersToNotify)} in this post: `, - url: `${baseURL}&said=${metadata.id}` - }, - mentionOnComment: { - text: `I have mentioned ${joinPeoplesName(usersToNotify)} on my comment on this post: `, - url: `${baseURL}&said=${metadata.articleId}&scid=${metadata.id}` - }, - upVote: { - text: "I have upVoted this post: ", - url: `${baseURL}said=${metadata.articleId}` - }, - // emoji: { - // text: "I have reacted to this post: ", - // url: ``}, - comment: { - text: "I have commented this post: ", - url: `${baseURL}said=${metadata.articleId}&scid=${metadata.id}` - }, - }; + const notificationTypeText = { + mention: { + text: `I have mentioned ${joinPeoplesName(usersToNotify)} in this post: `, + url: `${baseURL}&said=${metadata.id}`, + }, + mentionOnComment: { + text: `I have mentioned ${joinPeoplesName(usersToNotify)} on my comment on this post: `, + url: `${baseURL}&said=${metadata.articleId}&scid=${metadata.id}`, + }, + upVote: { + text: 'I have upVoted this post: ', + url: `${baseURL}said=${metadata.articleId}`, + }, + // emoji: { + // text: "I have reacted to this post: ", + // url: ``}, + comment: { + text: 'I have commented this post: ', + url: `${baseURL}said=${metadata.articleId}&scid=${metadata.id}`, + }, + } - if(author && !usersToNotify.includes(author)) { - usersToNotify.push(author); - } + if (author && !usersToNotify.includes(author)) { + usersToNotify.push(author) + } - const dataToAdd = { - post: { - main: JSON.stringify({ - type: "md", - text: `${notificationTypeText[notificationType].text} ${notificationTypeText[notificationType].url}`, - }), - }, - index: { - notify: JSON.stringify( - usersToNotify.map((user) => { - return { - key: user, - value: { - type: "mention", - item: { - type: "social", - path: `${metadata.author}/post/main`, - }, - }, - }; - }) - ), - }, - }; + const dataToAdd = { + post: { + main: JSON.stringify({ + type: 'md', + text: `${notificationTypeText[notificationType].text} ${notificationTypeText[notificationType].url}`, + }), + }, + index: { + notify: JSON.stringify( + usersToNotify.map((user) => { + return { + key: user, + value: { + type: 'mention', + item: { + type: 'social', + path: `${metadata.author}/post/main`, + }, + }, + } + }) + ), + }, + } - return dataToAdd; + return dataToAdd } return { - extractMentions, - getNotificationData, - functionsToTest: {joinPeoplesName}, -}; + extractMentions, + getNotificationData, + functionsToTest: { joinPeoplesName }, +} diff --git a/src/lib/reactions.jsx b/src/lib/reactions.jsx index 6294784..b239cfd 100644 --- a/src/lib/reactions.jsx +++ b/src/lib/reactions.jsx @@ -1,12 +1,20 @@ -const { getFromIndex } = VM.require("communityvoice.ndctools.near/widget/lib.socialDbIndex") +const { getFromIndex } = VM.require( + 'chatter.cheddar.near/widget/lib.socialDbIndex' +) || { getFromIndex: () => {} } const { generateMetadata, updateMetadata, buildDeleteMetadata } = VM.require( - "communityvoice.ndctools.near/widget/lib.metadata" -); -const { normalizeId } = VM.require("communityvoice.ndctools.near/widget/lib.normalization") + 'chatter.cheddar.near/widget/lib.metadata' +) || { + generateMetadata: () => {}, + updateMetadata: () => {}, + buildDeleteMetadata: () => {}, +} +const { normalizeId } = VM.require( + 'chatter.cheddar.near/widget/lib.normalization' +) || { normalizeId: () => {} } let config = {} -const ID_PREFIX = "reaction" -const CURRENT_VERSION = "v0.0.2" +const ID_PREFIX = 'reaction' +const CURRENT_VERSION = 'v0.0.1' function setConfig(value) { config = value @@ -17,16 +25,16 @@ function getConfig() { } function getAction(version) { - const baseAction = getConfig().baseActions.reaction; - const versionData = version ? versions[version] : versions[CURRENT_VERSION]; - const action = baseAction + versionData.actionSuffix; - return getConfig().isTest ? `test_${action}` : action; + const baseAction = getConfig().baseActions.reaction + const versionData = version ? versions[version] : versions[CURRENT_VERSION] + const action = baseAction + versionData.actionSuffix + return getConfig().isTest ? `test_${action}` : action } function normalizeOldToV_0_0_1(reaction) { - reaction.value.sbts = ["public"]; + reaction.value.sbts = ['public'] - return reaction; + return reaction } function normalizeFromV0_0_1ToV0_0_2(reaction, extraParams) { @@ -36,7 +44,7 @@ function normalizeFromV0_0_1ToV0_0_2(reaction, extraParams) { elementReactedId, } - const split = reaction.value.reactionId.split("-") + const split = reaction.value.reactionId.split('-') const createdTimestamp = parseInt(split[split.length - 1]) const metadata = { @@ -44,14 +52,14 @@ function normalizeFromV0_0_1ToV0_0_2(reaction, extraParams) { author: reaction.accountId, createdTimestamp: createdTimestamp, lastEditTimestamp: createdTimestamp, - versionKey: "v0.0.2" + versionKey: 'v0.0.2', } return { ...reaction, value: { reactionData, - metadata - } + metadata, + }, } } @@ -60,19 +68,19 @@ function normalizeFromV0_0_2ToV0_0_3(reaction) { } const versions = { - old: { - normalizationFunction: normalizeOldToV_0_0_1, - actionSuffix: "", - }, - "v0.0.1": { - normalizationFunction: normalizeFromV0_0_1ToV0_0_2, + // old: { + // normalizationFunction: normalizeOldToV_0_0_1, + // actionSuffix: '', + // }, + 'v0.0.1': { + normalizationFunction: (reaction) => reaction, actionSuffix: `_v0.0.1`, }, - "v0.0.2": { - normalizationFunction: normalizeFromV0_0_2ToV0_0_3, - actionSuffix: `_v0.0.2`, - } -}; + // 'v0.0.2': { + // normalizationFunction: normalizeFromV0_0_2ToV0_0_3, + // actionSuffix: `_v0.0.2`, + // }, +} function fillAction(version) { const baseAction = getConfig().baseActions.reaction @@ -81,16 +89,21 @@ function fillAction(version) { } function getReactionBlackListByBlockHeight() { - return []; + return [] } function filterInvalidReactions(reactions) { - return reactions - .filter((reaction) => reaction.value.reactionId || reaction.value.metadata.id) // Has id + return (reactions ?? []) + .filter( + (reaction) => + reaction.value.reactionId || reaction.value.metadata.id + ) // Has id .filter( (reaction) => - !getReactionBlackListByBlockHeight().includes(reaction.blockHeight) // Blockheight is not in blacklist - ); + !getReactionBlackListByBlockHeight().includes( + reaction.blockHeight + ) // Blockheight is not in blacklist + ) } function normalizeReaction(reaction, versionsIndex, elementReactedId) { @@ -104,90 +117,94 @@ function normalizeReaction(reaction, versionsIndex, elementReactedId) { } function getLatestEdits(reactions) { - return reactions.filter((obj) => { + return (reactions ?? []).filter((obj) => { const userLatestInteraction = reactions.find( (reaction) => reaction.accountId === obj.accountId - ); - return JSON.stringify(userLatestInteraction) === JSON.stringify(obj); - }); + ) + return JSON.stringify(userLatestInteraction) === JSON.stringify(obj) + }) } function getReactionsNormalized(elementReactedId) { return Object.keys(versions).map((version, versionIndex) => { - const action = fillAction(versions[version]); + const action = fillAction(versions[version]) return getFromIndex(action, elementReactedId).then((allReactions) => { - const validReactions = filterInvalidReactions(allReactions); + const validReactions = filterInvalidReactions(allReactions) - const latestEdits = getLatestEdits(validReactions); + const latestEdits = getLatestEdits(validReactions) - const normalizedReactions = latestEdits.map((reaction) => normalizeReaction(reaction, versionIndex, elementReactedId)) + const normalizedReactions = latestEdits.map((reaction) => + normalizeReaction(reaction, versionIndex, elementReactedId) + ) return normalizedReactions - - }); - }); + }) + }) } function groupReactions(reactions, loggedUserAccountId) { - const userEmoji = undefined; - const accountsGroupedByReaction = {}; + const userEmoji = undefined + const accountsGroupedByReaction = {} reactions.forEach((reaction) => { - const emoji = reaction.value.reactionData.emoji.split(" ")[0]; + const emoji = reaction.value.reactionData.emoji.split(' ')[0] if (reaction.accountId === loggedUserAccountId) { - userEmoji = emoji; + userEmoji = emoji } if (!accountsGroupedByReaction[emoji]) { - accountsGroupedByReaction[emoji] = []; + accountsGroupedByReaction[emoji] = [] } - accountsGroupedByReaction[emoji].push(reaction.accountId); - }); + accountsGroupedByReaction[emoji].push(reaction.accountId) + }) const reactionsStatistics = Object.keys(accountsGroupedByReaction).map( (reaction) => { return { accounts: accountsGroupedByReaction[reaction], emoji: reaction, - }; + } } - ); + ) - return { reactionsStatistics, userEmoji }; + return { reactionsStatistics, userEmoji } } function getInitialEmoji() { - return "🤍 Like"; + return '🤍 Like' } function getEmojis() { return [ - "❤️ Positive", - "🙏 Thank you", - "💯 Definitely", - "👀 Thinking", - "🔥 Awesome", - "👍 Like", - "🙌 Celebrate", - "👏 Applause", - "⚡ Lightning", - "⋈ Bowtie", + '❤️ Positive', + '🙏 Thank you', + '💯 Definitely', + '👀 Thinking', + '🔥 Awesome', + '👍 Like', + '🙌 Celebrate', + '👏 Applause', + '⚡ Lightning', + '⋈ Bowtie', ] } function getReactions(config, elementReactedId, loggedUserAccountId) { setConfig(config) - const normReactionsPromise = getReactionsNormalized(elementReactedId); + const normReactionsPromise = getReactionsNormalized(elementReactedId) return Promise.all(normReactionsPromise).then((normReactions) => { const lastReactions = normReactions.flat() - const groupedReactions = groupReactions(lastReactions, loggedUserAccountId); + const groupedReactions = groupReactions( + lastReactions, + loggedUserAccountId + ) return groupedReactions }) } function validateEmoji(emoji) { - const errArrMessage = []; + const errArrMessage = [] if (!emoji) { - errArrMessage.push("You can only react with an emoji") + errArrMessage.push('You can only react with an emoji') } if (!getEmojis().includes(emoji)) { errArrMessage.push(`The emoji ${emoji} is not available`) @@ -205,36 +222,43 @@ function composeData(reaction) { }, }), }, - }; + } } /** - * - * @param {*} emoji + * + * @param {*} emoji * @param {*} elementReactedId May be an article or a comment - * @param {*} onCommit - * @param {*} onCancel + * @param {*} onCommit + * @param {*} onCancel */ -function createReaction(config, emoji, elementReactedId, author, onCommit, onCancel) { +function createReaction( + config, + emoji, + elementReactedId, + author, + onCommit, + onCancel +) { setConfig(config) const errors = validateEmoji(emoji) if (errors && errors.length) { - return { error: true, data: errors }; + return { error: true, data: errors } } const metadata = generateMetadata({ idPrefix: ID_PREFIX, author, - versionKey: CURRENT_VERSION + versionKey: CURRENT_VERSION, }) const reaction = { reactionData: { emoji, - elementReactedId + elementReactedId, }, metadata, - }; + } const data = composeData(reaction) @@ -242,10 +266,9 @@ function createReaction(config, emoji, elementReactedId, author, onCommit, onCan force: true, onCommit, onCancel, - }); - - return { error: false, data: "Reaction created successfully" }; + }) + return { error: false, data: 'Reaction created successfully' } } -return { getEmojis, getReactions, createReaction } \ No newline at end of file +return { getEmojis, getReactions, createReaction } diff --git a/src/lib/socialDbIndex.jsx b/src/lib/socialDbIndex.jsx index 47a3ef9..9052114 100644 --- a/src/lib/socialDbIndex.jsx +++ b/src/lib/socialDbIndex.jsx @@ -1,22 +1,21 @@ -const DEFAULT_ORDER = "desc" +const DEFAULT_ORDER = 'desc' function getFromIndex(action, key, order) { - if(!order){ + if (!order) { order = DEFAULT_ORDER - } - const indexUrl = "https://api.near.social/index" + } + const indexUrl = 'https://api.near.social/index' return asyncFetch(indexUrl, { - method: "POST", - headers: { "Content-Type": "application/json" }, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action, key, options: { - order - } - }) + order, + }, + }), }).then((response) => response.body) - } -return { getFromIndex } \ No newline at end of file +return { getFromIndex } diff --git a/src/lib/socialIndex.jsx b/src/lib/socialIndex.jsx index ad97475..763529f 100644 --- a/src/lib/socialIndex.jsx +++ b/src/lib/socialIndex.jsx @@ -2,4 +2,4 @@ function getIndexData(config, props) { return asyncFetch(config.urls.socialIndex, props) } -return { getIndexData } \ No newline at end of file +return { getIndexData } diff --git a/src/lib/strings.jsx b/src/lib/strings.jsx index 0fb470f..45c1e03 100644 --- a/src/lib/strings.jsx +++ b/src/lib/strings.jsx @@ -1,18 +1,18 @@ function camelCaseToUserReadable(str) { - return str.replace(/([a-z])([A-Z])/g, '$1 $2'); + return str.replace(/([a-z])([A-Z])/g, '$1 $2') } function isValidUrl(string) { try { - new URL(string); - return true; + new URL(string) + return true } catch (err) { - return false; + return false } } function arrayIncludesIgnoreCase(arr, value) { - return arr.some((item) => item.toLowerCase().includes(value.toLowerCase())); + return arr.some((item) => item.toLowerCase().includes(value.toLowerCase())) } -return { camelCaseToUserReadable, isValidUrl, arrayIncludesIgnoreCase }; \ No newline at end of file +return { camelCaseToUserReadable, isValidUrl, arrayIncludesIgnoreCase } diff --git a/src/lib/upVotes.jsx b/src/lib/upVotes.jsx index 3d78c6b..aed28f2 100644 --- a/src/lib/upVotes.jsx +++ b/src/lib/upVotes.jsx @@ -1,268 +1,263 @@ -const { getFromIndex } = VM.require("communityvoice.ndctools.near/widget/lib.socialDbIndex"); +const promise = new Promise((resolve, reject) => { + setTimeout(() => { + resolve([]) + }, 1) +}) + +const { getFromIndex } = VM.require( + 'chatter.cheddar.near/widget/lib.socialDbIndex' +) || { getFromIndex: () => promise } const { generateMetadata, updateMetadata, buildDeleteMetadata } = VM.require( - "communityvoice.ndctools.near/widget/lib.metadata" -); + 'chatter.cheddar.near/widget/lib.metadata' +) || { + generateMetadata: () => {}, + updateMetadata: () => {}, + buildDeleteMetadata: () => {}, +} const { getNotificationData } = VM.require( - "communityvoice.ndctools.near/widget/lib.notifications" -); + 'chatter.cheddar.near/widget/lib.notifications' +) || { getNotificationData: () => {} } -let config = {}; -const ID_PREFIX = "upVote" -const currentVersion = "v0.0.3"; +let config = {} +const ID_PREFIX = 'upVote' +const currentVersion = 'v0.0.1' function setConfig(value) { - config = value; + config = value } function getConfig() { - return config; + return config } function normalizeOldToV_0_0_1(upVote) { - return upVote; + return upVote } function normalizeFromV0_0_1ToV0_0_2(upVote) { - upVote.sbts = ["public"]; - return upVote; + upVote.sbts = ['public'] + return upVote } function normalizeFromV0_0_2ToV0_0_3(upVote) { - const now = Date.now() - if (upVote.value.isDelete) { + const now = Date.now() + if (upVote.value.isDelete) { + upVote.value.metadata = { + id, + isDelete: true, + deleteTimestamp: now, + } + delete upVote.value.isDelete + return upVote + } + + const author = (upVote.value.upVoteId ?? '').split('/')[1] upVote.value.metadata = { - id, - isDelete: true, - deleteTimestamp: now, - }; - delete upVote.value.isDelete; - return upVote; - } - - const author = upVote.value.upVoteId.split("/")[1] - upVote.value.metadata = { - id: upVote.value.upVoteId, - author, - createdTimestamp: now, - lastEditTimestamp: now, - versionKey: "v0.0.3", - }; - delete upVote.value.upVoteId; - delete upVote.value.sbts; - return upVote; + id: upVote.value.upVoteId, + author, + createdTimestamp: now, + lastEditTimestamp: now, + versionKey: 'v0.0.3', + } + delete upVote.value.upVoteId + delete upVote.value.sbts + return upVote } function normalizeFromV0_0_3ToV0_0_4(upVote) { - return upVote; + return upVote } const versions = { - old: { - normalizationFunction: normalizeOldToV_0_0_1, - actionSuffix: "", - }, - "v0.0.1": { - normalizationFunction: normalizeFromV0_0_1ToV0_0_2, - actionSuffix: `-v0.0.1`, - }, - "v0.0.2": { - normalizationFunction: normalizeFromV0_0_2ToV0_0_3, - actionSuffix: `_v0.0.2`, - }, - "v0.0.3": { - normalizationFunction: normalizeFromV0_0_3ToV0_0_4, - actionSuffix: `_v0.0.3`, - }, -}; - -function getUpVotesData(action, id) { - return getFromIndex(action, id); + old: { + normalizationFunction: normalizeOldToV_0_0_1, + actionSuffix: '', + }, + 'v0.0.1': { + normalizationFunction: (upVote) => upVote, + actionSuffix: `-v0.0.1`, + }, + 'v0.0.2': { + normalizationFunction: normalizeFromV0_0_2ToV0_0_3, + actionSuffix: `_v0.0.2`, + }, + 'v0.0.3': { + normalizationFunction: normalizeFromV0_0_3ToV0_0_4, + actionSuffix: `_v0.0.3`, + }, +} + +async function getUpVotesData(action, id) { + return getFromIndex(action, id) } function fillAction(version) { - const baseAction = getConfig().baseActions.upVote; - const filledAction = baseAction + version.actionSuffix; - return getConfig().isTest ? `test_${filledAction}` : filledAction; + const baseAction = getConfig().baseActions.upVote + const filledAction = baseAction + version.actionSuffix + return getConfig().isTest ? `test_${filledAction}` : filledAction } function getUpVoteBlackListByBlockHeight() { - return []; + return [] } function getLatestEdits(upVotes) { - return upVotes.filter((obj) => { - const userLatestInteraction = upVotes.find( - (vote) => vote.accountId === obj.accountId - ); - return JSON.stringify(userLatestInteraction) === JSON.stringify(obj); - }); + return upVotes.filter((obj) => { + const userLatestInteraction = upVotes.find( + (vote) => vote.accountId === obj.accountId + ) + return JSON.stringify(userLatestInteraction) === JSON.stringify(obj) + }) } function filterInvalidUpVotes(upVotes) { - return upVotes - .filter((upVote) => upVote.value.metadata.id) // Has id - .filter( - (upVote) => - !getUpVoteBlackListByBlockHeight().includes(upVote.blockHeight) // Blockheight is not in blacklist - ); + return (upVotes ?? []) + .filter((upVote) => upVote.value.metadata.id) // Has id + .filter( + (upVote) => + !getUpVoteBlackListByBlockHeight().includes(upVote.blockHeight) // Blockheight is not in blacklist + ) } function normalizeUpVote(upVote, versionsIndex) { - const versionsKeys = Object.keys(versions); - for (let i = versionsIndex; i < versionsKeys.length; i++) { - const version = versions[versionsKeys[i]]; - upVote = version.normalizationFunction(upVote); - } - return upVote; + const versionsKeys = Object.keys(versions) + for (let i = versionsIndex; i < versionsKeys.length; i++) { + const version = versions[versionsKeys[i]] + upVote = version.normalizationFunction(upVote) + } + return upVote } function isActive(upVote) { - return upVote.value.metadata && !upVote.value.metadata.isDelete; + return upVote.value.metadata && !upVote.value.metadata.isDelete } -function getUpVotes(config, articleId) { - setConfig(config); - const upVotesByVersionPromise = Object.keys(versions).map( - (version, versionIndex, arr) => { - const action = fillAction(versions[version]); - - return getUpVotesData(action, articleId).then((upVotes) => { - const validUpVotes = filterInvalidUpVotes(upVotes); - const latestUpVotes = getLatestEdits(validUpVotes); - - const activeUpVotes = latestUpVotes.filter(isActive); - - const normalizedVotes = activeUpVotes.map((upVote) => - normalizeUpVote(upVote, versionIndex) - ); - - return normalizedVotes; - }); - } - ); - return Promise.all(upVotesByVersionPromise).then((upVotesByVersion) => { - return upVotesByVersion.flat(); - }); +async function getUpVotes(config, articleId) { + setConfig(config) + const upVotesByVersionPromise = Object.keys(versions).map( + (version, versionIndex, arr) => { + const action = fillAction(versions[version]) + + return getUpVotesData(action, articleId).then((upVotes) => { + const validUpVotes = filterInvalidUpVotes(upVotes) + const latestUpVotes = getLatestEdits(validUpVotes) + + const activeUpVotes = latestUpVotes.filter(isActive) + + const normalizedVotes = activeUpVotes.map((upVote) => + normalizeUpVote(upVote, versionIndex) + ) + + return normalizedVotes + }) + } + ) + return Promise.all(upVotesByVersionPromise).then((upVotesByVersion) => { + return upVotesByVersion.flat() + }) } function getAction(version) { - const baseAction = getConfig().baseActions.upVote; - const versionData = version ? versions[version] : versions[currentVersion]; - const action = baseAction + versionData.actionSuffix; - return getConfig().isTest ? `test_${action}` : action; + const baseAction = getConfig().baseActions.upVote + const versionData = version ? versions[version] : versions[currentVersion] + const action = baseAction + versionData.actionSuffix + return getConfig().isTest ? `test_${action}` : action } function composeData(upVote) { - //version and config are optative for testing - let data = { - index: { - [getAction()]: JSON.stringify({ - key: upVote.metadata.articleId, - value: { - ...upVote, + //version and config are optative for testing + let data = { + index: { + [getAction()]: JSON.stringify({ + key: upVote.metadata.articleId, + value: { + ...upVote, + }, + }), }, - }), - }, - }; + } - if(upVote.metadata.isDelete) return data + if (upVote.metadata.isDelete) return data - const articleIdSplitted = upVote.metadata.articleId.split("/"); - const articleAuthor = articleIdSplitted[1]; - - const dataToAdd = getNotificationData( - getConfig(), - "upVote", - [], - upVote.metadata, - {author: articleAuthor} - ); + const articleIdSplitted = upVote.metadata.articleId.split('/') + const articleAuthor = articleIdSplitted[1] - data.post = dataToAdd.post; - data.index.notify = dataToAdd.index.notify; + const dataToAdd = getNotificationData( + getConfig(), + 'upVote', + [], + upVote.metadata, + { author: articleAuthor } + ) - return data; + data.post = dataToAdd.post + data.index.notify = dataToAdd.index.notify + + return data } -function executeSaveUpVote( - upVote, - onCommit, - onCancel -) { - //version and config are optative for testing - const newData = composeData(upVote); - Social.set(newData, { - force: true, - onCommit, - onCancel, - }); +function executeSaveUpVote(upVote, onCommit, onCancel) { + //version and config are optative for testing + const newData = composeData(upVote) + Social.set(newData, { + force: true, + onCommit, + onCancel, + }) } -function createUpVote( - config, - articleId, - author, - onCommit, - onCancel -) { - setConfig(config); - - const metadataHelper = { - author, - idPrefix: ID_PREFIX, - versionKey: currentVersion, - }; - - const metadata = generateMetadata(metadataHelper); - - const upVote = { - metadata: { - ...metadata, - articleId - }, - } - const result = executeSaveUpVote(upVote, onCommit, onCancel); - return { error: false, data: result }; +function createUpVote(config, articleId, author, onCommit, onCancel) { + setConfig(config) + const metadataHelper = { + author, + idPrefix: ID_PREFIX, + versionKey: currentVersion, + } + + const metadata = generateMetadata(metadataHelper) + + const upVote = { + metadata: { + ...metadata, + articleId, + }, + } + const result = executeSaveUpVote(upVote, onCommit, onCancel) + return { error: false, data: result } } -function deleteUpVote( - config, - articleId, - upVoteId, - onCommit, - onCancel -) { - setConfig(config); - - const deleteMetadata = buildDeleteMetadata(upVoteId); - const upVote = { - metadata: { - ...deleteMetadata, - articleId - }, - }; - executeSaveUpVote(upVote, onCommit, onCancel); +function deleteUpVote(config, articleId, upVoteId, onCommit, onCancel) { + setConfig(config) + + const deleteMetadata = buildDeleteMetadata(upVoteId) + const upVote = { + metadata: { + ...deleteMetadata, + articleId, + }, + } + executeSaveUpVote(upVote, onCommit, onCancel) } return { - getUpVotes, - createUpVote, - deleteUpVote, - functionsToTest: { - normalizeOldToV_0_0_1, - normalizeFromV0_0_1ToV0_0_2, - normalizeFromV0_0_2ToV0_0_3, - getUpVotesData, - fillAction, - getUpVoteBlackListByBlockHeight, - getLatestEdits, - filterInvalidUpVotes, - normalizeUpVote, - composeData, - executeSaveUpVote, + getUpVotes, createUpVote, deleteUpVote, - }, -}; + functionsToTest: { + normalizeOldToV_0_0_1, + normalizeFromV0_0_1ToV0_0_2, + normalizeFromV0_0_2ToV0_0_3, + getUpVotesData, + fillAction, + getUpVoteBlackListByBlockHeight, + getLatestEdits, + filterInvalidUpVotes, + normalizeUpVote, + composeData, + executeSaveUpVote, + createUpVote, + deleteUpVote, + }, +} diff --git a/src/testingWidgets/TestArticle.jsx b/src/testingWidgets/TestArticle.jsx index bb22a01..0e66a79 100644 --- a/src/testingWidgets/TestArticle.jsx +++ b/src/testingWidgets/TestArticle.jsx @@ -1,6 +1,9 @@ -const { getArticles, createArticle, editArticle, deleteArticle } = VM.require("communityvoice.ndctools.near/widget/lib.article") -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice") - +const { getArticles, createArticle, editArticle, deleteArticle } = VM.require( + 'chatter.cheddar.near/widget/lib.article' +) +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) const [articles, setArticles] = useState([]) const [errors, setErrors] = useState([]) @@ -10,15 +13,19 @@ const isTest = !!props.isTest const config = getConfig(isTest) function onCommit() { - console.log("Executing on commit") + console.log('Executing on commit') } function onCancel() { - console.log("Executing on cancel") + console.log('Executing on cancel') } function loadArticles() { - const userFilters = {id: "article/rodrigos.near/1710843635815", authors: ["rodrigos.near"], tags: ["tag1"]} + const userFilters = { + id: 'article/rodrigos.near/1710843635815', + authors: ['rodrigos.near'], + tags: ['tag1'], + } getArticles(config, userFilters).then((newArticles) => { setArticles(newArticles) }) @@ -27,7 +34,7 @@ function loadArticles() { useEffect(() => { loadArticles() setInterval(() => { - console.log("Loading articles interval", Date.now() / 1000) + console.log('Loading articles interval', Date.now() / 1000) loadArticles() }, 15000) }, []) @@ -35,33 +42,45 @@ useEffect(() => { function failNewArticle() { const failedArticleData = { title: undefined, - body: "Test", - tags: [], + body: 'Test', + tags: [], } const metadataHelper = { author: context.accountId, } - const result = createArticle(config, failedArticleData, metadataHelper, onCommit, onCancel) - if(result.error) { + const result = createArticle( + config, + failedArticleData, + metadataHelper, + onCommit, + onCancel + ) + if (result.error) { setErrors(result.data) } } function newArticle() { const articleData = { - title: "Test title", - body: "Test body", - tags: ["hello"], + title: 'Test title', + body: 'Test body', + tags: ['hello'], } const metadataHelper = { author: context.accountId, } - const result = createArticle(config, articleData, metadataHelper, onCommit, onCancel) - if(result.error) { + const result = createArticle( + config, + articleData, + metadataHelper, + onCommit, + onCancel + ) + if (result.error) { setErrors(result.data) } } @@ -69,37 +88,53 @@ function newArticle() { function modifyArticle(article) { const articleData = article.value.articleData const metadata = article.value.metadata - articleData.body = "This is a test editing an article" - const result = editArticle(config, articleData, metadata, onCommit, onCancel) - if(result.error) { + articleData.body = 'This is a test editing an article' + const result = editArticle( + config, + articleData, + metadata, + onCommit, + onCancel + ) + if (result.error) { setErrors(result.data) } } function removeArticle(article) { - console.log("Removing article", article.value.metadata.id) + console.log('Removing article', article.value.metadata.id) deleteArticle(config, article.value.metadata.id, onCommit, onCancel) } -return <> -
- {errors && errors.length ? errors.map((err, index) => { - return
{err}
- }) : "No error"} -
-
Articles: {articles.length}
- - - - - { articles.length &&
- {articles.map((article, index) => - { - return (
- {index + 1}) - {JSON.stringify(article, null, 2)} -
-
) - })} -
} - \ No newline at end of file +return ( + <> +
+ {errors && errors.length + ? errors.map((err, index) => { + return
{err}
+ }) + : 'No error'} +
+
Articles: {articles.length}
+ + + + + {articles.length && ( +
+ {articles.map((article, index) => { + return ( +
+ {index + 1}){JSON.stringify(article, null, 2)} +
+
+ ) + })} +
+ )} + +) diff --git a/src/testingWidgets/TestComments.jsx b/src/testingWidgets/TestComments.jsx index 19a9d6d..48fc2f4 100644 --- a/src/testingWidgets/TestComments.jsx +++ b/src/testingWidgets/TestComments.jsx @@ -1,104 +1,93 @@ const { getComments, createComment, editComment, deleteComment } = VM.require( - "communityvoice.ndctools.near/widget/lib.comment" -); -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice"); + 'chatter.cheddar.near/widget/lib.comment' +) +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) -const [comments, setComments] = useState([]); +const [comments, setComments] = useState([]) -const isTest = !!props.isTest; +const isTest = !!props.isTest -const config = getConfig(isTest); -const articleId = "ayelen.near-1699314338205"; +const config = getConfig(isTest) +const articleId = 'ayelen.near-1699314338205' function onCommit() { - console.log("Executing on commit") + console.log('Executing on commit') } function onCancel() { - console.log("Executing on cancel") + console.log('Executing on cancel') } function loadComments() { - getComments(articleId, config).then((newComments) => { - setComments(newComments); - }); + getComments(articleId, config).then((newComments) => { + setComments(newComments) + }) } useEffect(() => { - loadComments(); - setInterval(() => { - console.log("Loading comments interval", Date.now() / 1000); - loadComments(); - }, 30000); -}, []); + loadComments() + setInterval(() => { + console.log('Loading comments interval', Date.now() / 1000) + loadComments() + }, 30000) +}, []) function newComment() { - createComment({ - config, - author: context.accountId, - commentText: "@ayelen.near Text of the comment", - replyingTo: articleId, //replyingTo will have the rootId. It can be an articleId or a comment.value.comment.metadata.id. - articleId, - onCommit, - onCancel - }); + createComment({ + config, + author: context.accountId, + commentText: '@ayelen.near Text of the comment', + replyingTo: articleId, //replyingTo will have the rootId. It can be an articleId or a comment.value.comment.metadata.id. + articleId, + onCommit, + onCancel, + }) } -let commentExampleToEdit = comments[comments.length - 1]; +let commentExampleToEdit = comments[comments.length - 1] function doEdition() { - commentExampleToEdit.value.commentData.text = "Text edited 4"; - editComment({ - config, - comment: commentExampleToEdit, - onCommit, - onCancel, - }); + commentExampleToEdit.value.commentData.text = 'Text edited 4' + editComment({ + config, + comment: commentExampleToEdit, + onCommit, + onCancel, + }) } - function supressComment() { - deleteComment({ - config, - commentId: commentExampleToEdit.value.metadata.id, - articleId: commentExampleToEdit.value.metadata.articleId, - rootId: commentExampleToEdit.value.metadata.rootId, - onCommit, - onCancel, - }); + deleteComment({ + config, + commentId: commentExampleToEdit.value.metadata.id, + articleId: commentExampleToEdit.value.metadata.articleId, + rootId: commentExampleToEdit.value.metadata.rootId, + onCommit, + onCancel, + }) } return ( - <> -
- {errors && errors.length - ? errors.map((err, index) => { - return
{err}
; - }) - : "No error"} -
-
Comments: {comments.length}
- {comments && comments.length && ( -
- {comments.map((upVote, index) => { - return
{JSON.stringify(upVote)}
; - })} -
- )} - - - - -); + <> +
+ {errors && errors.length + ? errors.map((err, index) => { + return
{err}
+ }) + : 'No error'} +
+
Comments: {comments.length}
+ {comments && comments.length && ( +
+ {comments.map((upVote, index) => { + return
{JSON.stringify(upVote)}
+ })} +
+ )} + + + + +) diff --git a/src/testingWidgets/TestCommunities.jsx b/src/testingWidgets/TestCommunities.jsx index 6c8127b..cde4a17 100644 --- a/src/testingWidgets/TestCommunities.jsx +++ b/src/testingWidgets/TestCommunities.jsx @@ -1,5 +1,11 @@ -const { setIsTest, createCommunity, getCommunities, editCommunity, deleteCommunity } = VM.require("communityvoice.ndctools.near/widget/lib.Communities") -const { parseError } = VM.require("communityvoice.ndctools.near/widget/lib.errorParser") +const { + setIsTest, + createCommunity, + getCommunities, + editCommunity, + deleteCommunity, +} = VM.require('chatter.cheddar.near/widget/lib.Communities') +const { parseError } = VM.require('chatter.cheddar.near/widget/lib.errorParser') const [communities, setCommunities] = useState([]) const [errors, setErrors] = useState([]) @@ -8,11 +14,11 @@ const [errors, setErrors] = useState([]) setIsTest(true) function onCommit() { - console.log("On commit executed") + console.log('On commit executed') } function onCancel() { - console.log("On cancel executed") + console.log('On cancel executed') } function loadCommunities() { @@ -27,40 +33,45 @@ useEffect(() => { loadCommunities() }, 200) setInterval(() => { - console.log("Loading communities interval", Date.now() / 1000) + console.log('Loading communities interval', Date.now() / 1000) loadCommunities() }, 30000) }, []) function failNewCommunity() { const community = { - name: "", // Should not be empty or undefined - description: "Description", // Should not be empty or undefined + name: '', // Should not be empty or undefined + description: 'Description', // Should not be empty or undefined type: 0, // Should not be between 0 and 2 - backgroundImage: "https://www.google.com", // Should not be empty or undefined and should be a valid url - profileImage: "https://www.google.com", // Should not be empty or undefined and should be a valid url + backgroundImage: 'https://www.google.com', // Should not be empty or undefined and should be a valid url + profileImage: 'https://www.google.com', // Should not be empty or undefined and should be a valid url } // const res = createCommunity(community, context.accountId, onCommit, onCancel) // User must be logged in and parameter should be passed const res = createCommunity(community, undefined, onCommit, onCancel) if (res.error) { - console.log("Data", res.data) + console.log('Data', res.data) setErrors(res.data) } } function newCommunity() { const community = { - name: "Hello", - description: "Description", + name: 'Hello', + description: 'Description', type: 0, - backgroundImage: "https://www.google.com", - profileImage: "https://www.google.com", + backgroundImage: 'https://www.google.com', + profileImage: 'https://www.google.com', } - const res = createCommunity(community, context.accountId, onCommit, onCancel) + const res = createCommunity( + community, + context.accountId, + onCommit, + onCancel + ) if (res.error) { - console.log("Data", res.data) + console.log('Data', res.data) setErrors(res.data) } } @@ -68,51 +79,62 @@ function newCommunity() { function modifyCommunity(communityIndex) { const community = { ...communityIndex.value.communityData, - name: communityIndex.value.communityData.name + " Edited" + name: communityIndex.value.communityData.name + ' Edited', } communityIndex.value.communityData = community const res = editCommunity(communityIndex, onCommit, onCancel) if (res.error) { - console.log("Data", res.data) + console.log('Data', res.data) setErrors(res.data) } } function removeCommunity() { const community = { - id: "kenrou-it.testnet-1708116044393", - name: "Hello edited again", - description: "Description edited again", + id: 'kenrou-it.testnet-1708116044393', + name: 'Hello edited again', + description: 'Description edited again', type: 1, - backgroundImage: "https://www.google.com.ar", - profileImage: "https://www.google.com.ar", + backgroundImage: 'https://www.google.com.ar', + profileImage: 'https://www.google.com.ar', } const res = deleteCommunity(community, onCommit, onCancel) - console.log("Community removed") + console.log('Community removed') if (res.error) { - console.log("Data", res.data) + console.log('Data', res.data) setErrors(res.data) } } -return <> -
- {errors && errors.length ? errors.map((err, index) => { - return
{err}
- }) : "No error"} -
-
Communities: {communities.length}
- - - - - { communities && communities.length &&
- {communities.map((community, index) => - { - return (
{JSON.stringify(community, null, "\n\t")}
) - })} -
} - \ No newline at end of file +return ( + <> +
+ {errors && errors.length + ? errors.map((err, index) => { + return
{err}
+ }) + : 'No error'} +
+
Communities: {communities.length}
+ + + + + {communities && communities.length && ( +
+ {communities.map((community, index) => { + return ( +
+ {JSON.stringify(community, null, '\n\t')} +
+ ) + })} +
+ )} + +) diff --git a/src/testingWidgets/TestNotifications.jsx b/src/testingWidgets/TestNotifications.jsx index fefdcf1..ba91b7d 100644 --- a/src/testingWidgets/TestNotifications.jsx +++ b/src/testingWidgets/TestNotifications.jsx @@ -1,11 +1,13 @@ -const {functionsToTest} = VM.require("communityvoice.ndctools.near/widget/lib.notifications"); +const { functionsToTest } = VM.require( + 'chatter.cheddar.near/widget/lib.notifications' +) -const arr1 = ["Martín"]; -const arr2 = ["Martín", "silkking.near"]; -const arr3 = ["Martín", "silkking.near", "bb"]; -const arr4 = ["Martín", "silkking.near", "bb", "fiufiu"]; +const arr1 = ['Martín'] +const arr2 = ['Martín', 'silkking.near'] +const arr3 = ['Martín', 'silkking.near', 'bb'] +const arr4 = ['Martín', 'silkking.near', 'bb', 'fiufiu'] -const {joinPeoplesName} = functionsToTest; +const { joinPeoplesName } = functionsToTest return ( <> diff --git a/src/testingWidgets/TestReaction.jsx b/src/testingWidgets/TestReaction.jsx index 6b18608..072091d 100644 --- a/src/testingWidgets/TestReaction.jsx +++ b/src/testingWidgets/TestReaction.jsx @@ -1,73 +1,97 @@ -const { getReactions, createReaction } = VM.require("communityvoice.ndctools.near/widget/lib.reactions") -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice") +const { getReactions, createReaction } = VM.require( + 'chatter.cheddar.near/widget/lib.reactions' +) +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) -const [reactions, setReactions] = useState({reactionsStatistics: [], userReaction: undefined}) +const [reactions, setReactions] = useState({ + reactionsStatistics: [], + userReaction: undefined, +}) -const articleId = "ayelen.near-1699406465524" +const articleId = 'ayelen.near-1699406465524' const isTest = !!props.isTest const config = getConfig(isTest) function onCommit() { - console.log("On commit") + console.log('On commit') } function onCancel() { - console.log("On cancel") + console.log('On cancel') } function loadReactions() { - getReactions(config, articleId, context.accountId).then((newReactions) => { setReactions(newReactions) }) } function addReaction() { - const emoji = "❤️ Positive" + const emoji = '❤️ Positive' const elementReactedId = articleId const author = context.accountId - - const result = createReaction(config, emoji, elementReactedId, author, onCommit, onCancel) - if(result.error) { + + const result = createReaction( + config, + emoji, + elementReactedId, + author, + onCommit, + onCancel + ) + if (result.error) { setErrors(result.data) } } useEffect(() => { - loadReactions() setInterval(() => { - console.log("Loading reactions interval", Date.now() / 1000) + console.log('Loading reactions interval', Date.now() / 1000) loadReactions() }, 15000) }, []) -return <> -
- {errors && errors.length ? errors.map((err, index) => { - return
{err}
- }) : "No error"} -
-
Reaction types: {reactions.reactionsStatistics.length}
-
User emoji: {reactions.userEmoji ?? "No user reaction"}
- - {/* - */} - { reactions.reactionsStatistics.length && +return ( + <>
- {reactions.reactionsStatistics.map((emoji, index) => - { - return (
Emoji: {emoji.emoji}. Total accounts: {emoji.accounts.length}. Accounts: {JSON.stringify(emoji.accounts)}
) - })} -
- } - { reactions.reactionsStatistics.length && -
Total reactions: - {reactions.reactionsStatistics.reduce((totalReactions, currEmoji) => - { - return totalReactions + currEmoji.accounts.length - }, 0)} + {errors && errors.length + ? errors.map((err, index) => { + return
{err}
+ }) + : 'No error'}
- } - - \ No newline at end of file +
Reaction types: {reactions.reactionsStatistics.length}
+
User emoji: {reactions.userEmoji ?? 'No user reaction'}
+ + {/* + */} + {reactions.reactionsStatistics.length && ( +
+ {reactions.reactionsStatistics.map((emoji, index) => { + return ( +
+ Emoji: {emoji.emoji}. Total accounts:{' '} + {emoji.accounts.length}. Accounts:{' '} + {JSON.stringify(emoji.accounts)} +
+ ) + })} +
+ )} + {reactions.reactionsStatistics.length && ( +
+ {' '} + Total reactions: + {reactions.reactionsStatistics.reduce( + (totalReactions, currEmoji) => { + return totalReactions + currEmoji.accounts.length + }, + 0 + )} +
+ )} + +) diff --git a/src/testingWidgets/TestSBT.jsx b/src/testingWidgets/TestSBT.jsx index 1216b23..af2cbb1 100644 --- a/src/testingWidgets/TestSBT.jsx +++ b/src/testingWidgets/TestSBT.jsx @@ -1,5 +1,7 @@ -const { getUserSBTs } = VM.require("communityvoice.ndctools.near/widget/lib.SBT") -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice") +const { getUserSBTs } = VM.require('chatter.cheddar.near/widget/lib.SBT') +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) const [userSbts, setSbts] = useState([]) @@ -8,7 +10,7 @@ const isTest = !!props.isTest const config = getConfig(isTest) function loadSbts(index) { - getUserSBTs("silkking.near",config).then((newArticles) => { + getUserSBTs('silkking.near', config).then((newArticles) => { setSbts(newArticles) }) } @@ -16,26 +18,35 @@ function loadSbts(index) { useEffect(() => { loadSbts() setInterval(() => { - console.log("Loading sbts interval", Date.now() / 1000) + console.log('Loading sbts interval', Date.now() / 1000) loadSbts() }, 10000) }, []) -return <> -
- {errors && errors.length ? errors.map((err, index) => { - return
{err}
- }) : "No error"} -
-
Sbts: {userSbts.length}
- {/* +return ( + <> +
+ {errors && errors.length + ? errors.map((err, index) => { + return
{err}
+ }) + : 'No error'} +
+
Sbts: {userSbts.length}
+ {/* */} - { userSbts && userSbts.length &&
- {userSbts.map((userSbt, index) => - { - return (
{JSON.stringify(userSbt, null, "\n\t")}
) - })} -
} - \ No newline at end of file + {userSbts && userSbts.length && ( +
+ {userSbts.map((userSbt, index) => { + return ( +
+ {JSON.stringify(userSbt, null, '\n\t')} +
+ ) + })} +
+ )} + +) diff --git a/src/testingWidgets/TestUpVotes.jsx b/src/testingWidgets/TestUpVotes.jsx index 6f2df5e..35938b4 100644 --- a/src/testingWidgets/TestUpVotes.jsx +++ b/src/testingWidgets/TestUpVotes.jsx @@ -1,11 +1,15 @@ -const { getUpVotes, createUpVote, deleteUpVote } = VM.require("communityvoice.ndctools.near/widget/lib.upVotes") -const { getConfig } = VM.require("communityvoice.ndctools.near/widget/config.CommunityVoice") +const { getUpVotes, createUpVote, deleteUpVote } = VM.require( + 'chatter.cheddar.near/widget/lib.upVotes' +) +const { getConfig } = VM.require( + 'chatter.cheddar.near/widget/config.CommunityVoice' +) const [upVotes, setUpVotes] = useState([]) const isTest = !!props.isTest const config = getConfig(isTest) -const articleId = "article/rodrigos.near/1710843635815" +const articleId = 'article/rodrigos.near/1710843635815' function loadUpVotes() { getUpVotes(config, articleId).then((newVotes) => { @@ -14,38 +18,54 @@ function loadUpVotes() { } useEffect(() => { - loadUpVotes() setInterval(() => { - console.log("Loading upvotes interval", Date.now() / 1000) + console.log('Loading upvotes interval', Date.now() / 1000) loadUpVotes() }, 15000) }, []) function newUpVote() { - const result = createUpVote(config, articleId, context.accountId, onCommit, onCancel) - if(result.error) { + const result = createUpVote( + config, + articleId, + context.accountId, + onCommit, + onCancel + ) + if (result.error) { setErrors(result.data) } } -function removeUpVote() { - deleteUpVote(config, articleId, upVotes[0].value.metadata.id, onCommit, onCancel) +function removeUpVote() { + deleteUpVote( + config, + articleId, + upVotes[0].value.metadata.id, + onCommit, + onCancel + ) } -return <> -
- {errors && errors.length ? errors.map((err, index) => { - return
{err}
- }) : "No error"} -
-
Upvotes: {upVotes.length}
- - - { upVotes && upVotes.length &&
- {upVotes.map((upVote, index) => - { - return (
{JSON.stringify(upVote)}
) - })} -
} - \ No newline at end of file +return ( + <> +
+ {errors && errors.length + ? errors.map((err, index) => { + return
{err}
+ }) + : 'No error'} +
+
Upvotes: {upVotes.length}
+ + + {upVotes && upVotes.length && ( +
+ {upVotes.map((upVote, index) => { + return
{JSON.stringify(upVote)}
+ })} +
+ )} + +) diff --git a/src/tests/lib/tester.jsx b/src/tests/lib/tester.jsx index 060f2b2..96cc5f3 100644 --- a/src/tests/lib/tester.jsx +++ b/src/tests/lib/tester.jsx @@ -1,207 +1,262 @@ -const renderFeedback = (functionArray, filteredErrorResults, functionsPassingTest, isSync) => { - return <> - Running {`${isSync ? "sync": "async"}`} functions: -
    - {functionArray.map((fn, index) => ( -
  • - {fn.fnName} -
  • - ))} -
-
- {Object.keys(filteredErrorResults).length ? ( +const renderFeedback = ( + functionArray, + filteredErrorResults, + functionsPassingTest, + isSync +) => { + return ( <> - {/* + + Running {`${isSync ? 'sync' : 'async'}`} functions:{' '} + +
    + {functionArray.map((fn, index) => ( +
  • + {fn.fnName} +
  • + ))} +
+
+ {Object.keys(filteredErrorResults).length ? ( + <> + {/* *The number after the function name represents the index asociated to the function. You can call multiple times the same function. */} - {functionsPassingTest.length > 0 && ( -
- - {`${functionsPassingTest.length} function${ - functionsPassingTest.length > 1 ? "s" : "" - } have passed the test:`} - -
-
    - {functionsPassingTest.map((fn) => ( -
  • - {fn} -
  • - ))} -
-
-
- )} - { -
- {Object.keys(filteredErrorResults).map((fn) => { - return ( -
- {`${fn}`}: - {filteredErrorResults[fn].description && ( - - {filteredErrorResults[fn].description} - - )} -
- {filteredErrorResults[fn].errorList.map((error) => { - return ( - <> - {typeof error === "string" ? ( -

{error}

- ) : Array.isArray(error) ? ( - error.map((e) => ( -

{e}

- )) - ) : ( -

- Error passed wrongly -

- )} - {filteredErrorResults[fn].errorList.length > 1 && ( -
- )} - - ); - })} + {functionsPassingTest.length > 0 && ( +
+ + {`${functionsPassingTest.length} function${ + functionsPassingTest.length > 1 + ? 's' + : '' + } have passed the test:`} + +
+
    + {functionsPassingTest.map((fn) => ( +
  • + {fn} +
  • + ))} +
+
+
+ )} + { +
+ {Object.keys(filteredErrorResults).map((fn) => { + return ( +
+ + {`${fn}`}: + + {filteredErrorResults[fn] + .description && ( + + { + filteredErrorResults[fn] + .description + } + + )} +
+ {filteredErrorResults[ + fn + ].errorList.map((error) => { + return ( + <> + {typeof error === + 'string' ? ( +

+ {error} +

+ ) : Array.isArray( + error + ) ? ( + error.map( + (e) => ( +

+ {e} +

+ ) + ) + ) : ( +

+ Error passed + wrongly +

+ )} + {filteredErrorResults[ + fn + ].errorList.length > + 1 &&
} + + ) + })} +
+
+ ) + })} +
+ } + + ) : ( +
+ + {functionArray.length > 1 + ? `All ${functionArray.length}` + : 'The'}{' '} + test{functionArray.length > 1 ? 's have' : ' has'}{' '} + passed +
-
- ); - })} + )}
- } - ) : ( -
- - {functionArray.length > 1 ? `All ${functionArray.length}` : "The"}{" "} - test{functionArray.length > 1 ? "s have" : " has"} passed - -
- )} -
- ; -}; + ) +} function displayTestsSyncResults(functionArray) { - if (!Array.isArray(functionArray)) { - return { error: true, msg: "Parameter is not an array" }; - } - - const errorResults = {}; - const functionsPassingTest = []; - for (let i = 0; i < functionArray.length; i++) { - const fn = functionArray[i].fn; - - //Sets a key for this function in errorResults - errorResults[functionArray[i].fnName] = {}; - errorResults[functionArray[i].fnName].description = - functionArray[i].description; - errorResults[functionArray[i].fnName].errorList = []; - if (typeof fn !== "function") { - errorResults[functionArray[i].fnName].errorList.push( - `${functionArray[i].fnName} is not a function` - ); - continue; + if (!Array.isArray(functionArray)) { + return { error: true, msg: 'Parameter is not an array' } } - const fnResult = fn(); + const errorResults = {} + const functionsPassingTest = [] + for (let i = 0; i < functionArray.length; i++) { + const fn = functionArray[i].fn - if (fnResult.isError) { - //Error msg can be an string to use 1 line or an Array to use 1 line per element in the Array - errorResults[functionArray[i].fnName].errorList.push(fnResult.msg); - } else { - functionsPassingTest.push(functionArray[i].fnName); - } - } - - //Filter functions without errors of list - let filteredErrorResults = Object.keys(errorResults) - .filter((keyName) => errorResults[keyName].errorList.length > 0) - .reduce((newObj, key) => { - newObj[key] = { - description: errorResults[key].description, - errorList: errorResults[key].errorList, - }; - return newObj; - }, {}); - - const Note = styled.strong` - font-size: small; - `; - - return renderFeedback(functionArray, filteredErrorResults, functionsPassingTest, true); -} + //Sets a key for this function in errorResults + errorResults[functionArray[i].fnName] = {} + errorResults[functionArray[i].fnName].description = + functionArray[i].description + errorResults[functionArray[i].fnName].errorList = [] + if (typeof fn !== 'function') { + errorResults[functionArray[i].fnName].errorList.push( + `${functionArray[i].fnName} is not a function` + ) + continue + } -function displayTestsAsyncResults(functionArray) { - if (!Array.isArray(functionArray)) { - return { error: true, msg: "Parameter is not an array" }; - } - - const functionsReturns = []; - - for (let i = 0; i < functionArray.length; i++) { - const fn = functionArray[i].fn; - - const functionReturn = fn().then((functionResponse) => { - //Sets a key for this function in errorResults - const functionName = functionArray[i].fnName; - - let functionPassingTest; - const functionErrorResults = {}; - functionErrorResults.description = functionArray[i].description; - functionErrorResults.errorList = []; - - if (typeof fn !== "function") { - functionErrorResults.errorList.push( - `${functionName} is not a function` - ); - } else { - if (functionResponse.isError) { - //Error msg can be an string to use 1 line or an Array to use 1 line per element in the Array - functionErrorResults.errorList.push(functionResponse.msg); + const fnResult = fn() + + if (fnResult.isError) { + //Error msg can be an string to use 1 line or an Array to use 1 line per element in the Array + errorResults[functionArray[i].fnName].errorList.push(fnResult.msg) } else { - functionPassingTest = functionName; + functionsPassingTest.push(functionArray[i].fnName) } - } - - return { functionName, functionErrorResults, functionPassingTest }; - }); - functionsReturns.push(functionReturn); - } - - const finalResults = Promise.all(functionsReturns).then((functionsResults) => { - const errorResults = {}; - const functionsPassingTest = []; - functionsResults.forEach((functionResult) => { - errorResults[functionResult.functionName] = - functionResult.functionErrorResults; - functionsPassingTest.push(functionResult.functionPassingTest); - }); + } //Filter functions without errors of list let filteredErrorResults = Object.keys(errorResults) - .filter((keyName) => errorResults[keyName].errorList.length > 0) - .reduce((newObj, key) => { - newObj[key] = { - description: errorResults[key].description, - errorList: errorResults[key].errorList, - }; - return newObj; - }, {}); - return { filteredErrorResults, functionsPassingTest }; - }); - - const Note = styled.strong` - font-size: small; - `; - - return finalResults.then((result) => { - 3; - const { filteredErrorResults, functionsPassingTest } = result; - return renderFeedback(functionArray, filteredErrorResults, functionsPassingTest, false); - }); + .filter((keyName) => errorResults[keyName].errorList.length > 0) + .reduce((newObj, key) => { + newObj[key] = { + description: errorResults[key].description, + errorList: errorResults[key].errorList, + } + return newObj + }, {}) + + const Note = styled.strong` + font-size: small; + ` + + return renderFeedback( + functionArray, + filteredErrorResults, + functionsPassingTest, + true + ) +} + +function displayTestsAsyncResults(functionArray) { + if (!Array.isArray(functionArray)) { + return { error: true, msg: 'Parameter is not an array' } + } + + const functionsReturns = [] + + for (let i = 0; i < functionArray.length; i++) { + const fn = functionArray[i].fn + + const functionReturn = fn().then((functionResponse) => { + //Sets a key for this function in errorResults + const functionName = functionArray[i].fnName + + let functionPassingTest + const functionErrorResults = {} + functionErrorResults.description = functionArray[i].description + functionErrorResults.errorList = [] + + if (typeof fn !== 'function') { + functionErrorResults.errorList.push( + `${functionName} is not a function` + ) + } else { + if (functionResponse.isError) { + //Error msg can be an string to use 1 line or an Array to use 1 line per element in the Array + functionErrorResults.errorList.push(functionResponse.msg) + } else { + functionPassingTest = functionName + } + } + + return { functionName, functionErrorResults, functionPassingTest } + }) + functionsReturns.push(functionReturn) + } + + const finalResults = Promise.all(functionsReturns).then( + (functionsResults) => { + const errorResults = {} + const functionsPassingTest = [] + functionsResults.forEach((functionResult) => { + errorResults[functionResult.functionName] = + functionResult.functionErrorResults + functionsPassingTest.push(functionResult.functionPassingTest) + }) + + //Filter functions without errors of list + let filteredErrorResults = Object.keys(errorResults) + .filter((keyName) => errorResults[keyName].errorList.length > 0) + .reduce((newObj, key) => { + newObj[key] = { + description: errorResults[key].description, + errorList: errorResults[key].errorList, + } + return newObj + }, {}) + return { filteredErrorResults, functionsPassingTest } + } + ) + + const Note = styled.strong` + font-size: small; + ` + + return finalResults.then((result) => { + 3 + const { filteredErrorResults, functionsPassingTest } = result + return renderFeedback( + functionArray, + filteredErrorResults, + functionsPassingTest, + false + ) + }) } -return { displayTestsSyncResults, displayTestsAsyncResults }; +return { displayTestsSyncResults, displayTestsAsyncResults } diff --git a/src/tests/testLibArticle.jsx b/src/tests/testLibArticle.jsx index 7f77014..42cdadd 100644 --- a/src/tests/testLibArticle.jsx +++ b/src/tests/testLibArticle.jsx @@ -1,25 +1,25 @@ const { - getLatestEdits, - processArticles, - getArticleNormalized, - getArticlesIndexes, - getAction, - filterFakeAuthors, - getArticleBlackListByArticleId, - getArticleBlackListByBlockHeight, -} = VM.require("communityvoice.ndctools.near/widget/lib.article"); + getLatestEdits, + processArticles, + getArticleNormalized, + getArticlesIndexes, + getAction, + filterFakeAuthors, + getArticleBlackListByArticleId, + getArticleBlackListByBlockHeight, +} = VM.require('chatter.cheddar.near/widget/lib.article') const { displayTestsSyncResults, displayTestsAsyncResults } = VM.require( - "communityvoice.ndctools.near/widget/tests.lib.tester" -); + 'chatter.cheddar.near/widget/tests.lib.tester' +) -const isTest = false; -const baseAction = "sayALotArticle"; -const currentVersion = "v0.0.4"; // EDIT: Set version +const isTest = false +const baseAction = 'sayALotArticle' +const currentVersion = 'v0.0.4' // EDIT: Set version -const prodAction = `${baseAction}_v${currentVersion}`; -const testAction = `test_${prodAction}`; -const versionsBaseActions = isTest ? `test_${baseAction}` : baseAction; -const action = isTest ? testAction : prodAction; +const prodAction = `${baseAction}_v${currentVersion}` +const testAction = `test_${prodAction}` +const versionsBaseActions = isTest ? `test_${baseAction}` : baseAction +const action = isTest ? testAction : prodAction // const articles = getArticlesIndexes(action, "main").then((response) => // console.log("articlesIndexes: ", response) @@ -35,430 +35,433 @@ const action = isTest ? testAction : prodAction; // }; function testLatestEditsRepeatedArticle() { - const fnName = "testLatestEdits"; - const articleIndexes = [ - { - accountId: "test.near", - blockHeight: 191891118, - value: { - type: "md", - id: "test.near-1651981918", - }, - }, - { - accountId: "test.near", - blockHeight: 191891117, - value: { - type: "md", - id: "test.near-1651981918", - }, - }, - { - accountId: "test.near", - blockHeight: 191891116, - value: { - type: "md", - id: "test.near-1651981919", - }, - }, - ]; + const fnName = 'testLatestEdits' + const articleIndexes = [ + { + accountId: 'test.near', + blockHeight: 191891118, + value: { + type: 'md', + id: 'test.near-1651981918', + }, + }, + { + accountId: 'test.near', + blockHeight: 191891117, + value: { + type: 'md', + id: 'test.near-1651981918', + }, + }, + { + accountId: 'test.near', + blockHeight: 191891116, + value: { + type: 'md', + id: 'test.near-1651981919', + }, + }, + ] + + let functionLatestEdit + try { + functionLatestEdit = getLatestEdits(articleIndexes) + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } - let functionLatestEdit; - try { - functionLatestEdit = getLatestEdits(articleIndexes); - } catch (err) { + const expectedLatestEdit = [ + { + accountId: 'test.near', + blockHeight: 191891118, + value: { + type: 'md', + id: 'test.near-1651981918', + }, + }, + { + accountId: 'test.near', + blockHeight: 191891116, + value: { + type: 'md', + id: 'test.near-1651981919', + }, + }, + ] + + const isError = + JSON.stringify(functionLatestEdit) !== + JSON.stringify(expectedLatestEdit) return { - isError: true, - msg: err.message, - fnName, - }; - } - - const expectedLatestEdit = [ - { - accountId: "test.near", - blockHeight: 191891118, - value: { - type: "md", - id: "test.near-1651981918", - }, - }, - { - accountId: "test.near", - blockHeight: 191891116, - value: { - type: "md", - id: "test.near-1651981919", - }, - }, - ]; - - const isError = - JSON.stringify(functionLatestEdit) !== JSON.stringify(expectedLatestEdit); - return { - isError: isError, - msg: isError - ? [ - `Items don't match.`, - `Get: ${JSON.stringify(functionLatestEdit)}`, - `Expected: ${JSON.stringify(expectedLatestEdit)}`, - ] - : "", - fnName, - }; - // return JSON.stringify(functionLatestEdit) === JSON.stringify(expectedLatestEdit) + isError: isError, + msg: isError + ? [ + `Items don't match.`, + `Get: ${JSON.stringify(functionLatestEdit)}`, + `Expected: ${JSON.stringify(expectedLatestEdit)}`, + ] + : '', + fnName, + } + // return JSON.stringify(functionLatestEdit) === JSON.stringify(expectedLatestEdit) } function testLatestEditEmptyIndex() { - const fnName = "testLatestEditEmptyIndex"; - const articleIndexes = []; - let functionLatestEdit; - try { - functionLatestEdit = getLatestEdits(articleIndexes); - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } - - const expectedLatestEdit = []; - const isError = - JSON.stringify(functionLatestEdit) !== JSON.stringify(expectedLatestEdit); - return { - isError: isError, - msg: isError - ? `Items don't match output ${functionLatestEdit}, expected ${expectedLatestEdit}` - : "", - fnName, - }; -} + const fnName = 'testLatestEditEmptyIndex' + const articleIndexes = [] + let functionLatestEdit + try { + functionLatestEdit = getLatestEdits(articleIndexes) + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } -function testGetArticleNormalized() { - //NEED CHECK AFTER FIX OF getArticleNormalized - - const fnName = "testGetArticleNormalized"; - const articleIndex = realArticleIndexInMainnet; - let articleNormalized; - try { - getArticleNormalized(articleIndex, action).then((response) => { - const expectedNormalizedArticle = []; - const isError = - JSON.stringify(response) !== JSON.stringify(expectedLatestEdit); - return { + const expectedLatestEdit = [] + const isError = + JSON.stringify(functionLatestEdit) !== + JSON.stringify(expectedLatestEdit) + return { isError: isError, msg: isError - ? `Items don't match output ${articleNormalized}, expected ${expectedLatestEdit}` - : "", + ? `Items don't match output ${functionLatestEdit}, expected ${expectedLatestEdit}` + : '', fnName, - }; - }); - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } + } } -function testGetActionInIsTestPassingParameters() { - const fnName = "testGetActionInIsTestPassingParameters"; - const config = { baseActions: { article: versionsBaseActions }, isTest }; - try { - const resultAction = getAction(currentVersion, config); - const expectedAction = baseAction; +function testGetArticleNormalized() { + //NEED CHECK AFTER FIX OF getArticleNormalized - const isError = resultAction === expectedAction; + const fnName = 'testGetArticleNormalized' + const articleIndex = realArticleIndexInMainnet + let articleNormalized + try { + getArticleNormalized(articleIndex, action).then((response) => { + const expectedNormalizedArticle = [] + const isError = + JSON.stringify(response) !== JSON.stringify(expectedLatestEdit) + return { + isError: isError, + msg: isError + ? `Items don't match output ${articleNormalized}, expected ${expectedLatestEdit}` + : '', + fnName, + } + }) + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } +} - return { - isError: isError, - msg: isError - ? [ - `Not returning the expected action.`, - `Returns: ${resultAction}`, - `Expected: ${expectedAction}`, - ] - : "", - fnName, - }; - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } +function testGetActionInIsTestPassingParameters() { + const fnName = 'testGetActionInIsTestPassingParameters' + const config = { baseActions: { article: versionsBaseActions }, isTest } + try { + const resultAction = getAction(currentVersion, config) + const expectedAction = baseAction + + const isError = resultAction === expectedAction + + return { + isError: isError, + msg: isError + ? [ + `Not returning the expected action.`, + `Returns: ${resultAction}`, + `Expected: ${expectedAction}`, + ] + : '', + fnName, + } + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } } function testGetActionInIsTestNotPassingParameters() { - const fnName = "testGetActionInIsTestNotPassingParameters"; - try { - const resultAction = getAction(); - const expectedAction = baseAction; - - const isError = resultAction === expectedAction; - - return { - isError: isError, - msg: isError - ? [ - `Not returning the expected action.`, - `Returns: ${resultAction}`, - `Expected: ${expectedAction}`, - ] - : "", - fnName, - }; - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } + const fnName = 'testGetActionInIsTestNotPassingParameters' + try { + const resultAction = getAction() + const expectedAction = baseAction + + const isError = resultAction === expectedAction + + return { + isError: isError, + msg: isError + ? [ + `Not returning the expected action.`, + `Returns: ${resultAction}`, + `Expected: ${expectedAction}`, + ] + : '', + fnName, + } + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } } function testGetActionPassingParameters() { - const fnName = "testGetActionPassingParameters"; - const config = { baseActions: { article: baseAction }, isTest: false }; - try { - const resultAction = getAction(currentVersion, config); - const expectedAction = baseAction; - - const isError = resultAction === expectedAction; - - return { - isError: isError, - msg: isError - ? [ - `Not returning the expected action.`, - `Returns: ${resultAction}`, - `Expected: ${expectedAction}`, - ] - : "", - fnName, - }; - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } + const fnName = 'testGetActionPassingParameters' + const config = { baseActions: { article: baseAction }, isTest: false } + try { + const resultAction = getAction(currentVersion, config) + const expectedAction = baseAction + + const isError = resultAction === expectedAction + + return { + isError: isError, + msg: isError + ? [ + `Not returning the expected action.`, + `Returns: ${resultAction}`, + `Expected: ${expectedAction}`, + ] + : '', + fnName, + } + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } } function testGetActionNotPassingParameters() { - const fnName = "testGetActionNotPassingParameters"; - try { - const resultAction = getAction(); - const expectedAction = baseAction; - - const isError = resultAction === expectedAction; - - return { - isError: isError, - msg: isError - ? [ - `Not returning the expected action.`, - `Returns: ${resultAction}`, - `Expected: ${expectedAction}`, - ] - : "", - fnName, - }; - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } + const fnName = 'testGetActionNotPassingParameters' + try { + const resultAction = getAction() + const expectedAction = baseAction + + const isError = resultAction === expectedAction + + return { + isError: isError, + msg: isError + ? [ + `Not returning the expected action.`, + `Returns: ${resultAction}`, + `Expected: ${expectedAction}`, + ] + : '', + fnName, + } + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } } function testFilterFakeAuthorsAuthorDoesntMatch() { - const fnName = "testFilterFakeAuthors"; - const articleData = { - author: "a", - }; - const articleIndexData = { - accountId: "b", - }; - let result; - try { - result = filterFakeAuthors(articleData, articleIndexData); - } catch (err) { + const fnName = 'testFilterFakeAuthors' + const articleData = { + author: 'a', + } + const articleIndexData = { + accountId: 'b', + } + let result + try { + result = filterFakeAuthors(articleData, articleIndexData) + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } + + const expectedLatestEdit = undefined + + const isError = !(result === expectedLatestEdit) return { - isError: true, - msg: err.message, - fnName, - }; - } - - const expectedLatestEdit = undefined; - - const isError = !(result === expectedLatestEdit); - return { - isError: isError, - msg: isError - ? `This item should be filtered and it's not been filtered` - : "", - fnName, - }; + isError: isError, + msg: isError + ? `This item should be filtered and it's not been filtered` + : '', + fnName, + } } function testFilterFakeAuthorsMatchAuthor() { - const fnName = "testFilterFakeAuthors"; - const articleData = { - author: "a", - }; - const articleIndexData = { - accountId: "a", - }; - let result; - try { - result = filterFakeAuthors(articleData, articleIndexData); - } catch (err) { + const fnName = 'testFilterFakeAuthors' + const articleData = { + author: 'a', + } + const articleIndexData = { + accountId: 'a', + } + let result + try { + result = filterFakeAuthors(articleData, articleIndexData) + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } + + const expectedLatestEdit = articleData + + const isError = + JSON.stringify(result) !== JSON.stringify(expectedLatestEdit) return { - isError: true, - msg: err.message, - fnName, - }; - } - - const expectedLatestEdit = articleData; - - const isError = JSON.stringify(result) !== JSON.stringify(expectedLatestEdit); - return { - isError: isError, - msg: isError - ? `This item should not be filtered and it's been filtered` - : "", - fnName, - }; + isError: isError, + msg: isError + ? `This item should not be filtered and it's been filtered` + : '', + fnName, + } } function testGetArticleBlackListByArticleIdReturnValidAccountIds() { - const fnName = "testGetArticleBlackListByArticleIdReturnValidAccountIds"; + const fnName = 'testGetArticleBlackListByArticleIdReturnValidAccountIds' - let result; - try { - result = getArticleBlackListByArticleId(); - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } - - const arrayArticleIdIsValid = result.map((articleId) => { - //articleId example: "silkking.near-1696797896796" - const splitedArticleId = articleId.split("-"); - - const timeStampPartOfArticleId = splitedArticleId.pop(); - - let userNamePartOfArticleId; - if (splitedArticleId.length === 1) { - userNamePartOfArticleId = splitedArticleId; - } else { - userNamePartOfArticleId = splitedArticleId.join("-"); + let result + try { + result = getArticleBlackListByArticleId() + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } } - const userNameRegEx = /^[a-zA-Z0-9._-]/; + const arrayArticleIdIsValid = result.map((articleId) => { + //articleId example: "silkking.near-1696797896796" + const splitedArticleId = articleId.split('-') + + const timeStampPartOfArticleId = splitedArticleId.pop() + + let userNamePartOfArticleId + if (splitedArticleId.length === 1) { + userNamePartOfArticleId = splitedArticleId + } else { + userNamePartOfArticleId = splitedArticleId.join('-') + } + + const userNameRegEx = /^[a-zA-Z0-9._-]/ - const isTimeStampANumber = !isNaN(Number(timeStampPartOfArticleId)); - const isValidUserName = userNameRegEx.test(userNamePartOfArticleId); + const isTimeStampANumber = !isNaN(Number(timeStampPartOfArticleId)) + const isValidUserName = userNameRegEx.test(userNamePartOfArticleId) - return isTimeStampANumber && isValidUserName; - }); + return isTimeStampANumber && isValidUserName + }) - const isError = arrayArticleIdIsValid.includes(false); + const isError = arrayArticleIdIsValid.includes(false) - return { - isError: isError, - msg: isError - ? `One or more articleId passed does not have the correct format` - : "", - fnName, - }; + return { + isError: isError, + msg: isError + ? `One or more articleId passed does not have the correct format` + : '', + fnName, + } } function testGetArticleBlackListByBlockHeightReturnsNumbers() { - const fnName = "testGetArticleBlackListByBlockHeightReturnsNumbers"; - let result; - try { - result = getArticleBlackListByBlockHeight(); - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } + const fnName = 'testGetArticleBlackListByBlockHeightReturnsNumbers' + let result + try { + result = getArticleBlackListByBlockHeight() + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } - const arrayIsResultANumber = result.map((blockHeihgt) => { - const isResultANumber = !isNaN(Number(blockHeihgt)); + const arrayIsResultANumber = result.map((blockHeihgt) => { + const isResultANumber = !isNaN(Number(blockHeihgt)) - return isResultANumber; - }); + return isResultANumber + }) - const isError = arrayIsResultANumber.includes(false); + const isError = arrayIsResultANumber.includes(false) - return { - isError: isError, - msg: isError ? `One or more blockHeights passed are not numbers` : "", - fnName, - }; + return { + isError: isError, + msg: isError ? `One or more blockHeights passed are not numbers` : '', + fnName, + } } async function testGetArticlesIndexes() { - const fnName = "testGetArticlesIndexes"; - function doResponseHavePropperIndexStructure(res) { - return res - .map((articleIndex) => { - return ( - typeof articleIndex.blockHeight === "number" && - typeof articleIndex.accountId === "string" && - typeof articleIndex.value.id === "string" - ); - }) - .includes(false); - } - const articlesIndexes = getArticlesIndexes(getAction(), "main"); - - let isError = false; - return articlesIndexes.then((res) => { - try { - if (Array.isArray(res) && res.length === 0) { - isError = false; - } else if (doResponseHavePropperIndexStructure(res)) { - isError = true; - } - - return { - isError, - msg: isError - ? [ - `Articles indexes doesn't match.`, - `Returns: ${res}`, - `Expected: ${expectedResult}`, - ] - : "", - fnName, - }; - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; + const fnName = 'testGetArticlesIndexes' + function doResponseHavePropperIndexStructure(res) { + return res + .map((articleIndex) => { + return ( + typeof articleIndex.blockHeight === 'number' && + typeof articleIndex.accountId === 'string' && + typeof articleIndex.value.id === 'string' + ) + }) + .includes(false) } - }); + const articlesIndexes = getArticlesIndexes(getAction(), 'main') + + let isError = false + return articlesIndexes.then((res) => { + try { + if (Array.isArray(res) && res.length === 0) { + isError = false + } else if (doResponseHavePropperIndexStructure(res)) { + isError = true + } + + return { + isError, + msg: isError + ? [ + `Articles indexes doesn't match.`, + `Returns: ${res}`, + `Expected: ${expectedResult}`, + ] + : '', + fnName, + } + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } + }) } -const [asyncComponent, setAsyncComponent] = useState(

Loading...

); +const [asyncComponent, setAsyncComponent] = useState(

Loading...

) // displayTestsAsyncResults(/*[ // { @@ -473,73 +476,74 @@ const [asyncComponent, setAsyncComponent] = useState(

Loading...

); // }); displayTestsAsyncResults([ - { - fnName: "testGetArticlesIndexes", - fn: testGetArticlesIndexes, - description: "Should get an array of article index", - }, + { + fnName: 'testGetArticlesIndexes', + fn: testGetArticlesIndexes, + description: 'Should get an array of article index', + }, ]).then((res) => { - setAsyncComponent(res); -}); + setAsyncComponent(res) +}) return ( - <> - {displayTestsSyncResults([ - // { - // fnName: "testLatestEditsRepeatedArticle", - // fn: testLatestEditsRepeatedArticle, - // description: "Should remove repeated articles keeping newest", - // }, - { - fnName: "testLatestEditEmptyIndex", - fn: testLatestEditEmptyIndex, - }, - // { - // fnName: "testGetArticleNormalized", - // fn: testGetArticleNormalized, - // }, - { - fnName: "testGetActionInIsTestPassingParameters", - fn: testGetActionInIsTestPassingParameters, - description: "Should get the propper action", - }, - { - fnName: "testGetActionInIsTestNotPassingParameters", - fn: testGetActionInIsTestNotPassingParameters, - description: "Should get the propper action", - }, - { - fnName: "testGetActionPassingParameters", - fn: testGetActionPassingParameters, - description: "Should get the propper action", - }, - { - fnName: "testGetActionNotPassingParameters", - fn: testGetActionNotPassingParameters, - description: "Should get the propper action", - }, - { - fnName: "testFilterFakeAuthorsMatchAuthor", - fn: testFilterFakeAuthorsMatchAuthor, - description: "Test if filtering is working propperly", - }, - { - fnName: "testFilterFakeAuthorsAuthorDoesntMatch", - fn: testFilterFakeAuthorsAuthorDoesntMatch, - description: "Test if filtering is working propperly", - }, - { - fnName: "testGetArticleBlackListByArticleIdReturnValidAccountIds", - fn: testGetArticleBlackListByArticleIdReturnValidAccountIds, - description: - "Test if getArticleBlackListByArticle returns valid articleId's", - }, - { - fnName: "testGetArticleBlackListByBlockHeightReturnsNumbers", - fn: testGetArticleBlackListByBlockHeightReturnsNumbers, - description: "Test if getArticleBlackListByBlockHeight returns numbers", - }, - ])} - {asyncComponent} - -); + <> + {displayTestsSyncResults([ + // { + // fnName: "testLatestEditsRepeatedArticle", + // fn: testLatestEditsRepeatedArticle, + // description: "Should remove repeated articles keeping newest", + // }, + { + fnName: 'testLatestEditEmptyIndex', + fn: testLatestEditEmptyIndex, + }, + // { + // fnName: "testGetArticleNormalized", + // fn: testGetArticleNormalized, + // }, + { + fnName: 'testGetActionInIsTestPassingParameters', + fn: testGetActionInIsTestPassingParameters, + description: 'Should get the propper action', + }, + { + fnName: 'testGetActionInIsTestNotPassingParameters', + fn: testGetActionInIsTestNotPassingParameters, + description: 'Should get the propper action', + }, + { + fnName: 'testGetActionPassingParameters', + fn: testGetActionPassingParameters, + description: 'Should get the propper action', + }, + { + fnName: 'testGetActionNotPassingParameters', + fn: testGetActionNotPassingParameters, + description: 'Should get the propper action', + }, + { + fnName: 'testFilterFakeAuthorsMatchAuthor', + fn: testFilterFakeAuthorsMatchAuthor, + description: 'Test if filtering is working propperly', + }, + { + fnName: 'testFilterFakeAuthorsAuthorDoesntMatch', + fn: testFilterFakeAuthorsAuthorDoesntMatch, + description: 'Test if filtering is working propperly', + }, + { + fnName: 'testGetArticleBlackListByArticleIdReturnValidAccountIds', + fn: testGetArticleBlackListByArticleIdReturnValidAccountIds, + description: + "Test if getArticleBlackListByArticle returns valid articleId's", + }, + { + fnName: 'testGetArticleBlackListByBlockHeightReturnsNumbers', + fn: testGetArticleBlackListByBlockHeightReturnsNumbers, + description: + 'Test if getArticleBlackListByBlockHeight returns numbers', + }, + ])} + {asyncComponent} + +) diff --git a/src/tests/testLibComment.jsx b/src/tests/testLibComment.jsx index c64a5af..17b2828 100644 --- a/src/tests/testLibComment.jsx +++ b/src/tests/testLibComment.jsx @@ -1,19 +1,21 @@ -const { functionsToTest } = VM.require("communityvoice.ndctools.near/widget/lib.comment"); +const { functionsToTest } = VM.require( + 'chatter.cheddar.near/widget/lib.comment' +) const { displayTestsSyncResults, displayTestsAsyncResults } = VM.require( - "communityvoice.ndctools.near/widget/tests.lib.tester" -); + 'chatter.cheddar.near/widget/tests.lib.tester' +) //=======================================================================Start consts======================================================================= -const isTest = true; -const baseAction = "sayALotComment"; -const currentVersion = "v0.0.3"; // EDIT: Set version +const isTest = true +const baseAction = 'sayALotComment' +const currentVersion = 'v0.0.3' // EDIT: Set version -const prodAction = `${baseAction}_${currentVersion}`; -const testAction = `test_${prodAction}`; -const action = isTest ? testAction : prodAction; +const prodAction = `${baseAction}_${currentVersion}` +const testAction = `test_${prodAction}` +const action = isTest ? testAction : prodAction -const config = { baseActions: { comment: baseAction }, isTest }; -const userNameRegEx = /^[a-zA-Z0-9._-]/; +const config = { baseActions: { comment: baseAction }, isTest } +const userNameRegEx = /^[a-zA-Z0-9._-]/ // const commentIdToTestSplit1 = "c_NEAR.near-12312323123"; // // const commentIdToTestSplit2 = "c-NEAR-near-12312323123" @@ -44,215 +46,221 @@ const userNameRegEx = /^[a-zA-Z0-9._-]/; // testGetSplittedCommentId(); function doesCommentIdHavePropperStructure(id) { - let splittedCommentId = functionsToTest.getSplittedCommentIdV0_0_3(id); - const timeStampPartOfCommentId = splittedCommentId.pop(); + let splittedCommentId = functionsToTest.getSplittedCommentIdV0_0_3(id) + const timeStampPartOfCommentId = splittedCommentId.pop() - const commentIdPrefix = splittedCommentId.shift(); + const commentIdPrefix = splittedCommentId.shift() - const commentIdUserNamePart = splittedCommentId; + const commentIdUserNamePart = splittedCommentId - const isTimeStampANumber = !isNaN(Number(timeStampPartOfCommentId)); - const isPrefixCorrect = commentIdPrefix === "c/"; - const isValidUserName = userNameRegEx.test(commentIdUserNamePart); + const isTimeStampANumber = !isNaN(Number(timeStampPartOfCommentId)) + const isPrefixCorrect = commentIdPrefix === 'c/' + const isValidUserName = userNameRegEx.test(commentIdUserNamePart) - if(!isTimeStampANumber) { - console.log("Timestamp is not a number: ", timeStampPartOfCommentId) - } + if (!isTimeStampANumber) { + console.log('Timestamp is not a number: ', timeStampPartOfCommentId) + } - if(!isPrefixCorrect) { - console.log("Prefix is not correct. Expected: 'c/'. Result: ", commentIdPrefix) - } + if (!isPrefixCorrect) { + console.log( + "Prefix is not correct. Expected: 'c/'. Result: ", + commentIdPrefix + ) + } - if(!isValidUserName) { - console.log("Not a valid userName: ", commentIdUserNamePart) - } + if (!isValidUserName) { + console.log('Not a valid userName: ', commentIdUserNamePart) + } - return isTimeStampANumber && isPrefixCorrect && isValidUserName; + return isTimeStampANumber && isPrefixCorrect && isValidUserName } function doesArticleIdHavePropperStructure(articleId) { - let splittedArticleId = articleId.includes("/") - ? articleId.split("/") - : articleId.split("-"); + let splittedArticleId = articleId.includes('/') + ? articleId.split('/') + : articleId.split('-') - const timeStampPartOfArticleId = splittedArticleId.pop(); + const timeStampPartOfArticleId = splittedArticleId.pop() - const articleIdUserNamePart = articleId.includes("/") - ? splittedArticleId - : splittedArticleId.join("-"); + const articleIdUserNamePart = articleId.includes('/') + ? splittedArticleId + : splittedArticleId.join('-') - const isTimeStampANumber = !isNaN(Number(timeStampPartOfArticleId)); - const isValidUserName = userNameRegEx.test(articleIdUserNamePart); + const isTimeStampANumber = !isNaN(Number(timeStampPartOfArticleId)) + const isValidUserName = userNameRegEx.test(articleIdUserNamePart) - return isTimeStampANumber && isValidUserName; + return isTimeStampANumber && isValidUserName } function doesRootIdHaveAValidFormat(rootId) { - if (rootId.startsWith("c_")) { - return doesCommentIdHavePropperStructure(rootId); - } else { - return doesArticleIdHavePropperStructure(rootId); - } + if (rootId.startsWith('c_')) { + return doesCommentIdHavePropperStructure(rootId) + } else { + return doesArticleIdHavePropperStructure(rootId) + } } function isResponseStructureWrong(res) { - // const resExample = [ - // { - // accountId: "ayelen.near", - // blockHeight: 109989354, - // value: { - // type: "md", - // comment: { - // text: "a verr", - // timestamp: 1704812207512, - // commentId: "c_ayelen.near-1704812207512", - // rootId: "ayelen.near-1699406465524", - // }, - // }, - // isEdition: true, - // }, - // ]; - - if (Array.isArray(res) && res.length === 0) { - console.log("res is an empty array"); - return false; - } - - let errorInStructure = false; - for (let i = 0; i < res.length; i++) { - const commentData = res[i]; - const commentAccountId = commentData.accountId; - const commentBlockHeight = Number(commentData.blockHeight); - const isEdition = commentData.isEdition; - - if (typeof commentAccountId !== "string") { - console.log(`In the commentData of index ${i} the accountId is not a string`); - errorInStructure = true; - } else if (!(typeof commentBlockHeight === "number")) { - console.log( - `In the commentData of index ${i} the blockHeight is not a Number` - ); - errorInStructure = true; - } else if (isEdition && typeof isEdition !== "boolean") { - console.log( - `In the commentData of index ${i} the isEdition property is not a booean` - ); - errorInStructure = true; - } else if ( - commentData.value.metadata && - !doesCommentIdHavePropperStructure(commentData.value.metadata.id) - ) { - console.log( - `In the commentData of index ${i} doesCommentIdHavePropperStructure is returning false` - ); - errorInStructure = true; - } else if (typeof commentData.value.metadata.author !== "string") { - console.log( - `In the commentData of index ${i} the author property in the metadata is not a string`, commentData - ); - errorInStructure = true; - } else if ( - typeof commentData.value.metadata.createdTimestamp !== "number" || - typeof commentData.value.metadata.lastEditTimestamp !== "number" - ) { - console.log( - `In the commentData of index ${i} the timestamps in the metadata are not a number` - ); - errorInStructure = true; - } else if (typeof commentData.value.metadata.versionKey !== "string") { - console.log( - `In the commentData of index ${i} the versionKey in the metadata is not a string` - ); - errorInStructure = true; - } else if (!doesRootIdHaveAValidFormat(commentData.value.metadata.rootId)) { - console.log( - `In the commentData of index ${i} doesRootIdHaveAValidFormat is returning false` - ); - errorInStructure = true; + // const resExample = [ + // { + // accountId: "ayelen.near", + // blockHeight: 109989354, + // value: { + // type: "md", + // comment: { + // text: "a verr", + // timestamp: 1704812207512, + // commentId: "c_ayelen.near-1704812207512", + // rootId: "ayelen.near-1699406465524", + // }, + // }, + // isEdition: true, + // }, + // ]; + + if (Array.isArray(res) && res.length === 0) { + console.log('res is an empty array') + return false } - } - return errorInStructure; + let errorInStructure = false + for (let i = 0; i < res.length; i++) { + const commentData = res[i] + const commentAccountId = commentData.accountId + const commentBlockHeight = Number(commentData.blockHeight) + const isEdition = commentData.isEdition + + if (typeof commentAccountId !== 'string') { + console.log( + `In the commentData of index ${i} the accountId is not a string` + ) + errorInStructure = true + } else if (!(typeof commentBlockHeight === 'number')) { + console.log( + `In the commentData of index ${i} the blockHeight is not a Number` + ) + errorInStructure = true + } else if (isEdition && typeof isEdition !== 'boolean') { + console.log( + `In the commentData of index ${i} the isEdition property is not a booean` + ) + errorInStructure = true + } else if ( + commentData.value.metadata && + !doesCommentIdHavePropperStructure(commentData.value.metadata.id) + ) { + console.log( + `In the commentData of index ${i} doesCommentIdHavePropperStructure is returning false` + ) + errorInStructure = true + } else if (typeof commentData.value.metadata.author !== 'string') { + console.log( + `In the commentData of index ${i} the author property in the metadata is not a string`, + commentData + ) + errorInStructure = true + } else if ( + typeof commentData.value.metadata.createdTimestamp !== 'number' || + typeof commentData.value.metadata.lastEditTimestamp !== 'number' + ) { + console.log( + `In the commentData of index ${i} the timestamps in the metadata are not a number` + ) + errorInStructure = true + } else if (typeof commentData.value.metadata.versionKey !== 'string') { + console.log( + `In the commentData of index ${i} the versionKey in the metadata is not a string` + ) + errorInStructure = true + } else if ( + !doesRootIdHaveAValidFormat(commentData.value.metadata.rootId) + ) { + console.log( + `In the commentData of index ${i} doesRootIdHaveAValidFormat is returning false` + ) + errorInStructure = true + } + } + + return errorInStructure } //=======================================================================End lib functions========================================================================= //=======================================================================Start tests======================================================================= -function testCreateComment() { - -} +function testCreateComment() {} async function testGetComments() { - const fnName = "testGetComments"; - const articleId = "ayelen.near-1699406465524"; - - // const articleId = "test-1"; - - // const articleId = "ayelen.near-1708014044488"; - // const articleId = "ayelen.near-1696980478217"; - // const articleId = "ayelen.near-1707407808397"; - // const articleId = "ayelen.near-1697150785479"; - // const articleId = "ayelen.near-1697152421776"; - - const getCommentsDataResult = functionsToTest.getComments(articleId, config); - - let isError = false; - let msg = ""; - return getCommentsDataResult.then((res) => { - try { - if (isResponseStructureWrong(res)) { - isError = true; - msg = [ - "One or more elements on the array have an invalid structure", - "Expected structure example: [{'accountId': accountId, 'blockHeight': number, isEdition: bool, 'value': 'comment': {'commentId': 'c-accountId-timestamp', rootId: commentId || articleId, text: string, timestamp: number}}]", - `Returned: ${JSON.stringify(res)}`, - ]; - } - - return { - isError, - msg, - fnName, - }; - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } - }); + const fnName = 'testGetComments' + const articleId = 'ayelen.near-1699406465524' + + // const articleId = "test-1"; + + // const articleId = "ayelen.near-1708014044488"; + // const articleId = "ayelen.near-1696980478217"; + // const articleId = "ayelen.near-1707407808397"; + // const articleId = "ayelen.near-1697150785479"; + // const articleId = "ayelen.near-1697152421776"; + + const getCommentsDataResult = functionsToTest.getComments(articleId, config) + + let isError = false + let msg = '' + return getCommentsDataResult.then((res) => { + try { + if (isResponseStructureWrong(res)) { + isError = true + msg = [ + 'One or more elements on the array have an invalid structure', + "Expected structure example: [{'accountId': accountId, 'blockHeight': number, isEdition: bool, 'value': 'comment': {'commentId': 'c-accountId-timestamp', rootId: commentId || articleId, text: string, timestamp: number}}]", + `Returned: ${JSON.stringify(res)}`, + ] + } + + return { + isError, + msg, + fnName, + } + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } + }) } //========================================================================End tests================================================================================ -const [asyncComponent, setAsyncComponent] = useState(

Loading...

); +const [asyncComponent, setAsyncComponent] = useState(

Loading...

) displayTestsAsyncResults([ - // { - // fnName: "testGetCommentsData", - // fn: testGetCommentsData, - // description: - // "Should get the upVotesData asociated with the article that have the passed ID", - // }, - { - fnName: "testGetComments", - fn: testGetComments, - description: - "Should get the commentsData asociated with the article that have the passed ID", - }, + // { + // fnName: "testGetCommentsData", + // fn: testGetCommentsData, + // description: + // "Should get the upVotesData asociated with the article that have the passed ID", + // }, + { + fnName: 'testGetComments', + fn: testGetComments, + description: + 'Should get the commentsData asociated with the article that have the passed ID', + }, ]).then((res) => { - setAsyncComponent(res); -}); + setAsyncComponent(res) +}) return ( - <> - {displayTestsSyncResults([ - // { - // fnName: "testComposeDataIsWorkingAsExpected", - // fn: testComposeDataIsWorkingAsExpected, - // description: "Check if the structure is as the expected one", - // }, - ])} - {asyncComponent} - -); + <> + {displayTestsSyncResults([ + // { + // fnName: "testComposeDataIsWorkingAsExpected", + // fn: testComposeDataIsWorkingAsExpected, + // description: "Check if the structure is as the expected one", + // }, + ])} + {asyncComponent} + +) diff --git a/src/tests/testLibSBT.jsx b/src/tests/testLibSBT.jsx index cdfab40..ab409e3 100644 --- a/src/tests/testLibSBT.jsx +++ b/src/tests/testLibSBT.jsx @@ -1,106 +1,108 @@ const { getSBTWhiteList, isValidUser, getUserSBTs } = VM.require( - "communityvoice.ndctools.near/widget/lib.SBT" -); + 'chatter.cheddar.near/widget/lib.SBT' +) const { displayTestsResults } = VM.require( - "communityvoice.ndctools.near/widget/tests.lib.tester" -); + 'chatter.cheddar.near/widget/tests.lib.tester' +) function testGetSBTWhiteListWorking() { - const fnName = "testGetSBTWhiteListWorking"; + const fnName = 'testGetSBTWhiteListWorking' - let functionGetSBTWhiteList; - try { - functionGetSBTWhiteList = getSBTWhiteList(); - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } + let functionGetSBTWhiteList + try { + functionGetSBTWhiteList = getSBTWhiteList() + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } - const expectedWhiteList = [ - { - value: "fractal.i-am-human.near - class 1", - title: "General", - default: true, - }, - { value: "community.i-am-human.near - class 1", title: "OG" }, - { value: "community.i-am-human.near - class 2", title: "Contributor" }, - { - value: "community.i-am-human.near - class 3", - title: "Core Contributor", - }, - { value: "elections.ndc-gwg.near - class 2", title: "HoM" }, - { value: "elections.ndc-gwg.near - class 3", title: "CoA" }, - { value: "elections.ndc-gwg.near - class 4", title: "TC" }, - { value: "public", title: "Public" }, - ]; + const expectedWhiteList = [ + { + value: 'fractal.i-am-human.near - class 1', + title: 'General', + default: true, + }, + { value: 'community.i-am-human.near - class 1', title: 'OG' }, + { value: 'community.i-am-human.near - class 2', title: 'Contributor' }, + { + value: 'community.i-am-human.near - class 3', + title: 'Core Contributor', + }, + { value: 'elections.ndc-gwg.near - class 2', title: 'HoM' }, + { value: 'elections.ndc-gwg.near - class 3', title: 'CoA' }, + { value: 'elections.ndc-gwg.near - class 4', title: 'TC' }, + { value: 'public', title: 'Public' }, + ] - const isError = - JSON.stringify(functionGetSBTWhiteList) !== - JSON.stringify(expectedWhiteList); - return { - isError: isError, - msg: isError - ? `Items don't match output ${functionGetSBTWhiteList}, expected ${expectedWhiteList}` - : "", - fnName, - }; + const isError = + JSON.stringify(functionGetSBTWhiteList) !== + JSON.stringify(expectedWhiteList) + return { + isError: isError, + msg: isError + ? `Items don't match output ${functionGetSBTWhiteList}, expected ${expectedWhiteList}` + : '', + fnName, + } } function testIsValidUserReturningCorrectly() { - const fnName = "testIsValidUserReturningCorrectly"; + const fnName = 'testIsValidUserReturningCorrectly' - const selectedSBTToCheckCredentials = ["fractal.i-am-human.near - class 1"] + const selectedSBTToCheckCredentials = ['fractal.i-am-human.near - class 1'] - let functionIsValidUser; - try { - functionIsValidUser = isValidUser("blaze.near", selectedSBTToCheckCredentials); - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } + let functionIsValidUser + try { + functionIsValidUser = isValidUser( + 'blaze.near', + selectedSBTToCheckCredentials + ) + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } - const expectedBlazeCredentials04D03M2024Y = { - "fractal.i-am-human.near - class 1": true, - "community.i-am-human.near - class 1": true, - "community.i-am-human.near - class 2": true, - "community.i-am-human.near - class 3": false, - "elections.ndc-gwg.near - class 2": false, - "elections.ndc-gwg.near - class 3": true, - "elections.ndc-gwg.near - class 4": false, - "public": true, - }; + const expectedBlazeCredentials04D03M2024Y = { + 'fractal.i-am-human.near - class 1': true, + 'community.i-am-human.near - class 1': true, + 'community.i-am-human.near - class 2': true, + 'community.i-am-human.near - class 3': false, + 'elections.ndc-gwg.near - class 2': false, + 'elections.ndc-gwg.near - class 3': true, + 'elections.ndc-gwg.near - class 4': false, + public: true, + } - const isError = - functionIsValidUser !== - expectedBlazeCredentials04D03M2024Y[selectedSBTToCheckCredentials]; - return { - isError: isError, - msg: isError - ? `Blaze ${expectedBlazeCredentials04D03M2024Y[selectedSBTToCheckCredentials] ? "do" : "doesn't"} have the following SBT: ${selectedSBTToCheckCredentials} and it's returning ${functionIsValidUser}` - : "", - fnName, - }; + const isError = + functionIsValidUser !== + expectedBlazeCredentials04D03M2024Y[selectedSBTToCheckCredentials] + return { + isError: isError, + msg: isError + ? `Blaze ${expectedBlazeCredentials04D03M2024Y[selectedSBTToCheckCredentials] ? 'do' : "doesn't"} have the following SBT: ${selectedSBTToCheckCredentials} and it's returning ${functionIsValidUser}` + : '', + fnName, + } } return ( - <> - {displayTestsResults([ - { - fnName: "testGetSBTWhiteListWorking", - fn: testGetSBTWhiteListWorking, - }, - { - fnName: "testIsValidUserReturningCorrectly", - fn: testIsValidUserReturningCorrectly, - }, - - ])} - -); + <> + {displayTestsResults([ + { + fnName: 'testGetSBTWhiteListWorking', + fn: testGetSBTWhiteListWorking, + }, + { + fnName: 'testIsValidUserReturningCorrectly', + fn: testIsValidUserReturningCorrectly, + }, + ])} + +) diff --git a/src/tests/testLibUpVotes.jsx b/src/tests/testLibUpVotes.jsx index ee5b249..e6e3112 100644 --- a/src/tests/testLibUpVotes.jsx +++ b/src/tests/testLibUpVotes.jsx @@ -1,270 +1,274 @@ -const { functionsToTest } = VM.require("communityvoice.ndctools.near/widget/lib.upVotes"); +const { functionsToTest } = VM.require( + 'chatter.cheddar.near/widget/lib.upVotes' +) const { displayTestsSyncResults, displayTestsAsyncResults } = VM.require( - "communityvoice.ndctools.near/widget/tests.lib.tester" -); + 'chatter.cheddar.near/widget/tests.lib.tester' +) //=======================================================================Start testLibUpVotes consts======================================================================= -const isTest = true; -const baseAction = "sayALotUpVote"; -const currentVersion = "v0.0.2"; // EDIT: Set version +const isTest = true +const baseAction = 'sayALotUpVote' +const currentVersion = 'v0.0.2' // EDIT: Set version -const prodAction = `${baseAction}_${currentVersion}`; -const testAction = `test_${prodAction}`; -const action = isTest ? testAction : prodAction; +const prodAction = `${baseAction}_${currentVersion}` +const testAction = `test_${prodAction}` +const action = isTest ? testAction : prodAction -const config = { baseActions: { upVote: baseAction }, isTest }; -const userNameRegEx = /^[a-zA-Z0-9._-]/; +const config = { baseActions: { upVote: baseAction }, isTest } +const userNameRegEx = /^[a-zA-Z0-9._-]/ //=======================================================================End testLibUpVotes consts========================================================================= //=======================================================================Start testLibUpVotes functions========================================================================= function doesUpVoteIdHavePropperStructure(upVoteData) { - const upVoteId = upVoteData.value.metadata.id; + const upVoteId = upVoteData.value.metadata.id - let splittedUpVoteId = upVoteId.includes("/") - ? upVoteId.split("/") - : upVoteId.split("-"); - const timeStampPartOfUpVoteId = splittedUpVoteId.pop(); + let splittedUpVoteId = upVoteId.includes('/') + ? upVoteId.split('/') + : upVoteId.split('-') + const timeStampPartOfUpVoteId = splittedUpVoteId.pop() - const upVoteIdPrefix = splittedUpVoteId.shift(); + const upVoteIdPrefix = splittedUpVoteId.shift() - const upVoteIdUserNamePart = upVoteId.includes("/") - ? splittedUpVoteId - : splittedUpVoteId.join("-"); + const upVoteIdUserNamePart = upVoteId.includes('/') + ? splittedUpVoteId + : splittedUpVoteId.join('-') - const isTimeStampANumber = !isNaN(Number(timeStampPartOfUpVoteId)); - const isPrefixCorrect = upVoteIdPrefix === "uv"; - const isValidUserName = userNameRegEx.test(upVoteIdUserNamePart); + const isTimeStampANumber = !isNaN(Number(timeStampPartOfUpVoteId)) + const isPrefixCorrect = upVoteIdPrefix === 'uv' + const isValidUserName = userNameRegEx.test(upVoteIdUserNamePart) - return isTimeStampANumber && isPrefixCorrect && isValidUserName; + return isTimeStampANumber && isPrefixCorrect && isValidUserName } function isResponseStructureWrong(res) { + if (Array.isArray(res) && res.length === 0) { + console.log('res is an empty array') + return false + } - if (Array.isArray(res) && res.length === 0) { - console.log("res is an empty array"); - return false; - } - - let errorInStructure = false; - for (let i = 0; i < res.length; i++) { - const upVoteData = res[i]; - const upVoteAccountId = upVoteData.accountId; - const upVoteBlockHeight = Number(upVoteData.blockHeight); - - if (typeof upVoteAccountId !== "string") { - console.log(`In the element of index ${i} the accountId is not a string`); - errorInStructure = true; - } else if (!(typeof upVoteBlockHeight === "number")) { - console.log( - `In the element of index ${i} the blockHeight is not a Number` - ); - errorInStructure = true; - } else if ( - upVoteData.value.metadata && - !doesUpVoteIdHavePropperStructure(upVoteData) && - typeof upVoteData.value.sbt !== "string" - ) { - console.log( - `In the element of index ${i} doesUpVoteIdHavePropperStructure is returning false` - ); - errorInStructure = true; + let errorInStructure = false + for (let i = 0; i < res.length; i++) { + const upVoteData = res[i] + const upVoteAccountId = upVoteData.accountId + const upVoteBlockHeight = Number(upVoteData.blockHeight) + + if (typeof upVoteAccountId !== 'string') { + console.log( + `In the element of index ${i} the accountId is not a string` + ) + errorInStructure = true + } else if (!(typeof upVoteBlockHeight === 'number')) { + console.log( + `In the element of index ${i} the blockHeight is not a Number` + ) + errorInStructure = true + } else if ( + upVoteData.value.metadata && + !doesUpVoteIdHavePropperStructure(upVoteData) && + typeof upVoteData.value.sbt !== 'string' + ) { + console.log( + `In the element of index ${i} doesUpVoteIdHavePropperStructure is returning false` + ) + errorInStructure = true + } } - } - return errorInStructure; + return errorInStructure } //========================================================================End testLibUpVotes functions=========================================================================== //=======================================================================Start testLibUpVotes tests============================================================================== async function testGetUpVotesData() { - const fnName = "testGetUpVotesData"; - // const articleId = "ayelen.near-1699406465524"; - const articleId = "test-1"; - - // const articleId = "ayelen.near-1708014044488"; - // const articleId = "ayelen.near-1696980478217"; - // const articleId = "ayelen.near-1707407808397"; - // const articleId = "ayelen.near-1697150785479"; - // const articleId = "ayelen.near-1697152421776"; - - const getUpVotesDataResult = functionsToTest.getUpVotesData( - articleId, - config - ); - - let isError = false; - let msg = ""; - return getUpVotesDataResult.then((res) => { - try { - if (isResponseStructureWrong(res)) { - isError = true; - msg = [ - "One or more elements on the array have an invalid structure", - "Expected structure example: [{'accountId': 'accountId', 'blockHeight': number, 'value': {'upVoteId': 'uv-accountId-timestamp', 'sbts': [sbt]}}]", - `Returned: ${JSON.stringify(res)}`, - ]; - } - - return { - isError, - msg, - fnName, - }; - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } - }); + const fnName = 'testGetUpVotesData' + // const articleId = "ayelen.near-1699406465524"; + const articleId = 'test-1' + + // const articleId = "ayelen.near-1708014044488"; + // const articleId = "ayelen.near-1696980478217"; + // const articleId = "ayelen.near-1707407808397"; + // const articleId = "ayelen.near-1697150785479"; + // const articleId = "ayelen.near-1697152421776"; + + const getUpVotesDataResult = functionsToTest.getUpVotesData( + articleId, + config + ) + + let isError = false + let msg = '' + return getUpVotesDataResult.then((res) => { + try { + if (isResponseStructureWrong(res)) { + isError = true + msg = [ + 'One or more elements on the array have an invalid structure', + "Expected structure example: [{'accountId': 'accountId', 'blockHeight': number, 'value': {'upVoteId': 'uv-accountId-timestamp', 'sbts': [sbt]}}]", + `Returned: ${JSON.stringify(res)}`, + ] + } + + return { + isError, + msg, + fnName, + } + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } + }) } function testComposeDataIsWorkingAsExpected() { - const fnName = "testComposeData"; - - const articleId = "ayelen.near-1699406465524"; - // const id = "ayelen.near-1708014044488"; - // const id = "ayelen.near-1696980478217"; - // const id = "ayelen.near-1707407808397"; - // const id = "ayelen.near-1697150785479"; - // const id = "ayelen.near-1697152421776"; - - const upVote = { - isDelete: false, - sbts: ["public"], //For the moment should only have 1 sbt in the array - }; - - let isError = false; - let msg = ""; - try { - const getComposeData = functionsToTest.composeData( - articleId, - upVote, - currentVersion, - config - ); - // expectedStructure = { - // index: { - // [action]: JSON.stringify({ - // key: articleId, - // value: { - // ...upVote, - // }, - // }), - // }, - // } - - const expectedStructure = { - index: { - [action]: JSON.stringify({ - key: articleId, - value: { - ...upVote, - }, - }), - }, - }; - - if (JSON.stringify(getComposeData) !== JSON.stringify(expectedStructure)) { - isError = true; - msg = [ - "The result is not matching the expected result", - `Expected: ${JSON.stringify(expectedStructure)}`, - `Result: ${JSON.stringify(getComposeData)}`, - ]; + const fnName = 'testComposeData' + + const articleId = 'ayelen.near-1699406465524' + // const id = "ayelen.near-1708014044488"; + // const id = "ayelen.near-1696980478217"; + // const id = "ayelen.near-1707407808397"; + // const id = "ayelen.near-1697150785479"; + // const id = "ayelen.near-1697152421776"; + + const upVote = { + isDelete: false, + sbts: ['public'], //For the moment should only have 1 sbt in the array } - return { - isError, - msg, - fnName, - }; - } catch (err) { - return { - isError: true, - msg: err.message, - fnName, - }; - } + let isError = false + let msg = '' + try { + const getComposeData = functionsToTest.composeData( + articleId, + upVote, + currentVersion, + config + ) + // expectedStructure = { + // index: { + // [action]: JSON.stringify({ + // key: articleId, + // value: { + // ...upVote, + // }, + // }), + // }, + // } + + const expectedStructure = { + index: { + [action]: JSON.stringify({ + key: articleId, + value: { + ...upVote, + }, + }), + }, + } + + if ( + JSON.stringify(getComposeData) !== JSON.stringify(expectedStructure) + ) { + isError = true + msg = [ + 'The result is not matching the expected result', + `Expected: ${JSON.stringify(expectedStructure)}`, + `Result: ${JSON.stringify(getComposeData)}`, + ] + } + + return { + isError, + msg, + fnName, + } + } catch (err) { + return { + isError: true, + msg: err.message, + fnName, + } + } } function testExecuteSaveUpVote() { - const now = Date.now(); - functionsToTest.executeSaveUpVote( - "test-1", - { - upVoteData: { isDelete: false, sbts: ["public"] }, - metadata: { - id: `uv/f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb/${now}`, - author: - "f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb", - sbt: ["public"], - createdTimestamp: now, - lastEditTimestamp: now, - versionKey: "v0.0.2", - }, - }, - () => { - console.log("testExecuteSaveUpVote commited"); - }, - () => { - console.log("testExecuteSaveUpVote canceled"); - }, - currentVersion, - config - ); + const now = Date.now() + functionsToTest.executeSaveUpVote( + 'test-1', + { + upVoteData: { isDelete: false, sbts: ['public'] }, + metadata: { + id: `uv/f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb/${now}`, + author: 'f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb', + sbt: ['public'], + createdTimestamp: now, + lastEditTimestamp: now, + versionKey: 'v0.0.2', + }, + }, + () => { + console.log('testExecuteSaveUpVote commited') + }, + () => { + console.log('testExecuteSaveUpVote canceled') + }, + currentVersion, + config + ) } function testCreateUpVote() { - const articleId = `a/f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb/${Date.now()}`; - const upVoteData = { isDelete: false, sbt: "public" }; - const userMetadataHelper = { - author: "f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb", - sbt: "public", - }; - const onCommit = () => { - console.log("testCreateUpVote commited"); - }; - const onCancel = () => { - console.log("testCreateUpVote canceled"); - }; - functionsToTest.createUpVote( - config, - articleId, - upVoteData, - userMetadataHelper, - onCommit, - onCancel - ); + const articleId = `a/f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb/${Date.now()}` + const upVoteData = { isDelete: false, sbt: 'public' } + const userMetadataHelper = { + author: 'f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb', + sbt: 'public', + } + const onCommit = () => { + console.log('testCreateUpVote commited') + } + const onCancel = () => { + console.log('testCreateUpVote canceled') + } + functionsToTest.createUpVote( + config, + articleId, + upVoteData, + userMetadataHelper, + onCommit, + onCancel + ) } //========================================================================End testLibUpVotes tests================================================================================ -const [asyncComponent, setAsyncComponent] = useState(

Loading...

); +const [asyncComponent, setAsyncComponent] = useState(

Loading...

) displayTestsAsyncResults([ - { - fnName: "testGetUpVotesData", - fn: testGetUpVotesData, - description: - "Should get the upVotesData asociated with the article that have the passed ID", - }, + { + fnName: 'testGetUpVotesData', + fn: testGetUpVotesData, + description: + 'Should get the upVotesData asociated with the article that have the passed ID', + }, ]).then((res) => { - setAsyncComponent(res); -}); + setAsyncComponent(res) +}) return ( - <> - {displayTestsSyncResults([ - { - fnName: "testComposeDataIsWorkingAsExpected", - fn: testComposeDataIsWorkingAsExpected, - description: "Check if the structure is as the expected one", - }, - ])} - {asyncComponent} - - - -); + <> + {displayTestsSyncResults([ + { + fnName: 'testComposeDataIsWorkingAsExpected', + fn: testComposeDataIsWorkingAsExpected, + description: 'Check if the structure is as the expected one', + }, + ])} + {asyncComponent} + + + +) diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..499f6a4 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,373 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@near-js/accounts@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@near-js/accounts/-/accounts-1.0.4.tgz#b699dc1c63ffccc1598481b4260dfaf2507f0a69" + integrity sha512-6zgSwq/rQ9ggPOIkGUx9RoEurbJiojqA/axeh6o1G+46GqUBI7SUcDooyVvZjeiOvUPObnTQptDYpbV+XZji8g== + dependencies: + "@near-js/crypto" "1.2.1" + "@near-js/providers" "0.1.1" + "@near-js/signers" "0.1.1" + "@near-js/transactions" "1.1.2" + "@near-js/types" "0.0.4" + "@near-js/utils" "0.1.0" + ajv "8.11.2" + ajv-formats "2.1.1" + bn.js "5.2.1" + borsh "1.0.0" + depd "2.0.0" + lru_map "0.4.1" + near-abi "0.1.1" + +"@near-js/crypto@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@near-js/crypto/-/crypto-1.2.1.tgz#aa18bed171e68653dae9f82114636eba34ece32a" + integrity sha512-iJOHaGKvdudYfR8nEtRhGlgcTEHeVmxMoT0JVXmuP3peG96v/sSnA03CE6MZBeCC8txKAQOffagxE7oU6hJp9g== + dependencies: + "@near-js/types" "0.0.4" + "@near-js/utils" "0.1.0" + "@noble/curves" "1.2.0" + bn.js "5.2.1" + borsh "1.0.0" + randombytes "2.1.0" + +"@near-js/keystores-browser@0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@near-js/keystores-browser/-/keystores-browser-0.0.9.tgz#4d6211ad617613124aeee78ede5771b153e3bcdd" + integrity sha512-JzPj+RHJN2G3CEm/LyfbtZDQy/wxgOlqfh52voqPGijUHg93b27KBqtZShazAgJNkhzRbWcoluWQnd2jL8vF7A== + dependencies: + "@near-js/crypto" "1.2.1" + "@near-js/keystores" "0.0.9" + +"@near-js/keystores-node@0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@near-js/keystores-node/-/keystores-node-0.0.9.tgz#c2fd2f5bfbca1c75699dd7324e300cfd12e790ed" + integrity sha512-2B9MYz6uIhysG1fhQSjvaPYCM7gM+UAeDchX0J8QRauXIeN8TGzpcdgkdkMUnWNTIdt3Iblh0ZuCs+FY02dTXg== + dependencies: + "@near-js/crypto" "1.2.1" + "@near-js/keystores" "0.0.9" + +"@near-js/keystores@0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@near-js/keystores/-/keystores-0.0.9.tgz#768aaaab1beb7f797432513cb1bbf9430e305a85" + integrity sha512-j8ySgVEcm2Gg6zxkSdadNtPlIqhJZdPGfWWM3tPtEoowNS9snhwZn5NRFPrgmX0+MzpF7E091CRcY90MvRVhsg== + dependencies: + "@near-js/crypto" "1.2.1" + "@near-js/types" "0.0.4" + +"@near-js/providers@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@near-js/providers/-/providers-0.1.1.tgz#7489eb5a4248562fe98fdbc4cb55ea3f6d787e0a" + integrity sha512-0M/Vz2Ac34ShKVoe2ftVJ5Qg4eSbEqNXDbCDOdVj/2qbLWZa7Wpe+me5ei4TMY2ZhGdawhgJUPrYwdJzOCyf8w== + dependencies: + "@near-js/transactions" "1.1.2" + "@near-js/types" "0.0.4" + "@near-js/utils" "0.1.0" + bn.js "5.2.1" + borsh "1.0.0" + http-errors "1.7.2" + optionalDependencies: + node-fetch "2.6.7" + +"@near-js/signers@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@near-js/signers/-/signers-0.1.1.tgz#6d32b262eac9b03fab5fc1ee93d2c4055c86980f" + integrity sha512-focJgs04dBUfawMnyGg3yIjaMawuVz2OeLRKC4t5IQDmO4PLfdIraEuwgS7tckMq3GdrJ7nqkwkpSNYpdt7I5Q== + dependencies: + "@near-js/crypto" "1.2.1" + "@near-js/keystores" "0.0.9" + "@noble/hashes" "1.3.3" + +"@near-js/transactions@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@near-js/transactions/-/transactions-1.1.2.tgz#7dec18b463cd336e325ee61b1e1c39a6192d9e81" + integrity sha512-AqYA56ncwgrWjIu+bNaWjTPRZb0O+SfpWIP7U+1FKNKxNYMCtkt6zp7SlQeZn743shKVq9qMzA9+ous/KCb0QQ== + dependencies: + "@near-js/crypto" "1.2.1" + "@near-js/signers" "0.1.1" + "@near-js/types" "0.0.4" + "@near-js/utils" "0.1.0" + "@noble/hashes" "1.3.3" + bn.js "5.2.1" + borsh "1.0.0" + +"@near-js/types@0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@near-js/types/-/types-0.0.4.tgz#d941689df41c850aeeeaeb9d498418acec515404" + integrity sha512-8TTMbLMnmyG06R5YKWuS/qFG1tOA3/9lX4NgBqQPsvaWmDsa+D+QwOkrEHDegped0ZHQwcjAXjKML1S1TyGYKg== + dependencies: + bn.js "5.2.1" + +"@near-js/utils@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@near-js/utils/-/utils-0.1.0.tgz#1368f59008c39df1e903bd3e0f308eedcdf25c1d" + integrity sha512-kOVAXmJzaC8ElJD3RLEoBuqOK+d5s7jc0JkvhyEtbuEmXYHHAy9Q17/YkDcX9tyr01L85iOt66z0cODqzgtQwA== + dependencies: + "@near-js/types" "0.0.4" + bn.js "5.2.1" + bs58 "4.0.0" + depd "2.0.0" + mustache "4.0.0" + +"@near-js/wallet-account@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@near-js/wallet-account/-/wallet-account-1.1.1.tgz#e66e8d10fb51f71f7cb722bdff63fb98e2b0d486" + integrity sha512-NnoJKtogBQ7Qz+AP+LdF70BP8Az6UXQori7OjPqJLMo73bn6lh5Ywvegwd1EB7ZEVe4BRt9+f9QkbU5M8ANfAw== + dependencies: + "@near-js/accounts" "1.0.4" + "@near-js/crypto" "1.2.1" + "@near-js/keystores" "0.0.9" + "@near-js/signers" "0.1.1" + "@near-js/transactions" "1.1.2" + "@near-js/types" "0.0.4" + "@near-js/utils" "0.1.0" + bn.js "5.2.1" + borsh "1.0.0" + +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + +"@noble/hashes@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@types/json-schema@^7.0.11": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +ajv-formats@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv@8.11.2: + version "8.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" + integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ajv@^8.0.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.16.0.tgz#22e2a92b94f005f7e0f9c9d39652ef0b8f6f0cb4" + integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + +base-x@^2.0.1: + version "2.0.6" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-2.0.6.tgz#4582a91ebcec99ee06f4e4032030b0cf1c2941d8" + integrity sha512-UAmjxz9KbK+YIi66xej+pZVo/vxUOh49ubEvZW5egCbxhur05pBb+hwuireQwKO4nDpsNm64/jEei17LEpsr5g== + dependencies: + safe-buffer "^5.0.1" + +bn.js@5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +borsh@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/borsh/-/borsh-1.0.0.tgz#b564c8cc8f7a91e3772b9aef9e07f62b84213c1f" + integrity sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ== + +bs58@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.0.tgz#65f5deaf6d74e6135a99f763ca6209ab424b9172" + integrity sha512-/jcGuUuSebyxwLLfKrbKnCJttxRf9PM51EnHTwmFKBxl4z1SGkoAhrfd6uZKE0dcjQTfm6XzTP8DPr1tzE4KIw== + dependencies: + base-x "^2.0.1" + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fs@^0.0.1-security: + version "0.0.1-security" + resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" + integrity sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +lru_map@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" + integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== + +mustache@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.0.0.tgz#7f02465dbb5b435859d154831c032acdfbbefb31" + integrity sha512-FJgjyX/IVkbXBXYUwH+OYwQKqWpFPLaLVESd70yHjSDunwzV2hZOoTBvPf4KLoxesUzzyfTH6F784Uqd7Wm5yA== + +near-abi@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/near-abi/-/near-abi-0.1.1.tgz#b7ead408ca4ad11de4fe3e595d30a7a8bc5307e0" + integrity sha512-RVDI8O+KVxRpC3KycJ1bpfVj9Zv+xvq9PlW1yIFl46GhrnLw83/72HqHGjGDjQ8DtltkcpSjY9X3YIGZ+1QyzQ== + dependencies: + "@types/json-schema" "^7.0.11" + +near-api-js@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/near-api-js/-/near-api-js-3.0.4.tgz#5ee094ce53e30239cc817ca942ec3e9739296dbb" + integrity sha512-qKWjnugoB7kSFhzZ5GXyH/eABspCQYWBmWnM4hpV5ctnQBt89LqgEu9yD1z4sa89MvUu8BuCxwb1m00BE8iofg== + dependencies: + "@near-js/accounts" "1.0.4" + "@near-js/crypto" "1.2.1" + "@near-js/keystores" "0.0.9" + "@near-js/keystores-browser" "0.0.9" + "@near-js/keystores-node" "0.0.9" + "@near-js/providers" "0.1.1" + "@near-js/signers" "0.1.1" + "@near-js/transactions" "1.1.2" + "@near-js/types" "0.0.4" + "@near-js/utils" "0.1.0" + "@near-js/wallet-account" "1.1.1" + "@noble/curves" "1.2.0" + ajv "8.11.2" + ajv-formats "2.1.1" + bn.js "5.2.1" + borsh "1.0.0" + depd "2.0.0" + http-errors "1.7.2" + near-abi "0.1.1" + node-fetch "2.6.7" + +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +path@^0.12.7: + version "0.12.7" + resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== + dependencies: + process "^0.11.1" + util "^0.10.3" + +process@^0.11.1: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +randombytes@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +"statuses@>= 1.5.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +uri-js@^4.2.2, uri-js@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== + dependencies: + inherits "2.0.3" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0"