{
"data": {
"rodrigos.near": {
"widget": {
"lib.article": {
"": "// lib.article\n\nconst {\n mainStateUpdate,\n isTest,\n stateUpdate,\n functionsToCallByLibrary,\n callLibs,\n baseAction,\n kanbanColumns,\n widgets,\n usersSBTs,\n} = props;\n\nconst libName = \"article\"; // EDIT: set lib name\nconst functionsToCall = functionsToCallByLibrary[libName];\n\nlet resultFunctionsToCallByLibrary = Object.assign(\n {},\n functionsToCallByLibrary\n);\nlet resultFunctionsToCall = [];\n\nconst currentVersion = \"0.0.2\"; // EDIT: Set version\n\nconst prodAction = `${baseAction}_v${currentVersion}`;\nconst testAction = `test_${prodAction}`;\nconst versionsBaseActions = isTest ? `test_${baseAction}` : baseAction;\nconst action = isTest ? testAction : prodAction;\n// START LIB CALLS SECTION\n// interface FunctionCall {\n// functionName: string,\n// key: string, // The state of the caller will be updated with this string as a key\n// props: Record<string, any> // function parameters as object\n// }\n\n// type LibsCalls = Record<string, FunctionCall> // Key is lib name after lib.\n\nconst libSrcArray = [widgets.libs.libSBT]; // string to lib widget // EDIT: set libs to call\n\nconst imports = { notifications: [\"getNotificationData\"] };\n\nconst libCalls = {};\nlibSrcArray.forEach((libSrc) => {\n const libName = libSrc.split(\"lib.\")[1];\n libCalls[libName] = [];\n});\n\nState.init({\n libCalls, // is a LibsCalls object\n notifications: {},\n});\n// END LIB CALLS SECTION\n\nfunction log(message) {\n console.log(`lib.${libName}`, message);\n}\n\nfunction logError(message) {\n console.error(`lib.${libName}`, message);\n}\n\nfunction libStateUpdate(obj) {\n State.update(obj);\n}\n\n// START LIB FUNCTIONS: EDIT set functions you need\nfunction canUserCreateArticle(props) {\n const { env, accountId, sbtsNames } = props;\n\n if (accountId) {\n setAreValidUsers([accountId], sbtsNames);\n } else {\n return false;\n }\n\n const result = state[`isValidUser-${accountId}`];\n resultFunctionsToCall = resultFunctionsToCall.filter((call) => {\n const discardCondition =\n call.functionName === \"canUserCreateArticle\" && result !== undefined;\n return !discardCondition;\n });\n\n return result;\n}\n\nfunction setAreValidUsers(accountIds, sbtsNames) {\n const newLibsCalls = Object.assign({}, state.libCalls);\n if (!newLibsCalls.SBT) {\n logError(\"Key SBT is not set in lib.\", libName);\n }\n\n accountIds.forEach((accountId) => {\n const isCallPushed =\n newLibsCalls.SBT.find((libCall) => {\n return (\n libCall.functionName === \"isValidUser\" &&\n libCall.props.accountId === accountId\n );\n }) !== undefined;\n const isCallReturned = state[`isValidUser-${accountId}`] !== undefined;\n\n if (isCallPushed || isCallReturned) {\n return;\n }\n\n const existingUserSBTs = usersSBTs.find(\n (userSBTs) => userSBTs.user === accountId\n );\n\n if (!existingUserSBTs) {\n newLibsCalls.SBT.push({\n functionName: \"isValidUser\",\n key: `isValidUser-${accountId}`,\n props: {\n accountId,\n sbtsNames,\n },\n });\n }\n });\n State.update({ libCalls: newLibsCalls });\n}\n\nfunction createArticle(props) {\n const { article, onCommit, onCancel } = props;\n\n saveHandler(article, onCommit, onCancel);\n\n resultFunctionsToCall = resultFunctionsToCall.filter((call) => {\n return call.functionName !== \"createArticle\";\n });\n\n return article;\n}\n\nfunction deleteArticle(props) {\n const { article, onCommit, onCancel } = props;\n\n article.deletedArticle = true;\n\n saveHandler(article, onCommit, onCancel);\n\n resultFunctionsToCall = resultFunctionsToCall.filter((call) => {\n return call.functionName !== \"deleteArticle\";\n });\n\n return article;\n}\n\nconst saveHandler = (article, onCommit, onCancel) => {\n if (article.title && article.body) {\n const newData = composeData(article);\n\n Social.set(newData, {\n force: true,\n onCommit,\n onCancel,\n });\n } else {\n logError(\"Article is missing title or body\");\n }\n};\n\nfunction getNotificationData(type, accountId, url) {\n if (state.notifications.getNotificationData) {\n return state.notifications.getNotificationData(type, accountId, url);\n }\n}\n\nfunction extractMentions(text) {\n const mentionRegex =\n /@((?:(?:[a-z\\d]+[-_])*[a-z\\d]+\\.)*(?:[a-z\\d]+[-_])*[a-z\\d]+)/gi;\n mentionRegex.lastIndex = 0;\n const accountIds = new Set();\n for (const match of text.matchAll(mentionRegex)) {\n if (\n !/[\\w`]/.test(match.input.charAt(match.index - 1)) &&\n !/[/\\w`]/.test(match.input.charAt(match.index + match[0].length)) &&\n match[1].length >= 2 &&\n match[1].length <= 64\n ) {\n accountIds.add(match[1].toLowerCase());\n }\n }\n return [...accountIds];\n}\n\nfunction composeData(article) {\n let data = {\n [action]: {\n main: JSON.stringify(article),\n },\n index: {\n [action]: JSON.stringify({\n key: \"main\",\n value: {\n type: \"md\",\n id: article.id ?? `${context.accountId}-${Date.now()}`,\n },\n }),\n },\n };\n\n const mentions = extractMentions(article.body);\n\n if (mentions.length > 0) {\n const dataToAdd = getNotificationData(\n \"mention\",\n mentions,\n `https://near.social/${widgets.thisForum}?sharedArticleId=${article.id}${\n isTest ? \"&isTest=t\" : \"\"\n }`\n );\n\n data.post = dataToAdd.post;\n data.index.notify = dataToAdd.index.notify;\n }\n\n return data;\n}\n\nfunction getArticleBlackListByBlockHeight() {\n return [\n 91092435, 91092174, 91051228, 91092223, 91051203, 98372095, 96414482,\n 96412953, 103131250, 106941548,\n ];\n}\n\nfunction getArticleBlackListByRealArticleId() {\n return [\n \"blaze.near-1690410074090\",\n \"blaze.near-1690409577184\",\n \"blaze.near-1690803928696\",\n \"blaze.near-1690803872147\",\n \"blaze.near-1690574978421\",\n \"f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691703303485\",\n \"f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691702619510\",\n \"f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691702487944\",\n \"f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691707918243\",\n \"f2bc8abdb8ba64fe5aac9689ded9491ff0e6fdcd7a5c680b7cf364142d1789fb-1691707889297\",\n \"blaze.near-1697211386373\",\n \"silkking.near-1696797896796\",\n \"silkking.near-1696797784589\",\n \"silkking.near-1696797350661\",\n \"silkking.near-1696797276482\",\n \"silkking.near-1696797155012\",\n \"silkking.near-1696796793605\",\n \"silkking.near-1696796543546\",\n \"silkking.near-1696795954175\",\n \"silkking.near-1696794571874\",\n \"silkking.near-1696792789177\",\n \"zarmade.near-1690578803015\",\n ];\n}\n\nfunction canUserEditArticle(props) {\n const { article } = props;\n\n return article.author === context.accountId;\n}\n\nfunction getArticlesIndexes(action, subscribe) {\n return Social.index(action, \"main\", {\n order: \"desc\",\n subscribe,\n // limit: 10,\n });\n}\n\nfunction filterFakeAuthors(articleData, articleIndexData) {\n if (articleData.author === articleIndexData.accountId) {\n return articleData;\n }\n}\n\nfunction getArticlesNormalized(env, articleIdToFilter) {\n const articlesByVersion = Object.keys(versions).map((version, index, arr) => {\n const action = versions[version].action;\n const subscribe = index + 1 === arr.length && !articleIdToFilter;\n const articlesIndexes = getArticlesIndexes(action, subscribe);\n\n if (!articlesIndexes) return [];\n const validArticlesIndexes = filterInvalidArticlesIndexes(\n env,\n articlesIndexes\n );\n\n const validLatestEdits = getLatestEdits(validArticlesIndexes);\n\n const validFilteredByArticleId = articleIdToFilter\n ? filterByArticleId(validArticlesIndexes, articleIdToFilter)\n : undefined;\n\n const finalArticlesIndexes = validFilteredByArticleId ?? validLatestEdits;\n\n const articles = finalArticlesIndexes\n .map((article) => {\n return filterFakeAuthors(getArticle(article, action), article);\n })\n .filter((article) => {\n return article !== undefined;\n });\n return articles;\n });\n\n return normalizeLibData(articlesByVersion);\n}\n\nfunction getArticle(articleIndex, action) {\n const article = Social.get(\n `${articleIndex.accountId}/${action}/main`,\n articleIndex.blockHeight\n );\n\n let articleParsed = undefined;\n if (article) {\n articleParsed = JSON.parse(article);\n articleParsed.blockHeight = articleIndex.blockHeight;\n articleParsed.id = articleIndex.value.id;\n }\n\n if (articleParsed) {\n return articleParsed;\n }\n}\n\nfunction filterByArticleId(newFormatArticlesIndexes, articleIdToFilter) {\n return newFormatArticlesIndexes.filter((articleIndex) => {\n return articleIndex.value.id === articleIdToFilter;\n });\n}\n\nfunction getLatestEdits(newFormatArticlesIndexes) {\n return newFormatArticlesIndexes.filter((articleIndex) => {\n const latestEditForThisArticle = newFormatArticlesIndexes.find(\n (newArticleData) => newArticleData.value.id === articleIndex.value.id\n );\n return (\n JSON.stringify(articleIndex) === JSON.stringify(latestEditForThisArticle)\n );\n });\n}\n\nfunction filterInvalidArticlesIndexes(env, articlesIndexes) {\n const myArticlesIndexes = articlesIndexes.filter(\n (articleIndex) => articleIndex.accountId === \"kenrou-it.near\"\n );\n\n return articlesIndexes\n .filter((articleIndex) => articleIndex.value.id) // Has id\n .filter((articleIndex) => {\n const splittedId = articleIndex.value.id.split(\"-\");\n splittedId.pop();\n\n return splittedId.join(\"-\") === articleIndex.accountId;\n }) // id begins with same accountId as index object\n .filter(\n (articleIndex) =>\n !getArticleBlackListByBlockHeight().includes(articleIndex.blockHeight) // Blockheight is not in blacklist\n )\n .filter(\n (articleIndex) =>\n !getArticleBlackListByRealArticleId().includes(articleIndex.value.id) // Article id is not in blacklist\n );\n}\n\nfunction getArticleVersions(props) {\n const { env, sbtsNames, articleIdToFilter } = props;\n\n // Call other libs\n const normArticles = getArticlesNormalized(env, articleIdToFilter);\n\n const articlesAuthors = normArticles.map((article) => {\n return article.author;\n });\n\n setAreValidUsers(articlesAuthors, sbtsNames);\n\n resultFunctionsToCall = resultFunctionsToCall.filter((call) => {\n const discardCondition =\n call.functionName === \"getArticleVersions\" &&\n (state[`isValidUser-${call.props.accountId}`] !== undefined ||\n usersSBTs.find((userSbt) => {\n articlesAuthors.includes(userSbt.user);\n }));\n return !discardCondition;\n });\n\n const finalArticles = filterValidArticles(normArticles);\n\n return finalArticles;\n}\n\nfunction getArticles(props) {\n const { env, sbtsNames } = props;\n\n // Call other libs\n const normArticles = getArticlesNormalized(env);\n\n // Keep last edit from every article\n const lastEditionArticles = normArticles.filter((article) => {\n return normArticles.find(\n (compArticle) => JSON.stringify(compArticle) === JSON.stringify(article)\n );\n });\n\n const articlesAuthors = lastEditionArticles.map((article) => {\n return article.author;\n });\n\n setAreValidUsers(articlesAuthors, sbtsNames);\n\n resultFunctionsToCall = resultFunctionsToCall.filter((call) => {\n const discardCondition =\n call.functionName === \"getArticleVersions\" &&\n (state[`isValidUser-${call.props.accountId}`] !== undefined ||\n usersSBTs.find((userSbt) => {\n articlesAuthors.includes(userSbt.user);\n }));\n return !discardCondition;\n });\n\n const finalArticles = filterValidArticles(lastEditionArticles);\n\n const finalArticlesMapped = {};\n sbtsNames.forEach((sbtName) => {\n const sbtArticles = finalArticles.filter((article) => {\n if (!article.sbts) return false;\n return article.sbts.indexOf(sbtName) !== -1;\n });\n finalArticlesMapped[sbtName] = sbtArticles;\n });\n\n return finalArticlesMapped;\n}\n\nfunction filterValidator(articles) {\n return articles.filter((article) => {\n return (\n article.sbts.find((articleSbt) => {\n return (\n state[`isValidUser-${article.author}`][articleSbt] ||\n articleSbt === \"public\" ||\n usersSBTs.find((userSbt) => {\n return userSbt.user === article.author;\n }).credentials[articleSbt]\n );\n }) !== undefined\n );\n });\n}\n\nfunction filterValidArticles(articles) {\n let filteredArticles = filterValidator(filteredArticles ?? articles);\n\n const filteredArticlesWithoutDeletedOnes = filteredArticles.filter(\n (article) => !article.deletedArticle\n );\n\n return filteredArticlesWithoutDeletedOnes;\n}\n\nfunction filterMultipleKanbanTags(articleTags, kanbanTags) {\n const normalizedKanbanTag = [];\n kanbanTags.forEach((tag) => {\n normalizedKanbanTag.push(tag.replace(` `, \"-\"));\n });\n\n const kanbanTagsInArticleTags = articleTags.filter((tag) =>\n normalizedKanbanTag.includes(tag.toLowerCase().replace(` `, \"-\"))\n );\n\n const nonKanbanTags = articleTags.filter(\n (tag) => !normalizedKanbanTag.includes(tag.toLowerCase().replace(` `, \"-\"))\n );\n\n const result = [...nonKanbanTags, kanbanTagsInArticleTags[0]];\n\n return result;\n}\n\nfunction normalizeOldToV_0_0_1(article) {\n article.realArticleId = `${article.author}-${article.timeCreate}`;\n article.sbts = [\"public\"];\n\n return article;\n}\n\nfunction normalizeFromV0_0_1ToV0_0_2(article) {\n article.title = article.articleId;\n article.id = article.realArticleId;\n if (article.sbts[0] !== \"public\") {\n article.sbts[0] = article.sbts[0] + \" - class 1\";\n } // There is only one article that is not public and only has class 1\n\n delete article.articleId;\n delete article.realArticleId;\n\n return article;\n}\n\nfunction normalizeFromV0_0_2ToV0_0_3(article) {\n if (!Array.isArray(article.tags) && typeof article.tags === \"object\") {\n article.tags = Object.keys(article.tags);\n }\n\n if (article.tags) {\n article.tags = article.tags.filter(\n (tag) => tag !== undefined && tag !== null\n );\n } else {\n article.tags = [];\n }\n\n if (kanbanColumns) {\n const lowerCaseColumns = [];\n kanbanColumns.forEach((cl) => {\n lowerCaseColumns.push(cl.toLowerCase());\n });\n\n article.tags = filterMultipleKanbanTags(article.tags, lowerCaseColumns);\n }\n\n //Add day-month-year tag if it doesn't exists yet\n const creationDate = new Date(article.timeCreate);\n\n const dateTag = `${creationDate.getDate()}-${\n creationDate.getMonth() + 1\n }-${creationDate.getFullYear()}`;\n\n if (!article.tags.includes(dateTag)) article.tags.push(dateTag);\n\n if (article.blockHeight < 105654020 && article.sbts.includes(\"public\")) {\n article.sbts = [\"fractal.i-am-human.near - class 1\"];\n }\n\n return article;\n}\n\n// END LIB FUNCTIONS\n\n// EDIT: set functions you want to export\nfunction callFunction(call) {\n if (call.functionName === \"canUserCreateArticle\") {\n return canUserCreateArticle(call.props);\n } else if (call.functionName === \"createArticle\") {\n return createArticle(call.props);\n } else if (call.functionName === \"deleteArticle\") {\n return deleteArticle(call.props);\n } else if (call.functionName === \"canUserEditArticle\") {\n return canUserEditArticle(call.props);\n } else if (call.functionName === \"getArticles\") {\n return getArticles(call.props);\n } else if (call.functionName === \"getArticleVersions\") {\n return getArticleVersions(call.props);\n }\n}\n\n// EDIT: set versions you want to handle, considering their action to Social.index and the way to transform to one version to another (normalization)\nconst versions = {\n old: {\n normalizationFunction: normalizeOldToV_0_0_1,\n action: versionsBaseActions,\n validBlockHeightRange: [0, 102530777],\n },\n \"v0.0.1\": {\n normalizationFunction: normalizeFromV0_0_1ToV0_0_2,\n action: `${versionsBaseActions}_v0.0.1`,\n validBlockHeightRange: [102530777, 103053147],\n },\n \"v0.0.2\": {\n normalizationFunction: normalizeFromV0_0_2ToV0_0_3,\n action: `${versionsBaseActions}_v0.0.2`,\n validBlockHeightRange: [103053147, undefined],\n },\n};\n\nfunction normalizeLibData(libDataByVersion) {\n let libData;\n\n Object.keys(versions).forEach((version, index, array) => {\n const normFn = versions[version].normalizationFunction;\n const validBlockHeightRange = versions[version].validBlockHeightRange;\n const normLibData = libDataByVersion[index]\n .filter((libData) => {\n if (validBlockHeightRange[1] === undefined) {\n return true;\n }\n\n return (\n validBlockHeightRange[0] < libData.blockHeight &&\n libData.blockHeight < validBlockHeightRange[1]\n );\n })\n .map((libData, i) => {\n if (libData) return normFn(libData);\n });\n\n if (index + 1 === array.length) {\n // Last index\n libData = normLibData;\n return;\n }\n libDataByVersion[index + 1] =\n libDataByVersion[index + 1].concat(normLibData);\n });\n\n return libData;\n}\n\nif (functionsToCall && functionsToCall.length > 0) {\n const updateObj = Object.assign({}, functionsToCallByLibrary);\n resultFunctionsToCall = [...functionsToCall];\n functionsToCall.forEach((call) => {\n updateObj[call.key] = callFunction(call);\n });\n\n resultFunctionsToCallByLibrary[libName] = resultFunctionsToCall;\n updateObj.functionsToCallByLibrary = resultFunctionsToCallByLibrary;\n\n const oldUsersSBTs = usersSBTs;\n // {\n // user: string,\n // credentials: {},\n // }\n\n const newUsersSBTs = Object.keys(state).map((key) => {\n if (key.includes(\"isValidUser-\")) {\n if (state[key] !== undefined) {\n const user = key.split(\"isValidUser-\")[1];\n const credentials = state[key];\n\n const oldUsers = oldUsersSBTs.map((userSbts) => userSbts.user);\n\n if (!oldUsers.includes(user)) {\n return {\n user,\n credentials,\n };\n }\n }\n }\n });\n\n const finalUsersSBTs = [...oldUsersSBTs, ...newUsersSBTs].filter(\n (userSBTs) => userSBTs !== undefined\n );\n\n if (finalUsersSBTs[0]) {\n mainStateUpdate({ usersSBTs: finalUsersSBTs });\n }\n\n stateUpdate(updateObj);\n}\n\nreturn (\n <>\n {libSrcArray.map((src) => {\n return callLibs(\n src,\n libStateUpdate,\n state.libCalls,\n {},\n `lib.${libName}`\n );\n })}\n\n <Widget\n src={`${widgets.libs.libNotifications}`}\n props={{\n stateUpdate: libStateUpdate,\n imports: imports[\"notifications\"],\n fatherNotificationsState: state.notifications,\n }}\n />\n </>\n);\n",
"metadata": {
"platform": "jutsu.ai"
},
"branch": {
"draft": null
}
}
}
}
}
}