Search
Search

Transaction: E1KmY7b...A3vF

Receiver
Status
Succeeded
Transaction Fee
0.01898 
Deposit Value
0.08951 
Gas Used
189 Tgas
Attached Gas
300 Tgas
Created
December 08, 2023 at 5:20:02am
Hash
E1KmY7btAiyX1hRcrnvNtKWC5LcPR8PJraJeMxCVA3vF

Actions

Called method: 'set' in contract: social.near
Arguments:
{ "data": { "calimero.near": { "widget": { "Calimero.Curb.Chat.UploadComponent": { "": "const uploadedFile = props.uploadedFile;\nconst setUploadedFile = props.setUploadedFile;\nconst type = props.type;\nconst icon = props.icon;\nconst text = props.text;\nconst setError = props.setError;\n\nconst uploadFileUpdateState = (body) => {\n setError('');\n asyncFetch('https://ipfs.near.social/add', {\n method: 'POST',\n headers: { Accept: 'application/json' },\n body,\n }).then((res) => {\n if (res.status === 500) {\n setError('Maximum file size is 10MB!');\n setUploadedFile(undefined);\n } else {\n setError('');\n const cid = res.body.cid;\n setUploadedFile({ file: { cid, name: body.name } });\n }\n });\n};\n\nconst filesOnChange = (files) => {\n if (files) {\n setUploadedFile({ file: { uploading: true, cid: null } });\n uploadFileUpdateState(files[0]);\n }\n};\n\nconst ButtonUpload = styled.div`\n .custom-files {\n width: 206px;\n padding: 8px 16px; /* 8px top and bottom, 16px left and right */\n color: #fff; /* Text color */\n font-family: 'Helvetica Neue'; /* Font family */\n font-size: 16px; /* Font size */\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 24px */\n cursor: pointer;\n border-radius: 2px;\n :hover {\n background-color: #686672;\n }\n }\n`;\n\nreturn (\n <div className=\"d-flex justify-center align-items-center mt-2\">\n <ButtonUpload>\n <Files\n multiple={false}\n accepts={type}\n minFileSize={1}\n maxFileSize={10000000}\n clickable\n className=\"custom-files\"\n onChange={filesOnChange}\n >\n {icon}\n {uploadedFile.file?.uploading ? 'Uploading' : text}\n </Files>\n </ButtonUpload>\n </div>\n);\n" }, "Calimero.Gateway.Dropdown.Element": { "": "const app = props.app ?? { name: 'App', icon: null };\nconst icon = props.icon ?? null;\nconst backgroundColor = props.backgroundColor ?? '#090723';\nconst onClick = props.onClick ?? null;\nconst componentOwnerId = props.componentOwnerId;\n\nconst Card = styled.div`\n display: inline-flex;\n flex-direction: row;\n align-items: center;\n background-color: ${backgroundColor};\n cursor: pointer;\n padding: 1rem;\n overflow: hidden;\n cursor: ${onClick ? 'pointer' : 'default'};\n width: 100%;\n`;\n\nconst Title = styled.div`\n color: #fff;\n /* text-sm/leading-5/font-medium */\n font-family: Inter;\n font-size: 0.875rem;\n font-style: normal;\n font-weight: 500;\n line-height: 1.25rem;\n margin-left: 1rem;\n`;\n\nreturn (\n <Card onClick={onClick}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Gateway.Dropdown.Icon`}\n props={{\n icon: app.icon,\n }}\n />\n <Title>{app.name}</Title>\n </Card>\n);\n" }, "Calimero.DocsChain.Members.Main": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst PageContainer = styled.div`\n width: 100%;\n height: 100vh;\n padding-left: 60px;\n padding-right: 60px;\n background-color: #0e0e10;\n color: #ffffff;\n`;\n\nreturn (\n <PageContainer>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Navbar.HorizontalNavbar'}\n config={redirectConfig}\n />\n <div className=\"d-flex\">\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.DocsSidebar'}\n config={redirectConfig}\n props={{\n onOpenCreatePage: state.onOpenCreatePage,\n createPageOpen: state.createPage.open,\n }}\n />\n </div>\n </PageContainer>\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.Comment": { "": "const { comment, componentOwnerId } = props;\n\nconst CommentContainer = styled.div`\n width: 100%;\n display: flex;\n flex-direction: column;\n`;\n\nconst CommenterInfoContainer = styled.div`\n width: 100%;\n display: flex;\n align-items: center;\n column-gap: 0.5rem;\n display: flex;\n justify-content: flex-start;\n`;\n\nconst ProfileIconContainerMsg = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n text-align: center;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n`;\n\nconst NameContainerCommenter = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n color: #6c757d;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 100%;\n padding-top: 2px;\n`;\n\nconst TimeText = styled.p`\n color: #adb5bd;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 100%;\n padding-top: 1rem;\n`;\n\nconst MessageText = styled.div`\n max-width: 100%;\n position: relative;\n word-wrap: break-word;\n display: flex;\n flex-direction: column;\n padding: 1rem;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n border-radius: 0px 8px 8px 8px;\n background-color: #1e1f28;\n`;\n\nconst formatTimeAgo = (seconds) => {\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const weeks = Math.floor(days / 7);\n const months = Math.floor(weeks / 4);\n\n if (months > 0) {\n return `${months} month${months > 1 ? 's' : ''} ago`;\n } else if (weeks > 0) {\n return `${weeks} week${weeks > 1 ? 's' : ''} ago`;\n } else if (days > 0) {\n return `${days} day${days > 1 ? 's' : ''} ago`;\n } else if (hours > 0) {\n return `${hours} hour${hours > 1 ? 's' : ''} ago`;\n } else if (minutes > 0) {\n return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;\n } else {\n return `just now`;\n }\n};\n\nreturn (\n <CommentContainer>\n <CommenterInfoContainer\n ownMessage={props.message.sender === context.accountId}\n >\n <ProfileIconContainerMsg>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: comment.member_id,\n showStatus: false,\n componentOwnerId: componentOwnerId,\n }}\n />\n </ProfileIconContainerMsg>\n <NameContainerCommenter>{comment.member_id}</NameContainerCommenter>\n <TimeText>{formatTimeAgo((Date.now() - comment.date) / 1000)}</TimeText>\n </CommenterInfoContainer>\n <MessageText>{comment.message}</MessageText>\n </CommentContainer>\n);\n" }, "Calimero.Curb.Communities.SearchContainer": { "": "const componentOwnerId = props.componentOwnerId;\nconst handleSearch = props.handleSearch;\nconst isSearched = props.isSearched;\nconst searchedTerm = props.searchedTerm;\nconst searchResultCount = props.searchResultCount;\nconst resetSearch = props.resetSearch;\n\nconst SearchContainer = styled.div`\n display: flex;\n flex-direction: column;\n ${({ isSearched }) =>\n isSearched\n ? 'align-items: start; padding-bottom: 0px; background-color: transparent;'\n : 'align-items: center; justify-content: center; height: 277px; background-color: #1d1d21;'}\n width: 100%;\n top: 127px;\n left: 60px;\n border-radius: 4px;\n padding: 24px;\n color: #fff;\n font-family: Helvetica Neue;\n`;\n\nconst Title = styled.div`\n font-size: 24px;\n font-weight: 500;\n line-height: 29px;\n letter-spacing: 0em;\n height: 29px;\n margin-bottom: 11px;\n`;\n\nconst Subtitle = styled.div`\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n margin-bottom: 26px;\n`;\n\nconst SearchResultContainer = styled.div`\n display: flex;\n column-gap: 16px;\n justify-content: center;\n`;\n\nconst SearchResult = styled.h5`\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n`;\n\nconst BackIcon = styled.i`\n cursor: pointer;\n color: #6c757d;\n :hover {\n color: #fff;\n }\n`;\n\nconst searchText =\n searchResultCount === 1\n ? `${searchResultCount} community for “${searchedTerm}”`\n : `${searchResultCount} communities for “${searchedTerm}”`;\n\nreturn (\n <SearchContainer isSearched={isSearched}>\n {isSearched ? (\n <SearchResultContainer>\n <BackIcon className=\"bi bi-arrow-left\" onClick={resetSearch}></BackIcon>\n <SearchResult>{searchText}</SearchResult>\n </SearchResultContainer>\n ) : (\n <>\n <Title>Discover communities on Calimero</Title>\n <Subtitle>\n From your favourite projects to Near protocol updates...\n </Subtitle>\n </>\n )}\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.Search`}\n props={{\n componentOwnerId,\n handleSearch,\n searchedTerm,\n }}\n />\n </SearchContainer>\n);\n" }, "Calimero.Curb.Chat.MessageImageField": { "": "const file = props.file;\nconst resetImage = props.resetImage;\n\nconst RemoveButton = styled.div`\n position: relative;\n top: 0;\n right: 16px;\n z-index: 20;\n cursor: pointer;\n`;\n\nconst ResetFileIcon = ({ resetFile }) => {\n return (\n <RemoveButton onClick={resetFile}>\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 12 12\"\n fill=\"#fff\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g clipPath=\"url(#clip0_972_45209)\">\n <path\n d=\"M12 6C12 7.5913 11.3679 9.11742 10.2426 10.2426C9.11742 11.3679 7.5913 12 6 12C4.4087 12 2.88258 11.3679 1.75736 10.2426C0.632141 9.11742 0 7.5913 0 6C0 4.4087 0.632141 2.88258 1.75736 1.75736C2.88258 0.632141 4.4087 0 6 0C7.5913 0 9.11742 0.632141 10.2426 1.75736C11.3679 2.88258 12 4.4087 12 6ZM4.0155 3.4845C3.94509 3.41408 3.84958 3.37453 3.75 3.37453C3.65042 3.37453 3.55491 3.41408 3.4845 3.4845C3.41408 3.55491 3.37453 3.65042 3.37453 3.75C3.37453 3.84958 3.41408 3.94509 3.4845 4.0155L5.46975 6L3.4845 7.9845C3.44963 8.01937 3.42198 8.06076 3.40311 8.10631C3.38424 8.15187 3.37453 8.20069 3.37453 8.25C3.37453 8.29931 3.38424 8.34813 3.40311 8.39369C3.42198 8.43924 3.44963 8.48063 3.4845 8.5155C3.55491 8.58591 3.65042 8.62547 3.75 8.62547C3.79931 8.62547 3.84813 8.61576 3.89369 8.59689C3.93924 8.57802 3.98063 8.55037 4.0155 8.5155L6 6.53025L7.9845 8.5155C8.01937 8.55037 8.06076 8.57802 8.10631 8.59689C8.15187 8.61576 8.20069 8.62547 8.25 8.62547C8.29931 8.62547 8.34813 8.61576 8.39369 8.59689C8.43924 8.57802 8.48063 8.55037 8.5155 8.5155C8.55037 8.48063 8.57802 8.43924 8.59689 8.39369C8.61576 8.34813 8.62547 8.29931 8.62547 8.25C8.62547 8.20069 8.61576 8.15187 8.59689 8.10631C8.57802 8.06076 8.55037 8.01937 8.5155 7.9845L6.53025 6L8.5155 4.0155C8.55037 3.98063 8.57802 3.93924 8.59689 3.89369C8.61576 3.84813 8.62547 3.79931 8.62547 3.75C8.62547 3.70069 8.61576 3.65187 8.59689 3.60631C8.57802 3.56076 8.55037 3.51937 8.5155 3.4845C8.48063 3.44963 8.43924 3.42198 8.39369 3.40311C8.34813 3.38424 8.29931 3.37453 8.25 3.37453C8.20069 3.37453 8.15187 3.38424 8.10631 3.40311C8.06076 3.42198 8.01937 3.44963 7.9845 3.4845L6 5.46975L4.0155 3.4845Z\"\n fill=\"white\"\n />\n </g>\n <defs>\n <clipPath id=\"clip0_972_45209\">\n <rect width=\"12\" height=\"12\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n </RemoveButton>\n );\n};\n\nconst ImageWrapper = styled.div`\n display: flex;\n height: 64px;\n width: 64px;\n margin-left: 8px;\n margin-bottom: 8px;\n`;\n\nif (!file) return null;\nconst cid = file?.cid;\nreturn (\n <ImageWrapper>\n <img\n src={`https://ipfs.near.social/ipfs/${cid}`}\n alt=\"uploaded\"\n height=\"64px\"\n width=\"64px\"\n style={{\n maxHeight: '64px',\n maxWidth: '64px',\n }}\n />\n <ResetFileIcon resetFile={resetImage} />\n </ImageWrapper>\n);\n" }, "Calimero.TaskChain.Popups.ColumnDetailsPopup": { "": "const {\n componentOwnerId,\n columnIndex,\n columnName,\n setColumnName,\n contract,\n projectId,\n addActionStatus,\n removeColumn,\n containsTasks,\n columnDetailsButton,\n} = props;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst ActionsContainer = styled.div`\n display: flex;\n flex-direction: row;\n gap: 3px;\n align-items: center;\n items-center: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n flex: 1;\n height: 40px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-width: 0;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n width: 30%;\n height: 40px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n background-color: transparent;\n border: none;\n display: flex;\n justify-content: start;\n align-items: center;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst DeleteButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n display: flex;\n flex-direction: row;\n cursor: pointer;\n text-align: center;\n color: #ffffff;\n justify-content: center;\n align-items: center;\n gap: 3px;\n`;\n\nconst DeleteButtonText = styled.p`\n padding: 0;\n margin: 0;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst MissingName = styled.div`\n position: absolute;\n left: 8.6rem;\n top: 2.8rem;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst TextContainer = styled.div`\n display: flex;\n justify-content: center;\n flex-direction: column;\n width: 100%;\n text-align: center;\n margin-bottom: 1rem;\n margin-top: 1rem;\n`;\n\nconst SaveButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DeleteColumnContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst CloseDeleteColumnButton = styled.div`\n background-color: transparent;\n display: flex;\n justify-content: center;\n color: #6b7280;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst Overlay = styled.div`\n background-color: var(--blackA9);\n position: fixed;\n z-index: 1000;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);\n`;\n\nconst ModalContent = styled.div`\n position: absolute;\n width: 500px;\n padding: 30px;\n border-radius: 20px;\n color: white;\n`;\n\nconst FunctionButton = styled.button`\n ${({ backgroundColor }) =>\n backgroundColor && `background-color: ${backgroundColor};`}\n ${({ color }) => (color ? `color: ${color};` : 'color: #fff;')}\n :hover {\n ${({ hoverColor }) => hoverColor && `background-color: ${hoverColor};`}\n }\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DialogTitle = styled.p`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 15px;\n font-style: normal;\n font-weight: 700;\n line-height: 100%;\n padding: 0;\n margin: 0;\n`;\n\nconst ButtonContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 8px;\n`;\n\nconst [columnDetailsOpen, setColumnDetailsOpen] = useState(false);\nconst [editColumnName, setEditColumnName] = useState(columnName);\nconst [editColumnNameMissing, setEditColumnNameMissing] = useState(false);\nconst [deleteContent, setDeleteContent] = useState(false);\nconst [editColumnNameStatus, setEditColumnNameStatus] = useState(undefined);\n\nconst onChangeEditColumnName = (event) => {\n setEditColumnNameMissing(false);\n setEditColumnName(event.target.value);\n};\n\nconst onClose = () => {\n setColumnDetailsOpen(false);\n setEditColumnNameMissing(false);\n setEditColumnName(columnName);\n};\n\nconst handleUpdateColumnName = (columnName, newColumnName, columnIndex) => {\n if (!newColumnName || newColumnName.trim() === '') {\n setEditColumnNameMissing(true);\n return;\n }\n\n if (columnName === newColumnName) {\n return;\n }\n\n const actionStatusPrefix = 'Rename column';\n let newActionStatus = {\n id: actionStatusPrefix + projectId + columnIndex,\n status: `Saving column ${columnName} as ${newColumnName}`,\n };\n try {\n addActionStatus(newActionStatus);\n\n setColumnName(newColumnName);\n\n Near.fakCalimeroCall(contract, 'rename_status', {\n project_id: projectId,\n status_index: columnIndex,\n new_name: newColumnName,\n }).then(() => {\n newActionStatus.status = `Saved column ${columnName} as ${newColumnName}`;\n addActionStatus(newActionStatus);\n });\n } catch {\n newActionStatus.status = `Error saving column ${columnName} as ${newColumnName}`;\n addActionStatus(newActionStatus);\n setColumnName(columnName);\n setColumnDetailsOpen(false);\n }\n};\n\nconst deleteColumn = useCallback(() => {\n const actionStatusPrefix = 'Delete column';\n let newActionStatus = {\n id: actionStatusPrefix + projectId + columnIndex,\n status: `Deleting column ${columnName}`,\n seen: false,\n };\n try {\n addActionStatus(newActionStatus);\n removeColumn(columnIndex);\n\n Near.fakCalimeroCall(contract, 'remove_status', {\n project_id: projectId,\n status_index: columnIndex,\n }).then(() => {\n newActionStatus.status = `Deleted column ${columnName}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n console.log('deleteColumn', e);\n newActionStatus.status = `Error deleting column ${columnName}`;\n addActionStatus(newActionStatus);\n }\n setColumnDetailsOpen(false);\n}, [contract, projectId, columnIndex, columnName]);\n\nconst editColumnContent = (\n <Overlay>\n <ModalContent>\n <PopupContainer>\n <Header>\n <DialogTitle>Edit column name</DialogTitle>\n <CloseButton onClick={onClose}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <Label>Name</Label>\n <Input\n onChange={onChangeEditColumnName}\n value={editColumnName}\n placeholder=\"Add Name\"\n />\n {editColumnNameMissing && <MissingName>Missing name</MissingName>}\n </FieldContainer>\n <ButtonContainer>\n <FunctionButton\n backgroundColor=\"#D0FC42\"\n color=\"black\"\n hoverColor=\"#BBE33B\"\n onClick={() =>\n handleUpdateColumnName(columnName, editColumnName, columnIndex)\n }\n >\n Save\n </FunctionButton>\n <FunctionButton\n backgroundColor=\"transparent\"\n hoverColor=\"#F25757\"\n onClick={() => setDeleteContent(true)}\n >\n Delete\n </FunctionButton>\n </ButtonContainer>\n </PopupContainer>\n </ModalContent>\n </Overlay>\n);\n\nconst deleteColumnContent = (\n <Overlay>\n <ModalContent>\n <DeleteColumnContainer>\n <TextContainer>\n <Text>Are you sure you want to delete this column?</Text>\n </TextContainer>\n <FunctionButton\n backgroundColor=\"#F25757\"\n hoverColor=\"#DA4E4E\"\n onClick={deleteColumn}\n >\n Delete\n </FunctionButton>\n <CloseDeleteColumnButton onClick={() => setDeleteContent(false)}>\n Close\n </CloseDeleteColumnButton>\n </DeleteColumnContainer>\n </ModalContent>\n </Overlay>\n);\n\nconst cannotDeleteColumnContent = (\n <Overlay>\n <ModalContent>\n <DeleteColumnContainer>\n <TextContainer>\n <Text>You cannot delete column which contains tasks?</Text>\n </TextContainer>\n <CloseDeleteColumnButton onClick={() => setDeleteContent(false)}>\n Close\n </CloseDeleteColumnButton>\n </DeleteColumnContainer>\n </ModalContent>\n </Overlay>\n);\n\nconst content = deleteContent\n ? containsTasks\n ? cannotDeleteColumnContent\n : deleteColumnContent\n : editColumnContent;\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.BaseModal`}\n props={{\n toggle: columnDetailsButton,\n content,\n open: columnDetailsOpen,\n onOpenChange: (open) => setColumnDetailsOpen(open),\n }}\n />\n);\n" }, "Calimero.Curb.Settings.AboutDetails": { "": "const SettingsItem = styled.div`\n background-color: #0e0e10;\n padding-left: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n ${({ borderbottom }) => borderbottom && 'border-bottom: 1px solid #282933;'}\n ${({ roundedTop }) =>\n roundedTop &&\n 'border-top-left-radius: 0.375rem; border-top-right-radius: 0.375rem;'}\n ${({ roundedBottom }) =>\n roundedBottom &&\n 'border-bottom-left-radius: 0.375rem; border-bottom-right-radius: 0.375rem;'}\n`;\n\nconst Text = styled.h6`\n ${({ red }) =>\n red ? 'color: #DC3545; :hover { color: #f76560 }' : 'color: #FFF;'}\n /* Body/Regular */\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 24px */\n`;\n\nconst ButtonLeave = styled.button`\n background-color: transparent;\n border: none;\n padding: 0rem;\n margin: 0rem;\n`;\n\nconst timestampToDate = (timestampMs) => {\n const date = new Date(timestampMs);\n const day = date.getDate();\n const month = date.getMonth() + 1;\n const year = date.getFullYear();\n const formattedDay = day < 10 ? `0${day}` : `${day}`;\n const formattedMonth = month < 10 ? `0${month}` : `${month}`;\n return `${formattedDay}/${formattedMonth}/${year}`;\n};\n\nreturn (\n <>\n <SettingsItem borderbottom roundedTop>\n <Text>Created</Text>\n <Text>\n {props.dateCreated ? timestampToDate(props.dateCreated) : 'N/A'}\n </Text>\n </SettingsItem>\n <SettingsItem borderbottom>\n <Text>Managed by</Text>\n <Text>{props.manager}</Text>\n </SettingsItem>\n <SettingsItem roundedBottom>\n <ButtonLeave>\n <Text red onClick={props.handleLeaveChannel}>\n Leave Channel\n </Text>\n </ButtonLeave>\n </SettingsItem>\n </>\n);\n" }, "Calimero.DocsChain.Popups.JoinPopup": { "": "const PageContainer = styled.div`\n height: 100vh;\n width: 100%;\n background-color: #0e0e10;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n border-radius: 8px;\n width: 489px;\n`;\n\nconst JoinContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n text: center;\n`;\n\nconst JoinHeader = styled.div`\n display: flex;\n align-items: center;\n flex-direction: column;\n justify-content: center;\n gap: 1.5rem;\n padding-bottom: 1rem;\n padding-top: 1rem;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Button = styled.button`\n background-color: #4e95ff;\n :hover {\n background-color: #5bb0ff;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <PageContainer>\n <PopupContainer>\n <JoinContainer>\n <JoinHeader>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Logo.DocsChainLogo'}\n props={{\n justify: true,\n }}\n />\n <Text>Join DocsChain to view documents!</Text>\n <Button onClick={props.join}>Join</Button>\n </JoinHeader>\n </JoinContainer>\n </PopupContainer>\n </PageContainer>\n);\n" }, "Calimero.TaskChain.Popups.AddColumnPopup": { "": "const {\n componentOwnerId,\n addColumnButton,\n addNewColumn,\n addActionStatus,\n contract,\n projectId,\n} = props;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Title = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 14px;\n top: 55px;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DialogTitle = styled.p`\n font-family: Helvetica Neue;\n font-size: 24px;\n font-weight: 500;\n line-height: 29px;\n letter-spacing: 0em;\n text-align: left;\n padding: 0;\n margin: 0;\n color: #fff;\n`;\n\nconst Overlay = styled.div`\n background-color: var(--blackA9);\n position: fixed;\n z-index: 1000;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);\n`;\n\nconst ModalContent = styled.div`\n position: absolute;\n width: 500px;\n padding: 30px;\n border-radius: 20px;\n color: white;\n`;\n\nconst [columnName, setColumnName] = useState('');\nconst [addColumnNameMissing, setAddColumnNameMissing] = useState(false);\nconst [addColumnPopupOpen, setAddColumnPopupOpen] = useState(false);\n\nconst onChangeColumnName = ({ target }) => {\n setAddColumnNameMissing(false);\n setColumnName(target.value);\n};\n\nconst handleAddColumn = useCallback(() => {\n if (!columnName || columnName.trim() === '') {\n setAddColumnNameMissing(true);\n return;\n }\n const actionStatusPrefix = 'Add column';\n let newActionStatus = {\n id: actionStatusPrefix + columnName,\n status: `Saving column ${columnName}`,\n seen: false,\n };\n\n try {\n addActionStatus(newActionStatus);\n\n const newColumnProps = { name: columnName, tasks: [] };\n addNewColumn(newColumnProps);\n\n Near.fakCalimeroCall(contract, 'add_status', {\n project_id: projectId,\n status_name: columnName,\n }).then(() => {\n newActionStatus.status = `Saved column ${columnName}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n newActionStatus.status = `Error saving column ${columnName}`;\n addActionStatus(newActionStatus);\n }\n setAddColumnPopupOpen(false);\n setColumnName('');\n}, [columnName]);\n\nconst content = (\n <Overlay>\n <ModalContent>\n <PopupContainer>\n <Header>\n <DialogTitle>Add new Column</DialogTitle>\n <CloseButton\n onClick={() => {\n setAddColumnPopupOpen(false);\n setColumnName('');\n }}\n >\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n </Header>\n <FieldContainer>\n <Title\n onChange={onChangeColumnName}\n value={columnName}\n placeholder=\"Add Name\"\n />\n {addColumnNameMissing && <MissingTitle>Missing name</MissingTitle>}\n </FieldContainer>\n <FunctionButton onClick={handleAddColumn}>Add</FunctionButton>\n </PopupContainer>\n </ModalContent>\n </Overlay>\n);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.BaseModal`}\n props={{\n toggle: addColumnButton,\n content,\n open: addColumnPopupOpen,\n onOpenChange: (open) => setAddColumnPopupOpen(open),\n }}\n />\n);\n" }, "Calimero.Curb.WebSocketManager": { "": "const render = props.render;\n\n// dependencies\nconst Logger = props.deps.Logger;\nconst EventEmitter = props.deps.EventEmitter;\n\nlet debug = 'debug' in props ? JSON.parse(props.debug) : false;\nif (typeof debug !== 'boolean') debug = false;\n\nconst userId = props.accountId;\nconst wsAddress = props.wsAddress ?? 'ws://0.0.0.0:6376/ws';\nconst maxRetries = props.maxRetries ?? 5;\nconst reconnectDelay = props.reconnectDelay ?? 1000;\nconst maxDelay = props.maxDelay ?? 30000;\nconst getAuthToken = props.getAuthToken;\n\nconst ConnectionStatus = {\n CONNECTED: 'Connected',\n CONNECTING: 'Connecting...',\n RECONNECTING: 'Reconnecting...',\n DISCONNECTED: 'Disconnected',\n};\n\nconst retryCountRef = useRef(0);\nconst shouldReconnectRef = useRef(true);\nconst pingIntervalRef = useRef(null);\nconst wsTransport = useRef(null);\nconst connectionStatus = useRef(ConnectionStatus.DISCONNECTED);\n\nlet meroshipLogger = Logger('meroship', null, debug);\nlet logger$ = meroshipLogger.fallback();\n\nconst wsState$ = (ws) => {\n switch (ws.readyState) {\n case 0:\n return 'CONNECTING';\n case 1:\n return 'OPEN';\n case 2:\n return 'CLOSING';\n case 3:\n return 'CLOSED';\n default:\n return 'UNKNOWN';\n }\n};\n\nconst wsApiEvents = useRef(EventEmitter());\n\nconst wsApiNotifications = useRef(EventEmitter());\n\nconst setupWebSocket = () => {\n const commandEvents = EventEmitter();\n\n const ws = new WebSocket(wsAddress);\n connectionStatus.current = ConnectionStatus.CONNECTING;\n\n let retryTimeout = null;\n\n wsTransport.current = {\n send: (command, args, callback, reschedule, logger) => {\n let { log: log$, error: error$ } = logger$(logger).span(\n 'wsTransport.send',\n {\n command,\n args,\n },\n );\n\n if (ws.readyState === 1 /* OPEN */) {\n let id;\n for (;;) {\n id = Math.trunc(Math.random() * (Math.pow(2, 53) - 1));\n try {\n commandEvents.once(\n id,\n ([err, result]) => {\n if (callback)\n if (err) return callback(err);\n else callback(null, result);\n },\n true,\n );\n break;\n } catch {\n error$('request ID collision, retrying', { id });\n }\n }\n ws.send(JSON.stringify({ id, command, args }));\n if (pingIntervalRef.current) clearInterval(pingIntervalRef.current);\n pingIntervalRef.current = setInterval(ping, 15_000);\n log$('sent', { id, command, args });\n } else {\n if (typeof reschedule === 'undefined') reschedule = (f) => f();\n if (typeof reschedule === 'function') {\n wsApiEvents.current.once('connected', () =>\n setTimeout(reschedule, null, () =>\n wsTransport.current.send(\n command,\n args,\n callback,\n reschedule,\n logger,\n ),\n ),\n );\n\n log$('scheduled', {\n command,\n args,\n wsState: wsState$(ws),\n connectionStatus: connectionStatus.current,\n });\n }\n\n if (ws.readyState !== 0 /* CONNECTING */) {\n if (\n connectionStatus.current === ConnectionStatus.RECONNECTING &&\n retryTimeout\n ) {\n log$('expediting reconnection to send', {\n command,\n args,\n wsState: wsState$(ws),\n connectionStatus: connectionStatus.current,\n });\n clearTimeout(retryTimeout);\n retryTimeout = null;\n } else {\n log$('forcefully reviving WS connection to send', {\n command,\n args,\n wsState: wsState$(ws),\n connectionStatus: connectionStatus.current,\n });\n wsApiEvents.current.emit('disconnected', {\n delay: 0,\n maxRetries,\n trial: 0,\n });\n }\n setupWebSocket();\n }\n }\n },\n close: (code, reason, logger) => {\n logger$(logger).span('wsTransport.close', {\n code,\n reason,\n });\n // shouldReconnectRef.current = false;\n ws.close(code, reason);\n },\n };\n\n function ping(logger) {\n const pingLogger = logger$(logger).span('ping');\n const { log: log$ } = pingLogger;\n wsTransport.current.send(\n 'Ping',\n [],\n (err) => {\n if (err) return log$('ping failed', { err });\n if (connectionStatus.current !== ConnectionStatus.CONNECTED) {\n connectionStatus.current = ConnectionStatus.CONNECTED;\n wsTransport.current.send(\n 'Init',\n { account_id: userId },\n (err) => {\n if (err) return log$('connection initialization failed:', err);\n wsApiEvents.current.emit('connected');\n },\n (_f) => {\n /* do not reschedule if the connection is not open */\n },\n pingLogger,\n );\n }\n },\n (_f) => {\n /* do not reschedule if the connection is not open */\n },\n pingLogger,\n );\n }\n\n ws.onopen = (event) => {\n const onOpenLogger = meroshipLogger.span('WebSocket.onopen', { event });\n const { error: error$ } = onOpenLogger;\n wsTransport.current.send(\n 'Gateway::Headers',\n {\n 'x-api-key': { getAuthToken }.getAuthToken?.(),\n },\n (err) => {\n if (\n err &&\n !(\n err.type === 'ParseError' &&\n typeof err.data === 'string' &&\n // if there is no gateway infront of meroship, this error is expected, ignore it\n err.data.startsWith('unknown variant `Gateway::Headers`')\n )\n )\n return error$('gateway auth header application failed:', err);\n\n retryCountRef.current = 0;\n ping(onOpenLogger);\n },\n );\n };\n\n ws.onmessage = (event) => {\n const { log: log$ } = meroshipLogger.span('WebSocket.onmessage', { event });\n\n let message = JSON.parse(event.data);\n\n if (message) {\n if ('id' in message) {\n if ('result' in message) {\n log$('received', { id: message.id, result: message.result });\n commandEvents.emit(message.id, [null, message.result]);\n return;\n } else if ('error' in message) {\n log$('received', { id: message.id, error: message.error });\n commandEvents.emit(message.id, [message.error]);\n return;\n }\n } else if ('event' in message) {\n log$('received', { event: message.event });\n wsApiNotifications.current.emit(message.event.type, message.event.data);\n return;\n }\n }\n\n log$('unknown message', event.data);\n };\n\n let _transport = wsTransport.current;\n ws.onclose = (event) => {\n const { log: log$ } = meroshipLogger.span('WebSocket.onclose', { event });\n\n // this event was delayed, and a new connection has been established, safe to ignore\n let expiredTransport = _transport !== wsTransport.current;\n\n log$('WebSocket closed', {\n wsState: wsState$(ws),\n expiredTransport,\n connectionStatus: connectionStatus.current,\n });\n\n if (expiredTransport) return;\n if (shouldReconnectRef.current && retryCountRef.current < maxRetries) {\n let delay =\n Math.min(\n reconnectDelay * Math.pow(2, retryCountRef.current),\n maxDelay,\n ) +\n Math.random() * 1500;\n\n log$('reconnecting', {\n delay,\n trial: retryCountRef.current,\n maxRetries,\n });\n\n wsApiEvents.current.emit('disconnected', {\n delay,\n maxRetries,\n trial: retryCountRef.current,\n });\n\n retryCountRef.current++;\n retryTimeout = setTimeout(() => {\n retryTimeout = null;\n setupWebSocket();\n }, delay);\n connectionStatus.current = ConnectionStatus.RECONNECTING;\n } else {\n clearInterval(pingIntervalRef.current);\n log$('disconnected', {\n delay: 0,\n trial: retryCountRef.current,\n maxRetries,\n });\n\n wsApiEvents.current.emit('disconnected', {\n delay: 0,\n maxRetries,\n trial: retryCountRef.current,\n });\n\n connectionStatus.current = ConnectionStatus.DISCONNECTED;\n }\n };\n};\n\nconst wsApi = {\n send: (command, args, callback, reschedule) => {\n if (wsTransport.current) {\n wsTransport.current.send(command, args, callback, reschedule);\n return wsApi;\n }\n throw 'WebSocket has not been initialized';\n },\n connect: () => {\n if (connectionStatus.current === ConnectionStatus.DISCONNECTED) {\n retryCountRef.current = 0;\n setupWebSocket();\n } else if (connectionStatus.current === ConnectionStatus.CONNECTED) {\n // throw \"Already connected\";\n }\n return wsApi;\n },\n disconnect: () => {\n if (wsTransport.current) {\n if (connectionStatus.current === ConnectionStatus.DISCONNECTED) {\n // throw \"Already disconnected..\";\n } else {\n shouldReconnectRef.current = false;\n wsTransport.current.close();\n }\n } else {\n throw 'WebSocket has not been initialized';\n }\n return wsApi;\n },\n on: (event, listener) => {\n wsApiEvents.current.on(event, listener);\n return wsApi;\n },\n once: (event, listener) => {\n wsApiEvents.current.once(event, listener);\n return wsApi;\n },\n prependListener: (event, listener) => {\n wsApiEvents.current.prependListener(event, listener);\n return wsApi;\n },\n prependOnceListener: (event, listener) => {\n wsApiEvents.current.prependOnceListener(event, listener);\n return wsApi;\n },\n off: (event, listener) => {\n wsApiEvents.current.off(event, listener);\n return wsApi;\n },\n notifications: {\n on: (event, listener) => {\n wsApiNotifications.current.on(event, listener);\n return wsApi.notifications;\n },\n once: (event, listener) => {\n wsApiNotifications.current.once(event, listener);\n return wsApi.notifications;\n },\n prependListener: (event, listener) => {\n wsApiNotifications.current.prependListener(event, listener);\n return wsApi.notifications;\n },\n prependOnceListener: (event, listener) => {\n wsApiNotifications.current.prependOnceListener(event, listener);\n return wsApi.notifications;\n },\n off: (event, listener) => {\n wsApiNotifications.current.off(event, listener);\n return wsApi.notifications;\n },\n },\n methods: {\n subscribe: (args, callback, reschedule) => {\n return wsApi.send('Subscribe', args, callback, reschedule);\n },\n unsubscribe: (args, callback, reschedule) => {\n return wsApi.send('Unsubscribe', args, callback, reschedule);\n },\n submitTx: (signedTx, callback, reschedule) => {\n return wsApi.send('SubmitTransaction', [signedTx], callback, reschedule);\n },\n getAccountsStatus: (accounts, callback, reschedule) => {\n return wsApi.send('Status', { accounts }, callback, reschedule);\n },\n },\n};\n\nif (render) {\n return render({ wsApi, EventEmitter });\n}\n" }, "Calimero.TaskChain.BoardContainer.TaskColumn": { "": "const {\n tasks,\n title,\n submitComment,\n onTaskDragStop,\n onTaskDragStart,\n onTaskDragOver,\n functionLoader,\n componentOwnerId,\n handleColumns,\n createTask,\n projectId,\n contract,\n columns,\n setColumns,\n addActionStatus,\n removeColumn,\n columnIndex,\n} = props;\n\nconst ColumnContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n width: 17.5rem;\n margin-left: 2em;\n margin-right: 2em;\n background-color: transparent;\n cursor: pointer;\n :hover {\n background-color: #11121a;\n }\n padding: 1rem;\n`;\n\nconst ColumnDetailsIcon = styled.div`\n color: transparent;\n`;\n\nconst ColumnLabel = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-top: 4px;\n padding-bottom: 4px;\n gap: 0.5rem;\n cursor: pointer;\n width: 15rem;\n :hover {\n ${ColumnDetailsIcon} {\n color: #ffffff;\n }\n }\n`;\n\nconst TasksContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding-bottom: 16px;\n`;\n\nconst AddTaskContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n`;\n\nconst ColumnLabelText = styled.div`\n display: flex;\n justify-content: start;\n align-items: start;\n`;\n\nconst AddIcon = styled.div`\n color: #777583;\n`;\n\nconst AddTaskButton = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n background-color: #1e1f28;\n color: #ffffff;\n width: 240px;\n padding: 16px;\n gap: 4px;\n border-radius: 4px;\n :hover {\n ${AddIcon} {\n color: #ffffff;\n }\n }\n`;\n\nconst AddTaskText = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n margin-left: 0.25rem;\n`;\n\nconst Title = styled.div`\n padding: 0;\n margin: 0;\n max-width: 100%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst createTaskButton = (\n <AddTaskButton>\n <AddIcon>\n <i className=\"bi bi-plus-circle-fill\"></i>\n </AddIcon>\n <AddTaskText>Add new Card</AddTaskText>\n </AddTaskButton>\n);\n\nconst columnDetailsButton = (\n <ColumnDetailsIcon>\n <i className=\"bi bi-pen-fill\"></i>\n </ColumnDetailsIcon>\n);\n\nconst [showAddTaskDialog, setShowAddTaskDialog] = useState(false);\nconst [createStatus, setCreateStatus] = useState([]);\nconst [editTaskId, setEditTaskId] = useState(undefined);\nconst [editedTask, setEditedTask] = useState(undefined);\nconst [editTaskTitle, setEditTaskTitle] = useState('');\nconst [editTaskDescription, setEditTaskDescription] = useState('');\nconst [editTaskTitleMissing, setEditTaskTitleMissing] = useState(false);\nconst [editTaskDescriptionMissing, setEditTaskDescriptionMissing] =\n useState(false);\nconst [editTaskColumn, setEditTaskColumn] = useState('');\nconst [editTaskDialogTaskId, setEditTaskDialogTaskId] = useState(undefined);\nconst [showDeleteTaskDialog, setShowDeleteTaskDialog] = useState(false);\nconst [deleteTaskId, setDeleteTaskId] = useState(undefined);\nconst [showEditTaskDialog, setShowEditTaskDialog] = useState(false);\nconst [editTaskTitleStatus, setEditTaskTitleStatus] = useState([]);\nconst [editTaskDescriptionStatus, setEditTaskDescriptionStatus] = useState([]);\n\nconst addCreateTaskStatus = useCallback((newStatus) => {\n let isNew = true;\n let newCreateTaskStatuses = createStatus.map((status) => {\n if (status.title === newStatus.title) {\n isNew = false;\n return newStatus;\n } else {\n return status;\n }\n });\n if (isNew) {\n newCreateTaskStatuses = [...newCreateTaskStatuses, newStatus];\n }\n setCreateStatus(newCreateTaskStatuses);\n}, []);\n\nconst onChangeShowDeleteTaskDialog = (open, id) => {\n setShowDeleteTaskDialog(open);\n setDeleteTaskId(id);\n};\n\nconst onEditTaskTitle = ({ target }) => {\n setEditTaskTitleMissing(false);\n setEditTaskTitle(target.value);\n};\n\nconst onChangeShowEditTaskDialog = (open, id) => {\n setEditTaskTitleMissing(false);\n setEditTaskDescriptionMissing(false);\n setShowEditTaskDialog(open);\n setEditTaskDialogTaskId(id);\n};\n\nconst onEditTaskDescription = ({ target }) => {\n setEditTaskDescriptionMissing(false);\n setEditTaskDescription(target.value);\n};\n\nconst setEditTask = (task, id, title, description, column) => {\n setEditedTask(task);\n setEditTaskId(id);\n setEditTaskTitle(title);\n setEditTaskDescription(description);\n setEditTaskColumn(column);\n};\n\nconst onChangeDeleteTaskStatus = (status) => {\n setDeleteTaskStatus(status);\n};\n\nconst deleteTask = useCallback(() => {\n const oldStatuses = columns;\n const taskId = deleteTaskId;\n\n const actionStatusPrefix = 'Delete task';\n const newActionStatus = {\n id: actionStatusPrefix + taskId,\n status: `Deleting task: ${taskId}`,\n seen: false,\n };\n try {\n addActionStatus(newActionStatus);\n const newStatuses = columns;\n\n for (let status of newStatuses) {\n if (status.tasks.find((task) => task.id === taskId)) {\n deleteTask = status.tasks.find((task) => task.id === taskId);\n status.tasks = status.tasks.filter((task) => task.id !== taskId);\n }\n }\n\n setColumns(newStatuses);\n\n Near.fakCalimeroCall(contract, 'delete_task_by_id', {\n project_id: projectId,\n task_id: taskId,\n }).then(() => {\n newActionStatus.status = `Deleted task: ${taskId}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n newActionStatus.status = `Error deleting task: ${taskId}`;\n addActionStatus(newActionStatus);\n setColumns(oldStatuses);\n }\n onChangeShowDeleteTaskDialog(false, undefined);\n onChangeShowEditTaskDialog(false, undefined);\n}, []);\n\nconst updateTaskTitle = useCallback(() => {\n if (!editTaskTitle || editTaskTitle.trim() === '') {\n setEditTaskTitleMissing(true);\n return;\n }\n const oldTask = tasks.find((task) => task.id === editTaskId);\n\n if (oldTask.title === editTaskTitle) {\n return;\n }\n\n const newStatus = { id: editTaskId, status: 'Saving...' };\n const newStatuses = [...editTaskTitleStatus, newStatus];\n try {\n setEditTaskTitleStatus(newStatuses);\n\n const editedTask = {\n project_id: projectId,\n id: editTaskId,\n title: editTaskTitle,\n description: editTaskDescription,\n status: editTaskColumn,\n };\n\n let newColumnsArray = [];\n const storageColumns = Storage.privateGet(\n 'tempColumns' + contract + projectId,\n );\n\n if (storageColumns && JSON.parse(storageColumns).length > 0) {\n const columnsArray = JSON.parse(storageColumns);\n\n let filteredColumnsArray = [];\n if (columnsArray.find((task) => task.id === editTaskId)) {\n filteredColumnsArray = columnsArray.filter(\n (task) => task.id !== editTaskId,\n );\n }\n if (columnsArray.find((task) => task.title === oldTask.title)) {\n filteredColumnsArray = columnsArray.filter(\n (task) => task.title !== oldTask.title,\n );\n }\n\n newColumnsArray = filteredColumnsArray;\n newColumnsArray.push(editedTask);\n } else {\n newColumnsArray.push(editedTask);\n }\n\n const jsonStringArray = JSON.stringify(newColumnsArray);\n\n Storage.privateSet('tempColumns' + contract + projectId, jsonStringArray);\n\n handleColumns(projectId);\n\n Near.fakCalimeroCall(contract, 'change_task_name', {\n project_id: projectId,\n task_id: editTaskId,\n new_title: editTaskTitle,\n }).then(() => {\n let foundStatus = newStatuses.find((t) => t.id === editTaskId);\n foundStatus.status = 'Saved';\n const filteredStatuses = newStatuses.filter((t) => t.id !== editTaskId);\n setEditTaskTitleStatus([...filteredStatuses, foundStatus]);\n setTimeout(() => {\n const filteredStatuses = newStatuses.filter((s) => s.id !== editTaskId);\n setEditTaskTitleStatus(filteredStatuses);\n }, 3000);\n });\n } catch (e) {\n let foundStatus = newStatuses.find((t) => t.id === editTaskId);\n foundStatus.status = 'Error saving';\n const filteredStatuses = newStatuses.filter((t) => t.id !== editTaskId);\n setEditTaskTitleStatus([...filteredStatuses, foundStatus]);\n setTimeout(() => {\n const filteredStatuses = newStatuses.filter((s) => s.id !== editTaskId);\n setEditTaskTitleStatus(filteredStatuses);\n }, 3000);\n }\n}, []);\n\nconst updateTaskDescription = useCallback(() => {\n const oldTask = tasks.find((task) => task.id === editTaskId);\n\n if (oldTask.description === editTaskDescription) {\n return;\n }\n\n const newStatus = { id: editTaskId, status: 'Saving...' };\n const newStatuses = [...editTaskDescriptionStatus, newStatus];\n\n try {\n setEditTaskDescriptionStatus(newStatuses);\n\n const editedTask = {\n project_id: projectId,\n id: editTaskId,\n title: editTaskTitle,\n description: editTaskDescription,\n status: editTaskColumn,\n };\n\n let newColumnsArray = [];\n const storageColumns = Storage.privateGet(\n 'tempColumns' + contract + projectId,\n );\n\n if (storageColumns && JSON.parse(storageColumns).length > 0) {\n const columnsArray = JSON.parse(storageColumns);\n\n let filteredColumnsArray = [];\n if (columnsArray.find((task) => task.id === editTaskId)) {\n filteredColumnsArray = columnsArray.filter(\n (task) => task.id !== editTaskId,\n );\n }\n\n if (\n columnsArray.find((task) => task.description === oldTask.description)\n ) {\n filteredColumnsArray = columnsArray.filter(\n (task) => task.description !== oldTask.description,\n );\n }\n\n newColumnsArray = filteredColumnsArray;\n newColumnsArray.push(editedTask);\n } else {\n newColumnsArray.push(editedTask);\n }\n\n const jsonStringArray = JSON.stringify(newColumnsArray);\n\n Storage.privateSet('tempColumns' + contract + projectId, jsonStringArray);\n\n handleColumns(projectId);\n\n Near.fakCalimeroCall(contract, 'edit_task_description', {\n project_id: projectId,\n task_id: editTaskId,\n new_description: editTaskDescription,\n }).then(() => {\n let foundStatus = newStatuses.find((t) => t.id === editTaskId);\n foundStatus.status = 'Saved';\n const filteredStatuses = newStatuses.filter((t) => t.id !== editTaskId);\n setEditTaskDescriptionStatus([...filteredStatuses, foundStatus]);\n setTimeout(() => {\n const filteredStatuses = editTaskDescriptionStatus.filter(\n (s) => s.id !== editTaskId,\n );\n setEditTaskDescriptionStatus(filteredStatuses);\n }, 3000);\n });\n } catch (e) {\n let foundStatus = newStatuses.find((t) => t.id === editTaskId);\n foundStatus.status = 'Error saving';\n const filteredStatuses = newStatuses.filter((t) => t.id !== editTaskId);\n setEditTaskDescriptionStatus([...filteredStatuses, foundStatus]);\n setTimeout(() => {\n const filteredStatuses = editTaskDescriptionStatus.filter(\n (s) => s.id !== editTaskId,\n );\n setEditTaskDescriptionStatus(filteredStatuses);\n }, 3000);\n }\n}, []);\n\nconst cleanTaskStatusById = (id) => {\n const filteredStatuses = createStatus.filter((s) => s.id !== id);\n setCreateStatus(filteredStatuses);\n};\n\nconst cleanEditTaskDescriptionStatus = (id) => {\n const filteredStatuses = editTaskDescriptionStatus.filter((s) => s.id !== id);\n setEditTaskDescriptionStatus(filteredStatuses);\n};\n\nconst cleanCreateTaskStatus = (title) => {\n const filteredStatuses = createStatus.filter((s) => s.title !== title);\n setCreateStatus(filteredStatuses);\n};\n\nreturn (\n <>\n {showDeleteTaskDialog && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ConfirmationPopup`}\n props={{\n onClick: () => deleteTask(),\n onClose: () => onChangeShowDeleteTaskDialog(false, undefined, title),\n functionLoader,\n title: 'Are you sure you want to delete the task?',\n }}\n />\n )}\n {showEditTaskDialog && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.TaskDetailsDialog`}\n props={{\n onChangeShowEditTaskDialog,\n editTaskTitle,\n editTaskDescription,\n onEditTaskTitle,\n onEditTaskDescription,\n updateTaskTitle,\n updateTaskDescription,\n editTaskTitleStatus,\n editTaskId,\n editedTask,\n task,\n editTaskDialogTaskId,\n editTaskDescriptionStatus,\n onChangeShowDeleteTaskDialog,\n editTaskTitleMissing,\n editTaskDescriptionMissing,\n componentOwnerId,\n contract,\n projectId,\n }}\n />\n )}\n <ColumnContainer\n droppable\n onDragOver={(e) => {\n onTaskDragOver();\n }}\n onDrop={() => onTaskDragStop(title)}\n >\n <ColumnLabel>\n <Title>{title}</Title>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.ColumnDetailsPopup`}\n props={{\n componentOwnerId,\n columnIndex,\n columnName: title,\n removeColumn,\n contract,\n projectId,\n addActionStatus,\n containsTasks: tasks.length > 0,\n columnDetailsButton,\n columns,\n setColumns,\n addActionStatus,\n }}\n />\n </ColumnLabel>\n <TasksContainer>\n {tasks.map((task, i) => {\n return (\n <div key={task.id}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.TaskCard`}\n props={{\n task: task,\n status: title,\n index: i,\n onTaskDragStart: onTaskDragStart,\n onTaskDragStop: onTaskDragStop,\n onClick: () => {\n setEditTask(\n task,\n task.id,\n task.title,\n task.description,\n title,\n );\n onChangeShowEditTaskDialog(true, task.id);\n },\n createStatus,\n }}\n />\n </div>\n );\n })}\n </TasksContainer>\n <AddTaskContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.CreateTaskPopup`}\n props={{\n createTaskButton,\n componentOwnerId,\n addCreateTaskStatus,\n cleanCreateTaskStatus,\n contract,\n handleColumns,\n projectId,\n columnTitle: title,\n }}\n />\n </AddTaskContainer>\n </ColumnContainer>\n </>\n);\n" }, "Calimero.Curb.Loader.Loader": { "": "const Loader = styled.div`\n display: inline-block;\n ${({ size }) =>\n size\n ? `width: ${size}px; height: ${size}px;`\n : 'width: 20px; height: 20px;'}\n border: 3px solid rgba(255, 255, 255, 0.3);\n border-radius: 50%;\n ${({ color }) =>\n color ? `border-top-color: ${color};` : 'border-top-color: #fff;'}\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n`;\n\nreturn <Loader size={props.size} color={props.color} />;\n" }, "Calimero.Curb.Navbar.CurbLogo": { "": "const LogoContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n ${({ justify }) => justify && 'justify-content: center;'}\n`;\n\nconst CurbNameContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-size: 20.923px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-right: 0.875rem;\n`;\n\nconst OrganizationNameContainer = () => {\n return <CurbNameContainer>Chat</CurbNameContainer>;\n};\n\nreturn (\n <LogoContainer>\n <svg\n width=\"28\"\n height=\"28\"\n viewBox=\"0 0 28 28\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <rect width=\"28\" height=\"28\" rx=\"6\" fill=\"#5770F2\" />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M24.1568 14C24.1568 8.69358 19.8554 4.39215 14.549 4.39215C9.2426 4.39215 4.94117 8.69358 4.94117 14C4.93884 15.7503 5.41672 17.4677 6.32278 18.9653L5.46576 21.8784C5.41655 22.0457 5.41331 22.2231 5.45639 22.392C5.49946 22.561 5.58725 22.7152 5.71053 22.8385C5.83381 22.9618 5.98802 23.0495 6.15696 23.0926C6.3259 23.1357 6.50333 23.1324 6.67058 23.0832L9.58368 22.2262C11.0811 23.1326 12.7986 23.6105 14.549 23.6078C15.4677 23.6078 16.3564 23.4789 17.1977 23.2381C17.1509 22.8994 17.1267 22.5534 17.1267 22.2018C17.1267 18.2267 20.2197 14.974 24.1303 14.7191C24.1479 14.4817 24.1568 14.2419 24.1568 14ZM10.1672 12.9809C10.4375 12.7107 10.804 12.5588 11.1863 12.5588C11.5685 12.5588 11.9351 12.7107 12.2053 12.9809C12.4756 13.2512 12.6274 13.6178 12.6274 14C12.6274 14.3822 12.4756 14.7488 12.2053 15.0191C11.9351 15.2893 11.5685 15.4412 11.1863 15.4412C10.804 15.4412 10.4375 15.2893 10.1672 15.0191C9.89693 14.7488 9.74509 14.3822 9.74509 14C9.74509 13.6178 9.89693 13.2512 10.1672 12.9809ZM16.8927 12.9809C17.163 12.7107 17.5295 12.5588 17.9118 12.5588C18.294 12.5588 18.6605 12.7107 18.9308 12.9809C19.2011 13.2512 19.3529 13.6178 19.3529 14C19.3529 14.3822 19.2011 14.7488 18.9308 15.0191C18.6605 15.2893 18.294 15.4412 17.9118 15.4412C17.5295 15.4412 17.163 15.2893 16.8927 15.0191C16.6224 14.7488 16.4706 14.3822 16.4706 14C16.4706 13.6178 16.6224 13.2512 16.8927 12.9809Z\"\n fill=\"white\"\n />\n </svg>\n <OrganizationNameContainer />\n </LogoContainer>\n);\n" }, "Calimero.DocsChain.Main": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId;\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\n\nconst accountId = props.accountId ?? context.accountId;\n\nif (!accountId) {\n return (\n <Widget\n src={`calimero.near/widget/Calimero.DocsChain.Popups.TextPopup`}\n config={redirectConfig}\n props={{\n componentOwnerId,\n popupText: 'Please login to NEAR Socials to continue!',\n }}\n />\n );\n}\n\nconst PageContainer = styled.div`\n width: 100%;\n height: 100vh;\n padding-left: 60px;\n padding-right: 60px;\n background-color: #0e0e10;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #5765f2;\n :hover {\n background-color: #717cf0;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nState.init({\n bootstraping: true,\n createPage: { open: false, private: false },\n onOpenCreatePage: (isPrivate) =>\n State.update({ openCreatePage: { open: true, private: isPrivate } }),\n});\n\nconst joinDocs = () => {\n Near.requestCalimeroFak(contract);\n};\n\nconst isMember = () => {\n return Near.calimeroView(contract, 'get_account', {\n account_id: context.accountId,\n });\n};\n\nconst verifyKey = () => {\n Near.hasValidCalimeroFak(contract).then((result) => {\n State.update({ bootstraping: false, loggedIn: result });\n if (result) {\n if (!isMember()) {\n Near.fakCalimeroCall(contract, 'join');\n }\n }\n });\n};\n\nif (state.bootstraping) {\n verifyKey();\n}\n\nreturn (\n <PageContainer>\n <>\n {state.bootstraping ? (\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Popups.TextPopup'}\n config={redirectConfig}\n props={{\n popupText: 'Loading...',\n }}\n />\n ) : (\n <>\n {isMember(context.accountId) ? (\n <>\n <Widget\n src={\n 'calimero.near/widget/Calimero.DocsChain.Navbar.HorizontalNavbar'\n }\n config={redirectConfig}\n />\n <div className=\"d-flex\">\n <Widget\n src={\n 'calimero.near/widget/Calimero.DocsChain.Sidebar.DocsSidebar'\n }\n config={redirectConfig}\n props={{\n onOpenCreatePage: state.onOpenCreatePage,\n createPageOpen: state.createPage.open,\n }}\n />\n </div>\n </>\n ) : (\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Popups.JoinPopup'}\n config={redirectConfig}\n props={{\n join: () => joinDocs(),\n }}\n />\n )}\n </>\n )}\n </>\n </PageContainer>\n);\n" }, "Calimero.DocsChain.MainNavigation": { "": "const currentPill = props.currentNavPill || '';\nconst contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst pills = [\n {\n id: 'articles',\n title: 'Articles',\n widgetName: 'Main',\n },\n {\n id: 'authors',\n title: 'Authors',\n widgetName: 'Authors',\n },\n {\n id: 'create',\n title: 'Create Article',\n widgetName: 'CreateArticle',\n },\n];\n\nreturn (\n <div>\n <ul className=\"nav nav-pills nav-fill mb-4\">\n {pills.map(({ id, title, widgetName }, i) => (\n <li className=\"nav-item\">\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.${widgetName}`,\n )}\n className={`nav-link ${\n id === currentPill ? 'active' : ''\n } text-decoration-none`}\n >\n {title}\n </a>\n </li>\n ))}\n </ul>\n </div>\n);\n" }, "Calimero.TaskChain.SideMenu.BoardOptions.BoardMembers": { "": "const {\n addMemberInputOpen,\n componentOwnerId,\n setSelectedUser,\n setMemberInputOpen,\n selectedUser,\n boardMembers,\n users,\n addMember,\n addMemberStatus,\n onSelectRemovingMember,\n} = props;\n\nconst OptionContainer = styled.div`\n display: flex;\n flex-direction: column;\n width: 100%;\n position: relative;\n`;\n\nconst AddMemberButton = styled.div`\n color: #0e0e10;\n background-color: #d0fc42;\n display: flex;\n flex-direction: row;\n ${({ center }) => center && 'justify-content: center;'}\n border-radius: 4px;\n height: 40px;\n gap: 4px;\n padding-top: 8px;\n padding-bottom: 8px;\n padding-left: 16px;\n padding-right: 16px;\n cursor: pointer;\n :hover {\n opacity: 0.8;\n }\n`;\n\nconst MemberListItem = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n cursor: pointer;\n`;\n\nconst MemberList = styled.div`\n margin-top: 16px;\n margin-bottom: 16px;\n overflow-y: scroll;\n max-height: 24rem;\n scroll-behavior: smooth;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-track {\n background-color: #1d1d21;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst MemberInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst MemberId = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst RemoveMemberButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst AddUserButton = styled.button`\n background-color: #D0FC42;\n :hover {\n back\n }\n padding: 2px 8px 2px 8px;\n width: 100%\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: center;\n`;\n\nreturn (\n <OptionContainer>\n {addMemberInputOpen ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.UserDropdown`}\n props={{\n users,\n componentOwnerId,\n onClick: setSelectedUser,\n onClose: () => {\n setMemberInputOpen(false);\n setSelectedUser(undefined);\n },\n selectedUser,\n addMemberStatus,\n }}\n />\n ) : (\n <AddMemberButton onClick={() => setMemberInputOpen(true)}>\n <i className=\"bi bi-plus-circle-fill\"></i>\n <p>Add new Member</p>\n </AddMemberButton>\n )}\n <MemberList>\n {boardMembers.length &&\n boardMembers.map((member, id) => (\n <MemberListItem key={id}>\n <MemberInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: member.id,\n componentOwnerId,\n }}\n />\n <MemberId>{member.id}</MemberId>\n </MemberInfo>\n <RemoveMemberButton onClick={() => onSelectRemovingMember(member)}>\n <i className=\"bi bi-x-circle\"></i>\n </RemoveMemberButton>\n </MemberListItem>\n ))}\n </MemberList>\n {selectedUser && (\n <AddMemberButton center={true} onClick={() => addMember(selectedUser.id)}>\n Add member\n </AddMemberButton>\n )}\n </OptionContainer>\n);\n" }, "Calimero.DocsChain.Popups.TextPopup": { "": "const PageContainer = styled.div`\n height: 100vh;\n width: 100%;\n background-color: #0e0e10;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n border-radius: 8px;\n width: fit-content;\n`;\n\nconst JoinContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n text: center;\n`;\n\nconst JoinHeader = styled.div`\n display: flex;\n align-items: center;\n flex-direction: column;\n justify-content: center;\n gap: 1.5rem;\n padding-bottom: 1rem;\n padding-top: 1rem;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Button = styled.button`\n background-color: #4e95ff;\n :hover {\n background-color: #5bb0ff;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <PageContainer>\n <PopupContainer>\n <JoinContainer>\n <JoinHeader>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Logo.DocsChainLogo'}\n props={{\n justify: true,\n }}\n />\n <Text>{props.popupText}</Text>\n </JoinHeader>\n </JoinContainer>\n </PopupContainer>\n </PageContainer>\n);\n" }, "Calimero.Curb.AppContainer": { "": "// dependencies\nconst EventEmitter = props.deps.EventEmitter;\n\nconst componentOwnerId = props.componentOwnerId;\nconst contract = props.contract;\nconst encryptionUrl = props.encryptionUrl;\nconst accountId = props.accountId;\nconst enableCommunities = props.enableCommunities;\nconst wsApi = props.wsApi;\nconst handleContractChange = props.handleContractChange;\nconst initialChat = props.initialChat;\nconst updateInitialChat = props.updateInitialChat;\nconst communities = props.communities;\n\n/**\n * Types of chats.\n * @typedef {object} ChatTypes\n * @property {string} CHANNEL - Designates a multi-user chat.\n * @property {string} DIRECT_MESSAGE - Designates a one-to-one chat.\n */\nconst ChatTypes = {\n CHANNEL: 'channel',\n DIRECT_MESSAGE: 'direct_message',\n};\n\n/**\n * `curbApi` is a utility object containing several methods for interacting\n * with a chat API and managing encryption in a chat application.\n *\n * @type {Object}\n * @property {function} createGroup - Sends an API request to create a new chat group.\n * @property {function} inviteUser - Invites a user to a channel.\n * @property {function} getChannels - Retrieves all channels that a user is part of.\n * @property {function} getDMs - Retrieves all direct messages for the user.\n * @property {function} getUnreadMessages - Retrieves all unread messages for the user.\n * @property {function} getChannelMeta - Fetches metadata about a particular channel.\n * @property {function} leaveChannel - Leaves a specified channel.\n * @property {function} createChannel - Creates a new channel.\n * @property {function} toggleReaction - Toggles a reaction to a message.\n * @property {function} fetchMessages - Fetches messages from a specified chat.\n * @property {function} fetchKey - Obtains the encryption key for a particular chat.\n * @property {function} sendMessage - Sends a message to a user or channel.\n *\n * @property {function} createGroup\n * @param {string} name - The name of the new group.\n * @returns {Promise} - A Promise that resolves with the result of the API call.\n *\n * @property {function} inviteUser\n * @param {Object} param\n * @param {string} param.account - The account of the user to invite.\n * @param {string} param.channel - The name of the channel to which the user is being invited.\n * @returns {Promise} - A Promise that resolves with the result of the API call.\n *\n * @property {function} getChannels\n * @returns {Promise<Array>} - A Promise that resolves with an array of channels.\n *\n * @property {function} fetchKey\n * @param {Object} param\n * @param {Object} param.chat - An object containing the chat details.\n * @returns {Promise<string>} - A Promise that resolves with the fetched key.\n *\n * @property {function} sendMessage\n * @param {Object} param\n * @param {string} param.message - The message to be sent.\n * @param {string} [param.img] - The image to be sent (if any).\n * @param {string} [param.toAccount] - The account to which the message is to be sent.\n * @param {string} [param.toChannel] - The channel to which the message is to be sent.\n * @param {string} param.key - The encryption key for the message.\n * @param {string} [param.threadId] - The ID of the thread to which the message belongs.\n * @throws {string} - Throws an error message if the required parameters are not valid.\n * @returns {Promise} - A Promise that resolves with the result of the API call.\n */\nconst curbApi = useMemo(() => {\n // Send transactions in order and wait for the previous transaction to be confirmed before sending the next one\n const meroshipSend = (method, params, callback, reschedule) => {\n transactionQueue.push({ method, params, callback, reschedule });\n processQueue();\n };\n\n let transactionQueue = [];\n let isProcessing = false;\n\n const processQueue = () => {\n if (transactionQueue.length === 0 || isProcessing) {\n return;\n }\n\n isProcessing = true;\n const { method, params, callback, reschedule } = transactionQueue.shift();\n\n Calimero.fakSignTx(contract, method, params).then(([_, signedTx]) => {\n wsApi.methods.submitTx(\n signedTx,\n (err, result) => {\n if (err) {\n console.log(\n 'Error: Calimero.Curb.AppContainer.meroshipSend error',\n contract,\n method,\n params,\n err,\n );\n // add retry logic\n callback?.(err);\n } else {\n console.log(\n 'Calimero.Curb.AppContainer.meroshipSend result',\n result,\n );\n callback?.(null, result);\n }\n isProcessing = false;\n processQueue();\n },\n reschedule,\n );\n });\n };\n\n const getStorageKey = (chatType, id) => `${contract}:${id}:${chatType}`;\n const fetchKey = ({ chat }) => {\n return new Promise((resolve, reject) => {\n if (!chat || !chat.type) {\n return reject(\n 'Error: Invalid chat object, you need to provide a valid chat object to fetch the key',\n );\n }\n const storageKey = getStorageKey(\n chat.type,\n chat.type === ChatTypes.CHANNEL ? chat.name : chat.id,\n );\n const storedEncriptionKey = Storage.privateGet(storageKey);\n if (storedEncriptionKey) {\n return resolve(storedEncriptionKey);\n }\n const nonce = toHexString(Crypto.randomBytes(16));\n Calimero.sign(contract, Buffer.from(accountId + '|' + nonce))\n .then((signature) => {\n const keyBody = {\n from: accountId,\n contract,\n };\n if (chat.type === ChatTypes.DIRECT_MESSAGE) {\n keyBody.dms = [chat.id];\n } else {\n keyBody.channels = [{ name: chat.name }];\n }\n keyBody.nonce = nonce;\n keyBody.signature = toHexString(signature.signature);\n\n return asyncFetch(encryptionUrl, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(keyBody),\n }).catch((e) => {\n return Promise.reject(e);\n });\n })\n .then((keyData) => {\n for (const [id, key] of Object.entries(keyData.body.dms)) {\n const storageKey = getStorageKey(ChatTypes.DIRECT_MESSAGE, id);\n Storage.privateSet(storageKey, key);\n }\n for (const [id, key] of Object.entries(keyData.body.channels)) {\n const storageKey = getStorageKey(ChatTypes.CHANNEL, id);\n Storage.privateSet(storageKey, key);\n }\n resolve(Storage.privateGet(storageKey));\n })\n .catch((e) => {\n console.log('Error: Calimero.Curb.AppContainer.fetchKey error', e);\n reject(e);\n });\n });\n };\n\n function extractAndAddMentions(inputText) {\n const regexPattern =\n /(@everyone)|(@here)|(@[a-zA-Z0-9_.-]+(?<![-.0-9])(?:\\.testnet|\\.near))/g;\n const uniqueMentions = new Set();\n let match;\n while ((match = regexPattern.exec(inputText)) !== null) {\n uniqueMentions.add(match[0].slice(1, match[0].length));\n }\n return Array.from(uniqueMentions);\n }\n\n const sendMessage = (\n { message, images, chat, files, threadId },\n callback,\n ) => {\n if (!message && images?.length === 0 && files?.length === 0) {\n // Nothing to send\n return;\n }\n if (!chat) {\n throw 'You need to provide a chat object to send the message';\n }\n return fetchKey({ chat }).then((key) => {\n const params = {};\n const encrypted = encrypt(message, images ?? [], files ?? [], key);\n const mentionsArray = extractAndAddMentions(message);\n if (chat.type === ChatTypes.DIRECT_MESSAGE) {\n params.account = chat.id;\n } else {\n params.group = { name: chat.name };\n }\n if (mentionsArray.length) {\n params.mentions = mentionsArray;\n }\n if (encrypted.images.length > 0) {\n params.images = encrypted.images;\n } else if (encrypted.files.length > 0) {\n params.files = encrypted.files;\n }\n\n params.message = encrypted.text;\n params.nonce = encrypted.nonce;\n params.timestamp = Date.now();\n params.parent_message = threadId ?? undefined;\n try {\n meroshipSend('send_message', params, callback, (retry) =>\n chat.type === ChatTypes.CHANNEL\n ? curbApi.once(\n 'subscribed',\n (name) => name === chat.name && retry(),\n )\n : retry(),\n );\n } catch (e) {\n return Promise.reject(e);\n }\n });\n };\n const parseHexString = (hexString) => {\n const result = [];\n while (hexString.length >= 2) {\n result.push(parseInt(hexString.substring(0, 2), 16));\n hexString = hexString.substring(2, hexString.length);\n }\n return result;\n };\n\n const deleteMessage = ({ message, chat, callback }) => {\n const params = {\n message_id: message.id,\n };\n if (chat.type === ChatTypes.DIRECT_MESSAGE) {\n params.account = chat.id;\n } else {\n params.group = { name: chat.name };\n }\n if (message.parent_message) {\n params.parent_message = message.parent_message;\n }\n try {\n meroshipSend('delete_message', params, callback, (retry) =>\n chat.type === ChatTypes.CHANNEL\n ? curbApi.once('subscribed', (name) => name === chat.name && retry())\n : retry(),\n );\n } catch (e) {\n return Promise.reject(e);\n }\n };\n\n const toHexString = (byteArray) => {\n let result = '';\n for (let byte of byteArray) {\n result += ('0' + (byte & 0xff).toString(16)).slice(-2);\n }\n return result;\n };\n\n const encryptWithCipher = (data, key, nonce) => {\n const cipher = Crypto.createCipheriv(\n 'aes-256-cbc',\n parseHexString(key),\n nonce,\n );\n let encryptedData = cipher.update(data, 'utf8', 'base64');\n encryptedData += cipher.final('base64');\n return encryptedData;\n };\n\n const encrypt = (text, images, files, key, prevNonce) => {\n let nonce;\n if (prevNonce) {\n nonce = [];\n let hexString = prevNonce;\n while (hexString.length >= 2) {\n nonce.push(parseInt(hexString.substring(0, 2), 16));\n hexString = hexString.substring(2, hexString.length);\n }\n } else {\n nonce = Crypto.randomBytes(16);\n }\n console.log('prev', text, key, nonce, encrypted);\n let encrypted = encryptWithCipher(text, key, nonce);\n console.log('encrypt', text, key, nonce, encrypted);\n let encryptedImages = [];\n let encryptedFiles = [];\n if (images.length > 0) {\n encryptedImages = images.map((img) => ({\n name: encryptWithCipher('image.png', key, nonce),\n ipfs_cid: encryptWithCipher(img.ipfs_cid, key, nonce),\n }));\n } else if (files.length > 0) {\n // TODO: handle multiple files and images\n encryptedFiles = files.map((file) => ({\n name: encryptWithCipher(file.name, key, nonce),\n ipfs_cid: encryptWithCipher(file.ipfs_cid, key, nonce),\n }));\n }\n return {\n text: encrypted,\n images: encryptedImages,\n files: encryptedFiles,\n nonce: toHexString(nonce),\n };\n };\n\n function decryptMessages(messages, key) {\n if (!key || key.length === 0) {\n return [];\n }\n const decryptedMessages = messages.map((msg) => {\n return {\n ...msg,\n text: decrypt(msg.text, key, msg.nonce),\n files: msg.files?.length\n ? [\n {\n name: decrypt(msg.files[0].name, key, msg.nonce),\n ipfs_cid: decrypt(msg.files[0].ipfs_cid, key, msg.nonce),\n },\n ]\n : [],\n images: msg.images?.length\n ? [\n {\n name: decrypt(msg.images[0].name, key, msg.nonce),\n ipfs_cid: decrypt(msg.images[0].ipfs_cid, key, msg.nonce),\n },\n ]\n : [],\n reactions: msg.reactions ?? [],\n };\n });\n return decryptedMessages;\n }\n function decrypt(text, key, nonce) {\n if (!key) {\n return null;\n }\n const byteNonce = [];\n let hexString = nonce;\n while (hexString.length >= 2) {\n byteNonce.push(parseInt(hexString.substring(0, 2), 16));\n hexString = hexString.substring(2, hexString.length);\n }\n\n try {\n const decipher = Crypto.createDecipheriv(\n 'aes-256-cbc',\n parseHexString(key),\n byteNonce,\n );\n let decrypted = decipher.update(text, 'base64', 'utf8');\n decrypted += decipher.final('utf8');\n return decrypted;\n } catch (e) {\n console.log('Calimero.decrypt error: ', e.toString());\n return null;\n }\n }\n\n let appEvents = new EventEmitter();\n\n return {\n utils: { toHexString, parseHexString },\n decryptMessages,\n fetchKey,\n on: (event, listener) => {\n appEvents.on(event, listener);\n return curbApi;\n },\n once: (event, listener) => {\n appEvents.once(event, listener);\n return curbApi;\n },\n off: (event, listener) => {\n appEvents.off(event, listener);\n return curbApi;\n },\n prependListener: (event, listener) => {\n appEvents.prependListener(event, listener);\n return curbApi;\n },\n prependOnceListener: (event, listener) => {\n appEvents.prependOnceListener(event, listener);\n return curbApi;\n },\n emit: (event, data) => {\n appEvents.emit(event, data);\n return curbApi;\n },\n createGroup: (name, isPrivate, isReadOnly) =>\n Near.fakCalimeroCall(contract, 'create_group', {\n group: { name },\n channel_type: isPrivate ? 'Private' : 'Public',\n read_only: isReadOnly,\n creator: accountId,\n }),\n inviteUser: ({ account, channel }) =>\n Near.fakCalimeroCall(contract, 'group_invite', {\n group: { name: channel },\n account,\n }),\n getChannels: () =>\n Near.asyncCalimeroView(contract, 'get_groups', {\n account: accountId,\n }).then((groups) =>\n groups.map((group) => ({ ...group, type: ChatTypes.CHANNEL })),\n ),\n getFilteredMembers: (namePrefix, channelName) =>\n Near.asyncCalimeroView(contract, 'get_members', {\n name_prefix: namePrefix,\n group: { name: channelName },\n exclude: true,\n }).then((members) =>\n members.map((member) => ({\n ...member,\n type: ChatTypes.DIRECT_MESSAGE,\n })),\n ),\n getDMs: () =>\n Near.asyncCalimeroView(contract, 'get_direct_messages', {\n account: accountId,\n }),\n fetchAccounts: ({ groupName, prefix, limit, exclude }) =>\n Near.asyncCalimeroView(contract, 'get_members', {\n group: groupName ? { groupName } : undefined,\n name_prefix: prefix,\n limit,\n exclude,\n }),\n getAccountsStatus: (accounts) =>\n new Promise((resolve, reject) => {\n wsApi.methods.getAccountsStatus(accounts, (err, result) => {\n if (err) {\n reject(err);\n return;\n }\n resolve(result);\n });\n }),\n getAppName: () =>\n Near.asyncCalimeroView(contract, 'public_info').then(({ name }) => name),\n getChannelMembers: (channelName) =>\n Near.asyncCalimeroView(contract, 'get_members', {\n group: { name: channelName },\n }),\n getUnreadMessages: () =>\n Near.asyncCalimeroView(contract, 'unread_messages', {\n account: accountId,\n }),\n getChannelMeta: (channelName) =>\n Near.asyncCalimeroView(contract, 'channel_info', {\n group: { name: channelName },\n }),\n promoteModerator: (accountId, channelName, isAdd) =>\n Near.fakCalimeroCall(contract, 'set_group_moderator', {\n group: { name: channelName },\n moderator: accountId,\n is_add: isAdd,\n }),\n removeUserFromChannel: (accountId, channelName) =>\n Near.fakCalimeroCall(contract, 'leave_group', {\n group: { name: channelName },\n account: accountId,\n }),\n leaveChannel: (channelName) =>\n Near.fakCalimeroCall(contract, 'leave_group', {\n group: { name: channelName },\n account: accountId,\n }),\n createChannel: (channelName) =>\n Near.fakCalimeroCall(contract, 'create_group', {\n group: { channelName },\n }),\n toggleReaction: ({ messageId, reaction }, callback) => {\n meroshipSend(\n 'toggle_reaction',\n {\n message_id: messageId,\n reaction,\n },\n callback,\n );\n },\n fetchMessages: ({ chat, beforeId, afterId, limit, parentMessageId }) =>\n new Promise((resolve, reject) => {\n if (!chat || !chat.type) {\n reject(`Error: Invalid chat object ${JSON.stringify(chat)}`);\n return;\n }\n let args = { limit: 1000000 };\n if (chat.type === ChatTypes.CHANNEL && chat.name) {\n args.group = { name: chat.name };\n } else if (chat.type === ChatTypes.DIRECT_MESSAGE && chat.id) {\n args.accounts = [accountId, chat.id];\n } else {\n reject('Error: Invalid chat object');\n return;\n }\n if (beforeId) {\n args.before_id = beforeId;\n } else if (afterId) {\n args.after_id = afterId;\n }\n if (limit) {\n args.limit = limit;\n }\n if (parentMessageId) {\n args.parent_message = parentMessageId;\n }\n Promise.all([\n fetchKey({ chat }),\n Near.asyncCalimeroView(contract, 'get_messages', args),\n ])\n .then(([key, messagesResponse]) => {\n const { messages, totalCount, startPosition } = messagesResponse;\n const result = {\n messages: decryptMessages(messages, key),\n totalCount,\n hasOlder: startPosition !== 0,\n };\n resolve(result);\n })\n .catch((e) => {\n console.log('Error: Calimero.Curb.Chat.fetchMessages error', e);\n reject(e);\n });\n }),\n sendMessage,\n deleteMessage,\n editMessage: ({ message, chat }, callback) => {\n if (!chat || !chat.type) {\n return;\n }\n return fetchKey({ chat }).then((key) => {\n const params = {};\n console.log('editMessage', message, key, message.nonce);\n const encrypted = encrypt(\n message.text,\n message.images,\n message.files,\n key,\n message.nonce,\n );\n if (chat.type === ChatTypes.DIRECT_MESSAGE) {\n params.account = chat.id;\n } else {\n params.group = { name: chat.name };\n }\n params.new_message = encrypted.text;\n params.message_id = message.id;\n params.images = [];\n params.files = [];\n if (encrypted.images && encrypted.images.length > 0) {\n params.images = encrypted.images;\n } else if (encrypted.files && encrypted.files.length > 0) {\n params.files = encrypted.files;\n }\n try {\n meroshipSend('edit_message', params, callback, (retry) =>\n chat.type === ChatTypes.CHANNEL\n ? curbApi.once(\n 'subscribed',\n (name) => name === chat.name && retry(),\n )\n : retry(),\n );\n } catch (e) {\n return Promise.reject(e);\n }\n });\n },\n readMessage: ({ chat, messageId }, callback) => {\n if (!chat || !chat.type) {\n return;\n }\n const readMessageParams =\n chat.type === ChatTypes.CHANNEL\n ? {\n group: { name: chat.name },\n }\n : {\n account: chat.id,\n };\n meroshipSend(\n 'read_message',\n {\n message_id: messageId,\n ...readMessageParams,\n },\n callback,\n );\n },\n getCommunities: (contracts) => {\n return Promise.all(\n contracts.map((contract) =>\n Near.asyncCalimeroView(contract, 'public_info').then(\n (publicInfo) => ({\n ...publicInfo,\n assets: JSON.parse(publicInfo.assets),\n contract,\n }),\n ),\n ),\n );\n },\n };\n}, [contract, accountId]);\n\n/**\n * Initial version of the chat object, currently only supports channels and p2p DMs.\n * @typedef {object} Chat\n * @property {string} type - Can be 'channel' or 'direct_message'.\n * @property {string} [name] - Name of the channel.\n * @property {string} [account] - Account for direct message.\n */\nconst [activeChat, setActiveChat] = useState({\n type: ChatTypes.CHANNEL,\n name: 'general',\n readOnly: false,\n});\n\nuseEffect(() => {\n if (initialChat) {\n setActiveChat(initialChat);\n updateInitialChat(initialChat);\n }\n}, [initialChat]);\n\nconst [isSidebarOpen, setIsSidebarOpen] = useState(false);\n\nconst ContentDivContainer = styled.div`\n width: 100%;\n @media (min-width: 1025px) {\n height: calc(100vh - 169px);\n display: flex;\n }\n`;\n\nconst Wrapper = styled.div`\n @media (min-width: 1025px) {\n flex: 1;\n }\n`;\nconst updateSelectedActiveChat = useCallback((selectedChat) => {\n setActiveChat(selectedChat);\n setIsSidebarOpen(false);\n updateInitialChat(selectedChat);\n}, []);\n\nconst [isWsConnectionActive, setIsWsConnectionActive] = useState(false);\n\nuseEffect(() => {\n let connected = () => setIsWsConnectionActive(true);\n let disconnected = () => setIsWsConnectionActive(false);\n\n wsApi.on('connected', connected).on('disconnected', disconnected).connect();\n\n return () =>\n wsApi\n .off('connected', connected)\n .off('disconnected', disconnected)\n .disconnect();\n}, []);\n\nreturn (\n <>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.NavbarContainer`}\n props={{\n componentOwnerId,\n activeChat,\n isSidebarOpen,\n setIsSidebarOpen,\n curbApi,\n enableCommunities,\n }}\n />\n <ContentDivContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ChannelsContainer`}\n props={{\n componentOwnerId,\n curbApi,\n onChatSelected: updateSelectedActiveChat,\n activeChat,\n isSidebarOpen,\n enableCommunities,\n isWsConnectionActive,\n wsApi,\n handleContractChange,\n communities,\n selectedCommunity: contract,\n }}\n />\n {!isSidebarOpen && (\n <Wrapper>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.ChatContainer`}\n props={{\n componentOwnerId,\n contract,\n curbApi,\n activeChat,\n accountId,\n wsApi,\n isWsConnectionActive,\n }}\n />\n </Wrapper>\n )}\n </ContentDivContainer>\n </>\n);\n" }, "Calimero.Curb.ChannelNavbarContainer": { "": "const activeChat = props.activeChat;\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\n\nconst generator = () => curbApi.getChannelMembers(activeChat.name);\nconst cachedMembers = useCache(generator, 'channel_members', {\n subscribe: true,\n});\n\nconst [members, setMembers] = useState([]);\n\nuseEffect(() => {\n if (cachedMembers) {\n setMembers(cachedMembers);\n }\n}, [cachedMembers]);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.CurbNavbar`}\n props={{\n ...props,\n channelUserList: members,\n }}\n />\n);\n" }, "Calimero.DocsChain.ArticleFooter": { "": "const { author, timeCreated, lastEditor, timeLastEdit, version } = props;\nconst getDate = (timestamp) => {\n const date = new Date(Number(timestamp) / 1e6);\n return date.toDateString();\n};\n\nreturn (\n <div className=\"alert alert-secondary bg-white\">\n <div>\n Created by{' '}\n <a\n href={`https://near.social/#/mob.near/widget/ProfilePage?accountId=${author}`}\n target=\"_blank\"\n style={{ textDecoration: 'underline' }}\n rel=\"noreferrer\"\n >\n {author}\n </a>\n <br />\n {timeCreated && (\n <>\n Posted on {getDate(timeCreated)}\n <br />\n </>\n )}\n <br />\n Last edit by{' '}\n <a\n href={`https://near.social/#/mob.near/widget/ProfilePage?accountId=${lastEditor}`}\n style={{ textDecoration: 'underline' }}\n >\n {lastEditor}\n </a>\n <br />\n Edited on {getDate(timeLastEdit)}\n <br />\n <br />\n Edit versions: {version}\n </div>\n </div>\n);\n" }, "Calimero.DocsChain.MarkdownEditorIframe": { "": "const initialText = props.initialText ?? '# Hello World\\n\\n';\n\nconst code = `\n<script src=\"https://unpkg.com/react@18/umd/react.development.js\" crossorigin></script>\n<script src=\"https://unpkg.com/react-dom@18/umd/react-dom.development.js\" crossorigin></script>\n<script src=\"https://unpkg.com/react-markdown-editor-lite@1.3.4/lib/index.js\" crossorigin></script>\n<link rel=\"stylesheet\" href=\"https://unpkg.com/react-markdown-editor-lite@1.3.4/lib/index.css\" />\n\n\n<div id=\"react-root\"></div>\n\n<script>\nfunction TestReact(props) {\n const [value, setValue] = React.useState(props.initialText || \"\");\n return React.createElement(ReactMarkdownEditorLite.default, {\n value,\n view: { menu: true, md: true, html: false },\n canView: { menu: true, md: false, html: false, fullScreen: false, hideMenu: true },\n onChange: ({ text }) => {\n setValue(text);\n window.top.postMessage(text, \"*\");\n },\n renderHTML: () => {},\n className: \"full\",\n }); \n}\n\nconst domContainer = document.querySelector('#react-root');\nconst root = ReactDOM.createRoot(domContainer);\n\nwindow.addEventListener(\"message\", (event) => {\n root.render(React.createElement(TestReact, {\n initialText: event.data,\n }));\n});\n\n</script>\n`;\n\nreturn (\n <iframe\n className=\"w-100 h-100\"\n srcDoc={code}\n message={initialText}\n onMessage={props.onChange}\n />\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.Comments": { "": "const { componentOwnerId, contract, projectId, taskId } = props;\n\nconst Comments = styled.div`\n width: 100%;\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: 84px;\n margin-top: 1rem;\n`;\n\nconst CommentsContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 10px;\n width: 520px;\n`;\n\nconst OldComments = styled.div`\n max-height: 300px;\n overflow-y: scroll;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 104px;\n height: 40px;\n padding: 8px 60px 8px 16px;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n border: none;\n`;\n\nconst [comments, setComments] = useState(props.comments);\n\nconst addComment = useCallback(\n (comment) => {\n const newComment = {\n id: comments.length + 1,\n member_id: context.accountId,\n message: comment,\n date: new Date(),\n };\n\n setComments(comments.concat(newComment));\n\n return Near.fakCalimeroCall(contract, 'comment_on_task', {\n project_id: projectId,\n task_id: taskId,\n message: comment,\n });\n },\n [comments, contract, projectId, taskId],\n);\n\nconst getComments = useCallback(\n (taskId) => {\n try {\n Near.fakCalimeroCall(contract, 'get_task', {\n project_id: projectId,\n task_id: taskId,\n }).then((task) => {\n setComments(task.comments);\n });\n } catch (e) {\n console.log('getComments', e);\n }\n },\n [setComments],\n);\n\nuseEffect(() => {\n if (taskId) {\n getComments(taskId);\n }\n}, [taskId]);\n\nreturn (\n <Comments>\n <Label>Comments</Label>\n <CommentsContainer>\n {comments && (\n <OldComments>\n {comments.map((comment, id) => (\n <Widget\n key={comment.id + id}\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.Comment`}\n props={{\n comment,\n componentOwnerId,\n }}\n />\n ))}\n </OldComments>\n )}\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.CommentInput`}\n props={{\n componentOwnerId,\n contract,\n projectId,\n taskId,\n addComment,\n }}\n />\n </CommentsContainer>\n </Comments>\n);\n" }, "Calimero.Curb.Communities.CommunityDropdown": { "": "const {\n componentOwnerId,\n communities,\n handleContractChange,\n selectedCommunity,\n} = props;\n\nconst SelectTrigger = styled('Select.Trigger')`\n display: flex;\n align-items: center;\n width: 293px;\n height: 63px;\n padding: 16px;\n border-radius: 4px;\n gap: 16px;\n background-color: #1e1f28;\n font-family: Helvetica Neue;\n font-weight: 400;\n border: none;\n :focus {\n outline-width: 0px;\n }\n :hover {\n background-color: #2e2e2e;\n }\n ${({ isOpen }) =>\n isOpen ? 'border-radius: 4px 4px 0px 0px;' : 'border-radius: 4px;'}\n @media (max-width: 1024px) {\n width: 350px;\n }\n`;\n\nconst SelectContent = styled('Select.Content')`\n display: flex;\n flex-direction: column;\n background-color: #1e1f28;\n gap: 16px;\n z-index: 20;\n border-radius: 0px 0px 4px 4px;\n cursor: pointer;\n width: 293px;\n @media (max-width: 1024px) {\n width: 350px;\n }\n`;\n\nconst SelectItem = styled('Select.Item')`\n display: flex;\n align-items: center;\n width: 293px;\n height: 63px;\n padding: 16px;\n border-radius: 4px;\n gap: 16px;\n background-color: #1e1f28;\n font-family: Helvetica Neue;\n font-weight: 400;\n border: none;\n :focus {\n outline-width: 0px;\n }\n :hover {\n background-color: #2e2e2e;\n }\n @media (max-width: 1024px) {\n width: 350px;\n }\n`;\n\nconst SelectIcon = styled('Select.Icon')`\n color: #fff;\n transition: transform 3s ease-in-out;\n transform: rotate(${({ isOpen }) => (isOpen ? '180deg' : '0deg')});\n`;\n\nconst CommunityInfo = styled.div`\n display: flex;\n column-gap: 8px;\n width: 220px;\n @media (max-width: 1024px) {\n width: 270px;\n }\n`;\n\nconst CommunityDetails = styled.div`\n display: flex;\n flex-direction: column;\n`;\n\nconst CommunityName = styled.p`\n padding: 0;\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n color: #fff;\n`;\n\nconst CommunityMembersCount = styled.p`\n padding: 0;\n margin: 0;\n font-size: 14px;\n line-height: 17px;\n letter-spacing: 0em;\n text-align: left;\n color: #777583;\n`;\n\nconst Text = styled.p`\n padding: 0;\n margin: 0;\n`;\n\nconst CommunityLogoContainer = styled.div`\n position: relative;\n width: 43px;\n height: 43px;\n`;\n\nconst NotificationsStatusCricle = styled.div`\n position: absolute;\n bottom: 1px;\n right: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background-color: #ff3a3a;\n`;\n\nconst CommunityLogo = styled.div`\n width: 42px;\n height: 42px;\n`;\n\nconst [community, setCommunity] = useState(\n communities.find((x) => x.contract === selectedCommunity),\n);\nconst [isOpen, setIsOpen] = useState(false);\n\nconst toggleOpen = () => {\n setIsOpen((prevIsOpen) => !prevIsOpen);\n};\n\nconst handleCommunityChange = (community) => {\n handleContractChange(community.contract);\n setCommunity(community);\n};\n\nreturn (\n <Select.Root\n value={community}\n onValueChange={handleCommunityChange}\n open={isOpen}\n onOpenChange={toggleOpen}\n >\n <SelectTrigger isOpen={isOpen}>\n <Select.Value value={community}>\n <CommunityInfo>\n {community.assets?.logo && (\n <CommunityLogoContainer>\n <CommunityLogo>\n <img\n src={`https://ipfs.near.social/ipfs/${community.assets.logo}`}\n alt=\"uploaded\"\n />\n </CommunityLogo>\n {community.notifications?.length > 0 && (\n <NotificationsStatusCricle />\n )}\n </CommunityLogoContainer>\n )}\n <CommunityDetails>\n <CommunityName>{community.name}</CommunityName>\n <CommunityMembersCount>{`${community.members} members`}</CommunityMembersCount>\n </CommunityDetails>\n </CommunityInfo>\n </Select.Value>\n <SelectIcon isOpen={isOpen}>\n <i className=\"bi bi-chevron-down\"></i>\n </SelectIcon>\n </SelectTrigger>\n <SelectContent position=\"popper\">\n <Select.Viewport>\n <Select.Group>\n {communities?.map((community, id) => (\n <SelectItem id={id} key={id} value={community} ref=\"forwardedRef\">\n <CommunityInfo>\n {community.assets?.logo && (\n <CommunityLogoContainer>\n <CommunityLogo>\n <img\n src={`https://ipfs.near.social/ipfs/${community.assets.logo}`}\n alt=\"uploaded\"\n />\n </CommunityLogo>\n {community.notifications?.length > 0 && (\n <NotificationsStatusCricle />\n )}\n </CommunityLogoContainer>\n )}\n <CommunityDetails>\n <CommunityName>{community.name}</CommunityName>\n <CommunityMembersCount>{`${community.members} members`}</CommunityMembersCount>\n </CommunityDetails>\n </CommunityInfo>\n </SelectItem>\n ))}\n </Select.Group>\n </Select.Viewport>\n </SelectContent>\n </Select.Root>\n);\n" }, "Calimero.Curb.Navbar.CurbNavbar": { "": "const appName = props.appName;\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\nconst mobileSideMenuOpen = props.mobileSideMenuOpen;\nconst channelSelected = props.channelSelected;\nconst channelUserList = props.channelUserList;\nconst activeChat = props.activeChat;\nconst isSidebarOpen = props.isSidebarOpen;\nconst setIsSidebarOpen = props.setIsSidebarOpen;\nconst enableCommunities = props.enableCommunities;\n\nconst NavigationBar = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-left: 1rem;\n padding-right: 1rem;\n border-bottom: 1px solid #282933;\n @media (max-width: 1024px) {\n ${({ isSidebarOpen }) =>\n isSidebarOpen ? 'display: none;' : 'display: flex;'}\n position: fixed;\n top: 64px;\n left: 0;\n right: 0;\n z-index: 1000;\n }\n`;\n\nconst CurbNameContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-size: 20.923px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-right: 0.875rem;\n`;\nconst VerticalSeparatorFull = styled.div`\n width: 1px;\n height: 80px;\n ${({ enableCommunities }) => enableCommunities && 'margin-left: 5.5rem;'}\n background-color: #282933;\n @media (max-width: 1024px) {\n display: none;\n }\n}\n`;\n\nconst OrgNameContainer = styled.div`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%;\n padding-left: 1rem;\n padding-right: 6rem;\n padding-top: 1rem;\n padding-bottom: 1rem;\n display: flex;\n justify-content: center;\n align-items: center;\n border-left: 1px solid #282933;\n @media (max-width: 1024px) {\n display: none;\n }\n}\n`;\n\nconst IconPlus = styled.div`\n :hover {\n border: solid 1px #fff;\n }\n cursor: pointer;\n width: 24px;\n height: 24px;\n background-color: #5765f2;\n color: #fff;\n margin-left: 0.5rem;\n display: flex;\n justify-content: center;\n align-items: center;\n border-radius: 50%;\n`;\n\nconst ItemsContainer = styled.div`\n display: flex;\n ${({ align }) => align && 'align-items: center;'}\n`;\n\nconst AddUserButton = () => {\n return (\n <IconPlus>\n <i className=\"bi bi-plus-lg\" />\n </IconPlus>\n );\n};\n\nconst LogoContainer = styled.div`\n display: flex;\n @media (max-width: 1024px) {\n display: none;\n }\n }\n column-gap: 0.5rem;\n align-items: center;\n ${({ justify }) => justify && 'justify-content: center;'}\n`;\n\nconst BackIcon = styled.svg`\n fill: #5765f2;\n display: none;\n @media (max-width: 1024px) {\n display: flex;\n }\n maring-right: 14px;\n`;\n\nconst CurbLogo = () => {\n return (\n <LogoContainer>\n <svg\n width=\"28\"\n height=\"28\"\n viewBox=\"0 0 28 28\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <rect width=\"28\" height=\"28\" rx=\"6\" fill=\"#5770F2\" />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M24.1568 14C24.1568 8.69358 19.8554 4.39215 14.549 4.39215C9.2426 4.39215 4.94117 8.69358 4.94117 14C4.93884 15.7503 5.41672 17.4677 6.32278 18.9653L5.46576 21.8784C5.41655 22.0457 5.41331 22.2231 5.45639 22.392C5.49946 22.561 5.58725 22.7152 5.71053 22.8385C5.83381 22.9618 5.98802 23.0495 6.15696 23.0926C6.3259 23.1357 6.50333 23.1324 6.67058 23.0832L9.58368 22.2262C11.0811 23.1326 12.7986 23.6105 14.549 23.6078C15.4677 23.6078 16.3564 23.4789 17.1977 23.2381C17.1509 22.8994 17.1267 22.5534 17.1267 22.2018C17.1267 18.2267 20.2197 14.974 24.1303 14.7191C24.1479 14.4817 24.1568 14.2419 24.1568 14ZM10.1672 12.9809C10.4375 12.7107 10.804 12.5588 11.1863 12.5588C11.5685 12.5588 11.9351 12.7107 12.2053 12.9809C12.4756 13.2512 12.6274 13.6178 12.6274 14C12.6274 14.3822 12.4756 14.7488 12.2053 15.0191C11.9351 15.2893 11.5685 15.4412 11.1863 15.4412C10.804 15.4412 10.4375 15.2893 10.1672 15.0191C9.89693 14.7488 9.74509 14.3822 9.74509 14C9.74509 13.6178 9.89693 13.2512 10.1672 12.9809ZM16.8927 12.9809C17.163 12.7107 17.5295 12.5588 17.9118 12.5588C18.294 12.5588 18.6605 12.7107 18.9308 12.9809C19.2011 13.2512 19.3529 13.6178 19.3529 14C19.3529 14.3822 19.2011 14.7488 18.9308 15.0191C18.6605 15.2893 18.294 15.4412 17.9118 15.4412C17.5295 15.4412 17.163 15.2893 16.8927 15.0191C16.6224 14.7488 16.4706 14.3822 16.4706 14C16.4706 13.6178 16.6224 13.2512 16.8927 12.9809Z\"\n fill=\"white\"\n />\n </svg>\n <CurbNameContainer>Chat</CurbNameContainer>\n </LogoContainer>\n );\n};\n\nconst AddUserDialog = ({ curbApi, channelName, componentOwnerId }) => {\n const addUser = useCallback(\n (account) => curbApi.inviteUser({ account, channel: channelName }),\n [curbApi, channelName],\n [channelName],\n );\n\n const isValidAccountName = useCallback((value) => {\n const regex = /^[a-z\\d]+[-_]*[a-z\\d]+[-_]*[a-z\\d]+\\.(near|testnet)$/;\n if (!regex.test(value)) {\n return false;\n }\n return true;\n }, []);\n\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.InputPopup`}\n props={{\n componentOwnerId,\n title: `Invite user to #${channelName}`,\n placeholder: 'ex: username.near',\n buttonText: 'Invite',\n functionLoader: addUser,\n colors: {\n base: '#5765f2',\n hover: '#717cf0',\n disabled: '#3B487A',\n },\n toggle: <AddUserButton />,\n validator: isValidAccountName,\n autocomplete: true,\n members: channelUserList,\n }}\n />\n );\n};\n\nconst IconWrapper = styled.div`\n padding: 8px;\n display: flex;\n justify-content: center;\n align-items: center;\n margin-right: 8px;\n`;\n\nconst BackIconContainer = ({ onClick }) => {\n return (\n <IconWrapper onClick={onClick}>\n <BackIcon\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n fill=\"currentColor\"\n className=\"bi bi-chevron-left\"\n viewBox=\"0 0 16 16\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z\"\n />\n </BackIcon>\n </IconWrapper>\n );\n};\n\nreturn (\n <NavigationBar isSidebarOpen={isSidebarOpen}>\n <ItemsContainer align={true}>\n {enableCommunities ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.NearSvgLogo`}\n />\n ) : (\n <>\n <CurbLogo />\n <OrgNameContainer>{appName}</OrgNameContainer>\n </>\n )}\n <VerticalSeparatorFull enableCommunities={enableCommunities} />\n {\n <BackIconContainer\n onClick={() => {\n setIsSidebarOpen(!isSidebarOpen);\n }}\n />\n }\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.DetailsDropdown`}\n props={{\n activeChat,\n componentOwnerId,\n curbApi,\n }}\n />\n </ItemsContainer>\n {channelUserList.length > 0 && (\n <ItemsContainer align={false}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Popups.ChannelDetailsPopup`}\n props={{\n toggle: (\n <div>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.UsersButtonGroup`}\n props={{\n componentOwnerId,\n channelUserList,\n aboutSelected: false,\n }}\n />\n </div>\n ),\n chat: activeChat,\n curbApi,\n componentOwnerId,\n }}\n />\n {/* <AddUserDialog\n curbApi={curbApi}\n channelName={channelSelected}\n componentOwnerId={componentOwnerId}\n /> */}\n </ItemsContainer>\n )}\n </NavigationBar>\n);\n" }, "Calimero.DocsChain.Common.Button": { "": "const Button = styled.div`\n color: #ffffff;\n\n padding: 8px;\n gap: 4px;\n ${({ color }) =>\n color ? `background-color: ${color};` : 'background-color: #1e1f28;'}\n :hover {\n ${({ hoverColor }) =>\n hoverColor\n ? `background-color: ${hoverColor};`\n : 'background-color: #25252A;'}\n }\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: normal;\n line-height: 150%;\n border-radius: 4px;\n ${({ width }) => (width ? `width: ${width};` : 'width: fit-content;')}\n cursor: pointer;\n justify-content: center;\n align-items: center;\n display: flex;\n font-size: 0.875rem;\n line-height: 1.25rem;\n`;\n\nconst Icon = styled.i`\n color: #777583;\n width: 16px;\n height: 16px;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nreturn (\n <Button\n onClick={props.onClick}\n color={props.color}\n hoverColor={props.hoverColor}\n width={props.width}\n >\n {props.icon && <Icon className={props.icon}></Icon>}\n {props.text}\n </Button>\n);\n" }, "Calimero.Common.TimeFormatting": { "": "const formatTimeAgo = (seconds) => {\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const weeks = Math.floor(days / 7);\n const months = Math.floor(weeks / 4);\n\n if (months > 0) {\n return `${months} month${months > 1 ? 's' : ''} ago`;\n } else if (weeks > 0) {\n return `${weeks} week${weeks > 1 ? 's' : ''} ago`;\n } else if (days > 0) {\n return `${days} day${days > 1 ? 's' : ''} ago`;\n } else if (hours > 0) {\n return `${hours} hour${hours > 1 ? 's' : ''} ago`;\n } else if (minutes > 0) {\n return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;\n } else {\n return `just now`;\n }\n};\n\nreturn formatTimeAgo(props.time);\n" }, "WebSocketDemo": { "": "const WS_ADDRESS = 'ws://localhost:8080';\nconst MAX_RETRIES = 5;\nconst RECONNECT_DELAY = 1000;\nconst PONG_WAIT_TIME = 6000;\nconst MAX_DELAY = 30000;\n\nconst userId = context.accountId;\n\nconst ConnectionStatus = {\n CONNECTED: 'Connected',\n RECONNECTING: 'Reconnecting...',\n DISCONNECTED: 'Disconnected',\n};\n\nconst WebSocketState = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n};\n\nconst [messages, setMessages] = useState([]);\nconst [inputValue, setInputValue] = useState('');\nconst [connectionStatus, setConnectionStatus] = useState(\n ConnectionStatus.DISCONNECTED,\n);\nconst [connectedUsers, setConnectedUsers] = useState([]);\nconst [selectedUser, setSelectedUser] = useState(null);\n\nconst socketRef = useRef(null);\nconst retryCountRef = useRef(0);\nconst shouldReconnectRef = useRef(true);\nconst pingIntervalRef = useRef(null);\n\nconst setupWebSocket = () => {\n const ws = new WebSocket(WS_ADDRESS);\n\n ws.onopen = () => {\n console.log('Connected to the WebSocket');\n setConnectionStatus(ConnectionStatus.CONNECTED);\n retryCountRef.current = 0;\n\n if (pingIntervalRef.current) {\n clearInterval(pingIntervalRef.current);\n }\n\n pingIntervalRef.current = setInterval(() => {\n if (ws.readyState === WebSocketState.OPEN) {\n ws.send('ping');\n }\n }, PONG_WAIT_TIME);\n };\n\n ws.onmessage = (event) => {\n console.log('Received message:', event.data);\n\n if (event.data === 'pong') {\n console.log('Received pong');\n setConnectionStatus(ConnectionStatus.CONNECTED);\n return;\n }\n\n const parsedEvent = JSON.parse(event.data);\n if (parsedEvent.type === 'usersList') {\n setConnectedUsers(parsedEvent.users);\n return;\n } else if (parsedEvent.type === 'message') {\n setMessages((prev) => [\n ...prev,\n `Received ${parsedEvent.content} from ${parsedEvent.from}`,\n ]);\n return;\n }\n };\n\n ws.onclose = () => {\n setConnectionStatus(\n ConnectionStatus.RECONNECTING +\n '...' +\n retryCountRef.current +\n 'attempts',\n );\n if (shouldReconnectRef.current && retryCountRef.current < MAX_RETRIES) {\n setTimeout(\n setupWebSocket,\n Math.min(\n RECONNECT_DELAY * Math.pow(2, retryCountRef.current),\n MAX_DELAY,\n ),\n );\n retryCountRef.current++;\n } else {\n setConnectionStatus(ConnectionStatus.DISCONNECTED);\n }\n };\n\n ws.onerror = () => {\n setConnectionStatus(ConnectionStatus.DISCONNECTED);\n };\n\n socketRef.current = ws;\n};\nuseEffect(() => {\n setupWebSocket();\n\n return () => {\n shouldReconnectRef.current = false;\n clearInterval(pingIntervalRef.current);\n if (socketRef.current) {\n socketRef.current.close();\n }\n };\n}, []);\n\nconst handleRegister = () => {\n if (\n socketRef.current &&\n socketRef.current.readyState === WebSocketState.OPEN\n ) {\n socketRef.current.send(JSON.stringify({ register: true, userId }));\n }\n};\n\nconst handleInputChange = (event) => {\n setInputValue(event.target.value);\n};\n\nconst handleSendMessage = () => {\n if (socketRef.current && inputValue !== '' && selectedUser) {\n const messageData = {\n content: inputValue,\n to: selectedUser,\n from: userId,\n };\n socketRef.current.send(JSON.stringify(messageData));\n setInputValue('');\n }\n};\n\nconst handleManualReconnect = () => {\n if (\n !socketRef.current ||\n socketRef.current.readyState === WebSocketState.CLOSED\n ) {\n retryCountRef.current = 0;\n setupWebSocket();\n }\n};\n\nreturn (\n <>\n <div className=\"App\">\n <h2>WebSocket Test Client</h2>\n <h3>Status: {connectionStatus}</h3>\n <button onClick={handleRegister}>Register</button>\n <div>\n <input\n value={inputValue}\n onChange={handleInputChange}\n placeholder=\"Type a message...\"\n />\n <button onClick={handleSendMessage}>Send</button>\n <button onClick={handleManualReconnect}>Manual Reconnect</button>\n </div>\n <h4>Connected Users:</h4>\n {connectedUsers.length === 0 && <p>No connected users</p>}\n {connectedUsers.length > 0 &&\n connectedUsers.map((user, index) => (\n <div style={{ display: 'flex', flexDirection: 'row' }}>\n <input\n style={{ width: '24px', height: '24px', marginRight: '10px' }}\n type=\"radio\"\n name=\"connectedUsers\"\n value={user}\n checked={selectedUser === user}\n onChange={() => setSelectedUser(user)}\n />\n {user}\n </div>\n ))}\n <h4>Messages:</h4>\n <ul>\n {messages.map((message, index) => (\n <li key={index}>{message}</li>\n ))}\n </ul>\n </div>\n </>\n);\n" }, "Calimero.TaskChain.Selector": { "": "const selectorOptions = props.selectorOptions;\nconst selected = props.selected;\nconst color = props.color ?? '#D0FC42';\nconst onClick = props.onClick;\n\nconst SelectorContainer = styled.div`\n display: flex;\n flex-direction: row;\n background-color: transparent;\n gap: 16px;\n`;\n\nconst OptionButton = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 8px;\n ${({ selected }) => (selected ? `color: ${color};` : 'color: #fff;')}\n :hover {\n color: ${color};\n }\n cursor: pointer;\n`;\n\nconst P = styled.p`\n margin: 0;\n`;\n\nreturn (\n <SelectorContainer>\n {selectorOptions.map((option) => (\n <OptionButton\n onClick={() => onClick(option.id)}\n selected={selected === option.id}\n >\n <i className={option.icon}></i>\n <P>{option.title}</P>\n </OptionButton>\n ))}\n </SelectorContainer>\n);\n" }, "Calimero.Curb.Communities.CommunityList": { "": "const communities = props.communities;\nconst filteredCommunities = props.filteredCommunities;\nconst isSearched = props.isSearched;\nconst componentOwnerId = props.componentOwnerId;\n\nconst Container = styled.div`\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: 16px;\n`;\n\nconst CommunitiesContainer = styled.div`\n width: 100%;\n display: grid;\n grid-template-columns: auto auto auto auto auto;\n gap: 16px;\n ${({ isSearched }) => isSearched && 'padding: 0px 24px 0px 24px;'}\n`;\n\nconst SearchBox = styled.input`\n width: 347px;\n padding: 6px 12px 6px 12px;\n border-radius: 4px 0px 0px 4px;\n background-color: #37373f;\n color: #6c757d;\n border: none;\n :focus {\n border-color: #d0fc42;\n border-style: solid;\n border-width: 0.1px;\n outline-width: 0px;\n }\n`;\n\nconst SearchButton = styled.button`\n display: flex;\n column-gap: 8px;\n justify-content: center;\n align-items: center;\n background-color: #d0fc42;\n color: #212529;\n width: 99px;\n padding: 6px 12px 6px 12px;\n border-radius: 0px 4px 4px 0px;\n border: none;\n :hover {\n background-color: #bbe33b;\n }\n`;\n\nconst Title = styled.h4`\n color: #fff;\n text-align: left;\n`;\n\nreturn (\n <Container>\n {!isSearched && <Title>Community list</Title>}\n <CommunitiesContainer isSearched={isSearched}>\n {isSearched\n ? filteredCommunities.map((community) => (\n <Widget\n key={community.id}\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.CommunityCard`}\n props={{\n community,\n }}\n />\n ))\n : communities.map((community) => (\n <Widget\n key={community.id}\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.CommunityCard`}\n props={{\n community,\n }}\n />\n ))}\n </CommunitiesContainer>\n </Container>\n);\n" }, "Calimero.Curb.Chat.MessageFileField": { "": "const file = props.file;\nconst resetFile = props.resetFile;\n\nconst FileContainer = styled.div`\n min-width: 197px;\n max-width: 197px;\n position: relative;\n width: fit-content;\n background-color: #1D1D21;\n display: flex;\n gap: 4px;,\n border-radius: 2px;\n padding: 4px;\n cursor: poiner;\n margin-left: 4px;\n margin-bottom: 4px;\n`;\n\nconst IconContainer = styled.div`\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: #4E95FF;\n border-radius: 2px;\n width: 2rem;\n height: 2rem;\n padding: 0.5rem:\n`;\n\nconst FileInfo = styled.div`\n display: flex;\n flex-direction: column;\n`;\n\nconst FileTitle = styled.div`\n color: #fff;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 130px;\n -webkit-font-smoothing: antialiased applied;\n`;\n\nconst FileExtension = styled.div`\n font-size: 12px;\n font-style: normal;\n font-weight: 400;\n line-height: 100%;\n color: #5c5c71;\n`;\n\nconst FileIcon = ({ className }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill=\"white\"\n className={className ?? 'bi bi-file-earmark-fill'}\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M4 0h5.293A1 1 0 0 1 10 .293L13.707 4a1 1 0 0 1 .293.707V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm5.5 1.5v2a1 1 0 0 0 1 1h2l-3-3z\" />\n </svg>\n);\n\nconst RemoveButton = styled.div`\n position: relative;\n top: -4px;\n right: -4px;\n z-index: 20;\n cursor: pointer;\n`;\n\nconst ResetFileIcon = ({ resetFile }) => {\n return (\n <RemoveButton onClick={resetFile}>\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g clipPath=\"url(#clip0_955_44892)\">\n <path\n d=\"M12 6C12 7.5913 11.3679 9.11742 10.2426 10.2426C9.11742 11.3679 7.5913 12 6 12C4.4087 12 2.88258 11.3679 1.75736 10.2426C0.632141 9.11742 0 7.5913 0 6C0 4.4087 0.632141 2.88258 1.75736 1.75736C2.88258 0.632141 4.4087 0 6 0C7.5913 0 9.11742 0.632141 10.2426 1.75736C11.3679 2.88258 12 4.4087 12 6ZM4.0155 3.4845C3.94509 3.41408 3.84958 3.37453 3.75 3.37453C3.65042 3.37453 3.55491 3.41408 3.4845 3.4845C3.41408 3.55491 3.37453 3.65042 3.37453 3.75C3.37453 3.84958 3.41408 3.94509 3.4845 4.0155L5.46975 6L3.4845 7.9845C3.44963 8.01937 3.42198 8.06076 3.40311 8.10631C3.38424 8.15187 3.37453 8.20069 3.37453 8.25C3.37453 8.29931 3.38424 8.34813 3.40311 8.39369C3.42198 8.43924 3.44963 8.48063 3.4845 8.5155C3.55491 8.58591 3.65042 8.62547 3.75 8.62547C3.79931 8.62547 3.84813 8.61576 3.89369 8.59689C3.93924 8.57802 3.98063 8.55037 4.0155 8.5155L6 6.53025L7.9845 8.5155C8.01937 8.55037 8.06076 8.57802 8.10631 8.59689C8.15187 8.61576 8.20069 8.62547 8.25 8.62547C8.29931 8.62547 8.34813 8.61576 8.39369 8.59689C8.43924 8.57802 8.48063 8.55037 8.5155 8.5155C8.55037 8.48063 8.57802 8.43924 8.59689 8.39369C8.61576 8.34813 8.62547 8.29931 8.62547 8.25C8.62547 8.20069 8.61576 8.15187 8.59689 8.10631C8.57802 8.06076 8.55037 8.01937 8.5155 7.9845L6.53025 6L8.5155 4.0155C8.55037 3.98063 8.57802 3.93924 8.59689 3.89369C8.61576 3.84813 8.62547 3.79931 8.62547 3.75C8.62547 3.70069 8.61576 3.65187 8.59689 3.60631C8.57802 3.56076 8.55037 3.51937 8.5155 3.4845C8.48063 3.44963 8.43924 3.42198 8.39369 3.40311C8.34813 3.38424 8.29931 3.37453 8.25 3.37453C8.20069 3.37453 8.15187 3.38424 8.10631 3.40311C8.06076 3.42198 8.01937 3.44963 7.9845 3.4845L6 5.46975L4.0155 3.4845Z\"\n fill=\"white\"\n />\n </g>\n <defs>\n <clipPath id=\"clip0_955_44892\">\n <rect width=\"12\" height=\"12\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n </RemoveButton>\n );\n};\n\nif (!file) return null;\nconst fileName = file?.name;\nreturn (\n <FileContainer>\n <IconContainer>\n <FileIcon className=\"bi bi-file-earmark-fill\" />\n </IconContainer>\n {fileName && (\n <FileInfo>\n <FileTitle>{fileName}</FileTitle>\n <FileExtension>\n {fileName.split('.').pop()?.toUpperCase()}\n </FileExtension>\n </FileInfo>\n )}\n <ResetFileIcon resetFile={resetFile} />\n </FileContainer>\n);\n" }, "Calimero.DocsChain.Navbar.HorizontalNavbar": { "": "const NavigationBar = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-left: 1rem;\n padding-right: 1rem;\n border-bottom: 1px solid #282933;\n`;\n\nconst VerticalSeparatorFull = styled.div`\n width: 1px;\n height: 80px;\n background-color: #282933;\n`;\n\nconst ItemsContainer = styled.div`\n display: flex;\n ${({ align }) => align && 'align-items: center;'}\n`;\n\nreturn (\n <NavigationBar>\n <ItemsContainer align={true}>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Logo.DocsChainLogo'}\n props={{\n justify: true,\n }}\n />\n <VerticalSeparatorFull />\n </ItemsContainer>\n </NavigationBar>\n);\n" }, "Calimero.TaskChain.ActionStatus.Status": { "": "const { actionStatus, setActionStatusNotVisible } = props;\n\nconst ToastRoot = styled('Toast.Root')`\n display: flex;\n flex-direction: row;\n justify-content: end;\n align-items: center;\n color: #fff;\n background-color: #1e1f28;\n border-radius: 0.5rem;\n padding: 1rem;\n`;\n\nconst Text = styled.p`\n padding: 0;\n margin: 0;\n`;\n\nconst [open, setOpen] = useState(true);\n\nconst onOpenChange = () => {\n setActionStatusNotVisible(actionStatus);\n setOpen(false);\n};\n\nreturn (\n <Toast.Provider swipeDirection=\"right\" duration={3000}>\n <ToastRoot open={open} onOpenChange={onOpenChange}>\n <Toast.Description asChild>\n <Text>{actionStatus.status}</Text>\n </Toast.Description>\n </ToastRoot>\n <Toast.Viewport />\n </Toast.Provider>\n);\n" }, "Calimero.Curb.EmojiSelector.EmojiSelectorPopup": { "": "const OnEmojiSelected = props.OnEmojiSelected;\nconst componentOwnerId = props.componentOwnerId;\nconst onClose = props.onClose;\n\nconst EmojiPopupContainer = styled.div`\n position: fixed;\n left: 0px;\n right: 0px;\n bottom: 0px;\n top: 0px;\n width: 100%;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n z-index: 100;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n z-index: 40;\n position: absolute;\n top: -30px;\n left: 140px;\n cursor: pointer;\n`;\n\nconst CloseButtonContainer = styled.div`\n position: relative;\n`;\n\nreturn (\n <EmojiPopupContainer>\n <CloseButtonContainer>\n <CloseButton onClick={onClose}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n </CloseButtonContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.EmojiSelector.EmojiSelector`}\n props={{\n OnEmojiSelected: (emoji) => OnEmojiSelected(emoji),\n }}\n key={'emojis-popup-component'}\n />\n </EmojiPopupContainer>\n);\n" }, "Calimero.Curb.Logger": { "": "const render = props.render;\n\nfunction Logger(scope, args, debug) {\n scope = typeof scope === 'string' ? { name: scope, stack: [] } : scope;\n let tag = `[${scope.name}]${\n scope.stack.length ? ` {${scope.stack.join(' > ')}}` : ''\n }`;\n let log = (str, args) => debug && console.log(tag, str, args);\n let error = (str, args) => console.log(tag, str, args);\n let span = (name, args) =>\n Logger({ name: scope.name, stack: [...scope.stack, name] }, args, debug);\n if (args && debug) console.log(tag + ' args', args);\n let self = { log, error, span };\n self.fallback = () => (logger) =>\n logger && typeof logger.span === 'function' ? logger : self;\n return self;\n}\n\nreturn render({ Logger });\n" }, "Calimero.TaskChain.Loader.Loader": { "": "const Loader = styled.div`\n display: inline-block;\n ${({ size }) =>\n size\n ? `width: ${size}px; height: ${size}px;`\n : 'width: 20px; height: 20px;'}\n border: 3px solid rgba(255, 255, 255, 0.3);\n border-radius: 50%;\n ${({ color }) =>\n color ? `border-top-color: ${color};` : 'border-top-color: #fff;'}\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n`;\n\nreturn <Loader size={props.size} color={props.color} />;\n" }, "Calimero.TaskChain.CreateBoardDialog": { "": "const {\n open,\n componentOwnerId,\n addCreateBoardStatus,\n accountId,\n handleBoards,\n contract,\n createBoardStatus,\n createBoardButton,\n addBoard,\n} = props;\n\nconst Overlay = styled.div`\n background-color: var(--blackA9);\n position: fixed;\n z-index: 1000;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);\n`;\n\nconst ModalContent = styled.div`\n position: absolute;\n width: 500px;\n padding: 30px;\n border-radius: 20px;\n color: white;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n font-style: normal;\n`;\n\nconst Title = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 30%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n border: none;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingText = styled.div`\n position: absolute;\n right: 36%;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 14px;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DialogTitle = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst [boardName, setBoardName] = useState('');\nconst [addBoardNameMissing, setAddBoardNameMissing] = useState(null);\nconst [isCreateBoardDialogVisible, setIsCreateBoardDialogVisible] =\n useState(false);\n\nconst onChangeBoardName = ({ target }) => {\n setAddBoardNameMissing(null);\n setBoardName(target.value);\n};\n\nconst handleOnClose = useCallback(() => {\n setIsCreateBoardDialogVisible(false);\n setBoardName(null);\n}, []);\n\nconst createBoard = useCallback(async () => {\n let newStatus = { name: boardName, status: 'Saving' };\n try {\n addCreateBoardStatus(newStatus);\n\n const newBoard = [null, boardName];\n\n addBoard(newBoard);\n\n Near.fakCalimeroCall(contract, 'create_new_project', {\n name: boardName,\n owner: accountId,\n members: [accountId],\n statuses: ['TO DO', 'IN PROGRESS', 'DONE'],\n }).then(() => {\n newStatus.status = 'Saved';\n addCreateBoardStatus(newStatus);\n });\n } catch {\n newStatus.status = 'Error';\n addCreateBoardStatus(newStatus);\n }\n setIsCreateBoardDialogVisible(false);\n setBoardName('');\n}, [boardName, addCreateBoardStatus, accountId, contract]);\n\nconst content = (\n <Overlay>\n <ModalContent>\n <PopupContainer>\n <Header>\n <DialogTitle>Create new Board</DialogTitle>\n <CloseButton onClick={handleOnClose}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n </Header>\n <FieldContainer>\n <Title\n onChange={onChangeBoardName}\n value={boardName}\n placeholder=\"Add Name\"\n />\n {addBoardNameMissing && (\n <MissingTitle>{addBoardNameMissing}</MissingTitle>\n )}\n </FieldContainer>\n <FunctionButton\n onClick={() => {\n if (boardName) {\n createBoard();\n } else {\n setAddBoardNameMissing('Missing name');\n }\n }}\n >\n Create\n </FunctionButton>\n </PopupContainer>\n </ModalContent>\n </Overlay>\n);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.BaseModal`}\n props={{\n toggle: createBoardButton,\n content,\n open: isCreateBoardDialogVisible,\n onOpenChange: (open) => setIsCreateBoardDialogVisible(open),\n }}\n />\n);\n" }, "Calimero.TaskChain.SideMenu.EditBoardDialog": { "": "const onClose = props.onClose;\nconst functionLoader = props.functionLoader;\nconst onEditBoardName = props.onEditBoardName;\nconst componentOwnerId = props.componentOwnerId;\nconst selectedProjectId = props.selectedProjectId;\nconst users = props.users;\nconst selectedUser = props.selectedUser;\nconst setSelectedUser = props.setSelectedUser;\nconst addActionStatus = props.addActionStatus;\nconst contract = props.contract;\nconst editedBoard = props.editedBoard;\nconst updateBoardName = props.updateBoardName;\nconst deleteBoard = props.deleteBoard;\nconst onChangeShowEditBoardDialog = props.onChangeShowEditBoardDialog;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n`;\n\nconst ActionsContainer = styled.div`\n display: flex;\n flex-direction: row;\n gap: 2px;\n align-items: center;\n items-center: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Name = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 70%;\n height: 40px;\n padding: 8px 60px 8px 0px;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst CloseButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst P = styled.p`\n display: inline-block;\n width: 100px;\n margin: 0;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 22px;\n margin-bottom: 16px;\n`;\n\nconst SuccessIcon = styled.div`\n color: #00ff66;\n`;\n\nconst ErrorIcon = styled.div`\n color: #dc3545;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst StatusIcon = styled.div`\n padding-right: 60px;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 16px;\n top: 40px;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst EditBoardNameButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n display: flex;\n flex-direction: row;\n cursor: pointer;\n justify-content: center;\n align-items: center;\n gap: 5px;\n`;\n\nconst CloseAddMeberButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n position: absolute;\n right: 10px;\n top: 10px;\n`;\n\nconst FunctionButton = styled.button`\n background-color: red;\n :hover {\n opacity: 0.8;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DeleteColumnContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst CloseDeleteColumnButton = styled.div`\n background-color: transparent;\n display: flex;\n justify-content: center;\n color: #6b7280;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst TextContainer = styled.div`\n display: flex;\n justify-content: center;\n flex-direction: column;\n width: 100%;\n text-align: center;\n margin-bottom: 1rem;\n margin-top: 1rem;\n`;\n\nconst [selectedOption, setSelectedOption] = useState(1);\nconst [\n isRemoveMemberConfirmationVisible,\n setIsRemoveMemberConfirmationVisible,\n] = useState(false);\nconst [removingMember, setRemovingMember] = useState(undefined);\nconst [boardMembers, setBoardMembers] = useState([]);\nconst [addMemberStatus, setAddMemberStatus] = useState(undefined);\nconst [addMemberInputOpen, setMemberInputOpen] = useState(false);\nconst [editedBoardName, setEditedBoardName] = useState(editedBoard[1]);\nconst [editBoardNameStatus, setEditBoardNameStatus] = useState(null);\nconst [editBoardNameMissing, setEditBoardNameMissing] = useState(false);\n\nconst [showDeleteBoardDialog, setShowDeleteBoardDialog] = useState(false);\n\nconst onChangeEditBoardName = ({ target }) => {\n setEditedBoardName(target.value);\n};\n\nconst onChangeShowDeleteBoardDialog = (open) => {\n setShowDeleteBoardDialog(open);\n};\n\nconst editBoardName = useCallback(async () => {\n if (!editedBoardName || editedBoardName.trim() === '') {\n setEditBoardNameMissing(true);\n setTimeout(() => setEditedBoardName(editedBoard[1]), 3000);\n setTimeout(() => setEditBoardNameMissing(false), 3000);\n return;\n }\n if (editedBoardName === editedBoard[1]) {\n return;\n }\n\n try {\n setEditBoardNameStatus('Saving...');\n\n const newBoard = [editedBoard[0], editedBoardName];\n\n updateBoardName(newBoard);\n\n Near.fakCalimeroCall(contract, 'edit_name', {\n project_id: editedBoard[0],\n new_name: editedBoardName,\n }).then(() => {\n setEditBoardNameStatus('Saved');\n setTimeout(() => setEditBoardNameStatus(null), 3000);\n });\n } catch (e) {\n setEditBoardNameStatus('Error saving');\n }\n}, [editedBoardName, editedBoard]);\n\nconst handleDeleteBoard = useCallback(() => {\n const actionStatusPrefix = 'Delete board';\n let newActionStatus = {\n id: actionStatusPrefix + editedBoard[0],\n status: `Deleting board: ${editedBoard[1]}`,\n seen: false,\n };\n try {\n addActionStatus(newActionStatus);\n\n deleteBoard(editedBoard[0]);\n\n Near.fakCalimeroCall(contract, 'delete_project', {\n project_id: editedBoard[0],\n }).then(() => {\n newActionStatus.status = `Deleted board: ${editedBoard[1]}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n newActionStatus.status = `Error deleting board: ${editedBoard[1]}`;\n addActionStatus(newActionStatus);\n }\n onChangeShowEditBoardDialog(false, undefined);\n onChangeShowDeleteBoardDialog(false);\n}, [editedBoard]);\n\nconst removeBoardMember = useCallback(\n (member) => {\n setBoardMembers(boardMembers.filter((m) => m.id !== member.id));\n },\n [boardMembers, setBoardMembers],\n);\n\nconst onSelectRemovingMember = (member) => {\n setIsRemoveMemberConfirmationVisible(true);\n setRemovingMember(member);\n};\n\nconst removeMember = useCallback(() => {\n const actionStatusPrefix = 'Remove member';\n let newActionStatus = {\n id: actionStatusPrefix + editedBoard[0] + removingMember.id,\n status: `Removing member ${removingMember.id} from board ${editedBoardName}`,\n seen: false,\n };\n try {\n addActionStatus(newActionStatus);\n removeBoardMember(removingMember);\n\n Near.fakCalimeroCall(contract, 'remove_member', {\n project_id: selectedProjectId,\n member: removingMember.id,\n }).then(() => {\n newActionStatus.status = `Removed member ${removingMember.id} from board ${boardName}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n console.log('removeMember', e);\n newActionStatus.status = `Error removing member ${removingMember.id} from board ${boardName}`;\n addActionStatus(newActionStatus);\n }\n setIsRemoveMemberConfirmationVisible(false);\n}, [removingMember]);\n\nconst addMember = useCallback(\n async (accountId) => {\n setMemberInputOpen(false);\n const oldMembers = JSON.parse(JSON.stringify(boardMembers));\n try {\n setAddMemberStatus('Saving...');\n Near.fakCalimeroCall(contract, 'add_member', {\n project_id: selectedProjectId,\n new_member: accountId,\n }).then(() => {\n setAddMemberStatus('Saved');\n setTimeout(() => setAddMemberStatus(undefined), 3000);\n });\n setSelectedUser(undefined);\n setBoardMembers([...oldMembers, { id: accountId }]);\n } catch (e) {\n setBoardMembers(oldMembers);\n setAddMemberStatus('Error');\n }\n },\n [boardMembers, selectedProjectId, contract],\n);\n\nconst getMembers = useCallback(\n async (projectId) => {\n try {\n Near.asyncCalimeroView(contract, 'get_members', {\n project_id: projectId,\n }).then((members) => {\n setBoardMembers(members.map((member) => ({ id: member })));\n });\n } catch (e) {\n console.log('getMembers', e);\n }\n },\n [contract],\n);\n\nuseEffect(() => {\n if (selectedProjectId) {\n getMembers(selectedProjectId);\n }\n}, [selectedProjectId]);\n\nconst BoardDetailsContent = () => (\n <OverlayContainer>\n <PopupContainer>\n <Header>\n <FieldContainer>\n <Name\n onChange={onChangeEditBoardName}\n value={editedBoardName}\n onBlur={editBoardName}\n placeholder=\"Add Title\"\n />\n {editBoardNameMissing && <MissingTitle>Missing name</MissingTitle>}\n </FieldContainer>\n <ActionsContainer>\n {editBoardNameStatus && (\n <StatusIcon>\n {editBoardNameStatus === 'Saving...' && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Loader.Loader`}\n props={{ size: 16 }}\n />\n )}\n {editBoardNameStatus === 'Saved' && (\n <SuccessIcon>\n <i className=\"bi bi-check\"></i>\n </SuccessIcon>\n )}\n {editBoardNameStatus === 'Error' && (\n <ErrorIcon>\n <i className=\"bi bi-x-circle\"></i>\n </ErrorIcon>\n )}\n </StatusIcon>\n )}\n <CloseButton onClick={onClose}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </ActionsContainer>\n </Header>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Selector`}\n props={{\n selectorOptions: [\n { id: 1, title: 'About', icon: 'bi bi-info-circle-fill' },\n {\n id: 2,\n title: `Members ${boardMembers.length}`,\n icon: 'bi bi-people-fill',\n },\n ],\n onClick: setSelectedOption,\n selected: selectedOption,\n }}\n />\n <Divider />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.EditBoardOptions`}\n props={{\n onChangeShowDeleteBoardDialog,\n editedBoardId: editedBoard[0],\n addMemberInputOpen,\n componentOwnerId,\n setSelectedUser,\n setMemberInputOpen,\n selectedUser,\n boardMembers,\n selectedOption,\n users: users.filter(\n (user) => !boardMembers.some((member) => member.id === user.id),\n ),\n addMember,\n addMemberStatus,\n onSelectRemovingMember,\n }}\n />\n </PopupContainer>\n </OverlayContainer>\n);\n\nconst ConfirmationDialog = ({\n message,\n onConfirm,\n confirmLabel,\n onClose,\n closeLabel,\n}) => (\n <OverlayContainer>\n <PopupContainer>\n <DeleteColumnContainer>\n <TextContainer>\n <Text>{message}</Text>\n </TextContainer>\n <FunctionButton onClick={onConfirm}>{confirmLabel}</FunctionButton>\n <CloseDeleteColumnButton onClick={onClose}>\n {closeLabel}\n </CloseDeleteColumnButton>\n </DeleteColumnContainer>\n </PopupContainer>\n </OverlayContainer>\n);\n\nconst RemoveMemberContent = () => (\n <ConfirmationDialog\n message={`Are you sure you want to remove member ${removingMember.id}?`}\n onConfirm={removeMember}\n confirmLabel=\"Remove\"\n onClose={() => setIsRemoveMemberConfirmationVisible(false)}\n closeLabel=\"Close\"\n />\n);\n\nconst DeleteBoardContent = () => (\n <ConfirmationDialog\n message=\"Are you sure you want to delete this board?\"\n onConfirm={handleDeleteBoard}\n confirmLabel=\"Delete\"\n onClose={() => setShowDeleteBoardDialog(false)}\n closeLabel=\"Close\"\n />\n);\n\nif (isRemoveMemberConfirmationVisible) {\n return <RemoveMemberContent />;\n} else if (showDeleteBoardDialog) {\n return <DeleteBoardContent />;\n} else {\n return <BoardDetailsContent />;\n}\n" }, "Calimero.Curb.Settings.MemberDetails": { "": "const componentOwnerId = props.componentOwnerId;\nconst userList = props.userList;\nconst addMember = props.addMember;\nconst channelName = props.channelName;\nconst chatUsers = props.chatUsers;\nconst setOptionsOpen = props.setOptionsOpen;\nconst optionsOpen = props.optionsOpen;\nconst promoteModerator = props.promoteModerator;\nconst removeUserFromChannel = props.removeUserFromChannel;\nconst channelOwner = props.channelOwner;\nconst getNonInvitedUsers = props.getNonInvitedUsers;\nconst selectedUser = props.selectedUser;\nconst setSelectedUser = props.setSelectedUser;\n\nconst groupMembers = userList.map((user) => user.id);\n\nconst AddMemberButton = styled.div`\n display: flex;\n column-gap: 0.5rem;\n padding-left: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n color: #fff;\n :hover {\n background-color: #5765f2;\n }\n border-radius: 4px;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n cursor: pointer;\n`;\n\nconst UserListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n :hover {\n background-color: #25252a;\n }\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n cursor: pointer;\n`;\n\nconst UserList = styled.div`\n overflow-y: scroll;\n max-height: 24rem;\n @media (max-width: 1024px) {\n max-height: 12rem;\n }\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst UserInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst Text = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n color: ${({ isSelected }) => (isSelected ? '#5765F2' : '#fff')};\n`;\n\nconst AddUserDialog = ({\n addMember,\n channelName,\n componentOwnerId,\n getNonInvitedUsers,\n nonInvitedUserList,\n}) => {\n const addUser = useCallback(\n (account) => addMember({ account, channel: channelName }),\n [addMember, channelName, channelName],\n );\n const isValidAccountName = useCallback((value) => {\n getNonInvitedUsers(value, channelName);\n const regex = /^[a-z\\d]+[-_]*[a-z\\d]+[-_]*[a-z\\d]+\\.(near|testnet)$/;\n let isValid = false;\n let error = '';\n\n if (!regex.test(value)) {\n isValid = false;\n error = '';\n } else if (groupMembers?.includes(value)) {\n isValid = false;\n error = 'User already in channel.';\n } else {\n isValid = true;\n error = '';\n }\n return { isValid, error };\n }, []);\n\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.InputPopup`}\n props={{\n componentOwnerId,\n title: `Invite user to #${channelName}`,\n placeholder: 'ex: username.near',\n buttonText: 'Invite',\n functionLoader: addUser,\n colors: {\n base: '#5765f2',\n hover: '#717cf0',\n disabled: '#3B487A',\n },\n toggle: (\n <AddMemberButton>\n <i className=\"bi bi-plus-circle-fill\" />\n Add new member\n </AddMemberButton>\n ),\n isChild: true,\n validator: isValidAccountName,\n autocomplete: true,\n nonInvitedUserList,\n }}\n />\n );\n};\n\nconst OptionsButton = ({ handleClick }) => {\n return (\n <div onClick={handleClick}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill=\"white\"\n className=\"bi bi-three-dots\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M3 9.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z\" />\n </svg>\n </div>\n );\n};\n\nconst OptionsWindow = styled.div`\n width: 252px;\n display: flex;\n flex-direction: column;\n padding-top: 4px;\n padding-bottom: 4px;\n border-radius: 0px 0px 4px 4px;\n background-color: #25252a;\n pointer-events: auto;\n`;\n\nconst Option = styled.div`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n padding-left: 1rem;\n padding-right: 1rem;\n padding-top: 4px;\n padding-bottom: 4px;\n font-weight: 400;\n line-height: 150%;\n -webkit-font-smoothing: antialiased applied;\n cursor: pointer;\n :hover {\n background-color: #2a2b37;\n }\n`;\n\nconst RoleText = styled.div`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 12px;\n font-style: normal;\n font-weight: 700;\n line-height: 100%;\n`;\n\nconst ModeratorOptions = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 12px;\n`;\n\nconst isMod = userList?.some(\n (user) => user.id === context.accountId && user.moderator === true,\n);\nconst isOwner = context.accountId === channelOwner;\n\nconst OverLay = styled.div`\n position: absolute;\n z-index: 10;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst TopOverlay = styled.div`\n position: absolute;\n z-index: 20;\n`;\n\nconst ModeratorOptionsPopup = ({ id, user }) => {\n return (\n <Popover.Root>\n <Popover.Trigger asChild>\n <OptionsButton\n handleClick={() => {\n setOptionsOpen(id);\n setSelectedUser(user);\n }}\n />\n </Popover.Trigger>\n <TopOverlay>\n <Popover.Content\n className=\"PopoverContent\"\n side=\"bottom\"\n align=\"end\"\n sticky=\"always\"\n onInteractOutside={() => setOptionsOpen(-1)}\n >\n <OptionsWindow>\n {selectedUser.id !== channelOwner && isOwner && (\n <Option\n onClick={() =>\n promoteModerator(selectedUser.id, !selectedUser.moderator)\n }\n >{`${\n selectedUser.moderator ? 'Remove moderator' : 'Make moderator'\n }`}</Option>\n )}\n <Option onClick={() => removeUserFromChannel(selectedUser.id)}>\n Remove from channel\n </Option>\n </OptionsWindow>\n </Popover.Content>\n </TopOverlay>\n </Popover.Root>\n );\n};\n\nreturn (\n <>\n <AddUserDialog {...props} />\n {optionsOpen !== -1 && <OverLay />}\n <UserList>\n {userList.length &&\n userList.map((user, id) => (\n <UserListItem key={id}>\n <UserInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n active: user.active,\n componentOwnerId,\n }}\n />\n <Text isSelected={optionsOpen === id}>{user.id}</Text>\n </UserInfo>\n <ModeratorOptions>\n {(user.moderator || channelOwner === user.id) && (\n <RoleText>{`${\n channelOwner === user.id\n ? 'Channel Owner'\n : 'Channel Moderator'\n }`}</RoleText>\n )}\n {((isMod && !user.moderator && channelOwner !== user.id) ||\n isOwner) && <ModeratorOptionsPopup id={id} user={user} />}\n </ModeratorOptions>\n </UserListItem>\n ))}\n </UserList>\n </>\n);\n" }, "Calimero.DocsChain.Sidebar.DocsSidebar": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst SideMenu = styled.div`\n background-color: #0e0e10;\n padding-top: 1rem;\n width: 317px;\n`;\n\nconst HorizontalSeparatorLine = styled.div`\n background-color: '#BF4F74';\n width: 317px;\n height: 1px;\n background-color: #282933;\n margin-top: 1rem;\n margin-bottom: 1rem;\n`;\n\nconst handleCreatePage = useCallback(\n (isPrivate) => {\n props.onOpenCreatePage(isPrivate);\n },\n [props.createPageOpen],\n);\n\nreturn (\n <SideMenu>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.Header'}\n config={redirectConfig}\n props={{\n title: 'Shared Pages',\n onChange: () => handleCreatePage(false),\n componentName: 'CreateArticle',\n }}\n />\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.AllArticlesList'}\n config={redirectConfig}\n />\n <HorizontalSeparatorLine />\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.Header'}\n config={redirectConfig}\n props={{\n title: 'Settings',\n }}\n />\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.SettingsList'}\n config={redirectConfig}\n />\n </SideMenu>\n);\n" }, "Calimero.DocsChain.CreateArticle": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst initialBody = '# Hello world';\n\nconst PageContainer = styled.div`\n width: 100%;\n height: 100vh;\n padding-left: 60px;\n padding-right: 60px;\n background-color: #0e0e10;\n color: #ffffff;\n`;\n\nconst initialCreateArticleState = {\n articleId: '',\n articleBody: initialBody,\n createPage: { open: false, private: false },\n onOpenCreatePage: (isPrivate) =>\n State.update({ openCreatePage: { open: true, private: isPrivate } }),\n};\n\nState.init(initialCreateArticleState);\n\nconst saveArticle = () => {\n if (!state.articleBody) {\n return;\n }\n const params = {\n article_id: state.articleId,\n body: state.articleBody,\n };\n Near.fakCalimeroCall(contract, 'post_article', params);\n};\n\nreturn (\n <PageContainer>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Navbar.HorizontalNavbar'}\n config={redirectConfig}\n />\n <div className=\"d-flex\">\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.DocsSidebar'}\n config={redirectConfig}\n props={{\n onOpenCreatePage: state.onOpenCreatePage,\n createPageOpen: state.createPage.open,\n }}\n />\n <div>\n <div>\n <div>\n <button\n type=\"submit\"\n className=\"btn btn-success\"\n onClick={saveArticle}\n >\n Save Article\n </button>\n </div>\n <div className=\"d-flex flex-column pt-3\">\n <label htmlFor=\"inputArticleId\">\n Input article id (case-sensitive, without spaces):\n </label>\n <label htmlFor=\"inputArticleId\" className=\"small text-danger\">\n {state.errorId}\n </label>\n <input\n className=\"form-control mt-2\"\n id=\"inputArticleId\"\n value={state.articleId}\n placeholder=\"Input article id\"\n onChange={(e) => {\n State.update({\n ...state,\n articleId: e.target.value.replace(/\\s+/g, ''),\n });\n }}\n />\n </div>\n <div className=\"d-flex flex-column pt-3\">\n <label htmlFor=\"textareaArticleBody\">\n Input article body (in markdown format):\n </label>\n <label htmlFor=\"textareaArticleBody\" className=\"small text-danger\">\n {state.errorBody}\n </label>\n <div className=\"d-flex gap-2\" style={{ minHeight: '300px' }}>\n <div className=\"w-50\">\n <Widget\n src=\"calimero.near/widget/Calimero.DocsChain.MarkdownEditorIframe\"\n config={redirectConfig}\n props={{\n initialText: initialBody,\n onChange: (articleBody) => State.update({ articleBody }),\n }}\n />\n </div>\n <div className=\"w-50\">\n <Widget\n src=\"calimero.near/widget/Calimero.DocsChain.SocialMarkdown\"\n config={redirectConfig}\n props={{ text: state.articleBody }}\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </PageContainer>\n);\n" }, "Calimero.DocsChain.AllArticlesList": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst getDateLastEdit = (timestamp) => {\n const date = new Date(Number(timestamp) / 1e6);\n const dateString = `${date.toLocaleDateString()} / ${date.toLocaleTimeString()}`;\n return dateString;\n};\n\nconst allArticles = Near.calimeroView(\n contract,\n 'get_article_ids_paged',\n {},\n).map((articleId) => {\n return {\n id: articleId,\n data: Near.calimeroView(contract, 'get_article', { article_id: articleId }),\n };\n});\nconsole.log(allArticles);\n\nreturn (\n <ol>\n {allArticles &&\n allArticles.map((article) => (\n <li key={article.id}>\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.ArticleView?articleId=${article.id}&blockHeight=${article.data.blockHeight}&lastEditor=${article.data.author}`,\n )}\n >\n {article.id}{' '}\n <small>\n (author: {article.data.author}\n {getDateLastEdit(article.data.timestamp)})\n </small>\n </a>\n </li>\n ))}\n </ol>\n);\n" }, "Calimero.Curb.ProfileIcon.UserProfileIcon": { "": "const ProfileIconContainer = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n ${({ width }) => width && `width: ${width};`}\n ${({ height }) => height && `height: ${width};`}\n`;\n\nconst ActiveStatusCricle = styled.div`\n position: absolute;\n bottom: -2px;\n right: -2px;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n ${({ active }) =>\n active ? 'background-color: #00FF66;' : 'background-color: #777583;'}\n border: 1px solid #1A1A1D;\n`;\nconst accountId = props.accountId;\nconst showStatus = props.showStatus ?? true;\nconst width = props.width ?? '24px';\nconst height = props.height ?? '24px';\nconst componentOwnerId = props.componentOwnerId;\n\nreturn (\n <ProfileIconContainer width={width} height={height}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ProfileIcon.Image`}\n props={{\n accountId,\n alt: `profile-icon-${accountId}`,\n className: 'rounded-circle',\n style: { width: width, height: height, objectFit: 'cover' },\n thumbnail: 'thumbnail',\n fallbackUrl: 'https://i.imgur.com/e8buxpa.png',\n componentOwnerId: componentOwnerId,\n }}\n />\n {showStatus && <ActiveStatusCricle active={props.active} />}\n </ProfileIconContainer>\n);\n" }, "Calimero.TaskChain.Popups.CreateTaskPopup": { "": "const {\n componentOwnerId,\n createTaskButton,\n addCreateTaskStatus,\n cleanCreateTaskStatus,\n handleColumns,\n projectId,\n contract,\n columnTitle,\n columnIndex,\n addTaskToColumn,\n} = props;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst DescriptionInput = styled.textarea`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n height: 40px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-width: 0px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n display: block;\n flex: 1;\n`;\n\nconst TitleInput = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%;\n letter-spacing: 0em;\n text-align: left;\n flex: 1;\n height: 40px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-width: 0px;\n }\n ::placeholder {\n color: #ffffff7f;\n }\n border: none;\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 30%;\n height: 40px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-width: 0px;\n }\n z-index: 10;\n border: none;\n display: flex;\n align-items: center;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst DescriptionContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n width: 100%;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n width: 100%;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n right: 15.5rem;\n top: 7rem;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst Overlay = styled.div`\n background-color: var(--blackA9);\n position: fixed;\n z-index: 1000;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);\n`;\n\nconst ModalContent = styled.div`\n position: absolute;\n width: 500px;\n padding: 30px;\n border-radius: 20px;\n color: white;\n`;\n\nconst DialogTitle = styled.div`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 15px;\n font-style: normal;\n font-weight: 700;\n line-height: 100%;\n`;\n\nconst [taskTitle, setTaskTitle] = useState('');\nconst [taskDescription, setTaskDescription] = useState('');\nconst [addTaskTitleMissing, setAddTaskTitleMissing] = useState(false);\nconst [isCreateTaskVisible, setIsCreateTaskVisible] = useState(false);\nconst [assignee, setAssignee] = useState(null);\n\nconst onChangeTaskTitle = ({ target }) => {\n setAddTaskTitleMissing(false);\n setTaskTitle(target.value);\n};\n\nconst onChangeTaskDescription = ({ target }) => {\n setTaskDescription(target.value);\n};\n\nconst handleOnClose = useCallback(() => {\n setIsCreateTaskVisible(false);\n setAddTaskTitleMissing(false);\n setAssignee(null);\n setTaskTitle('');\n setTaskDescription('');\n}, []);\n\nconst handleCreateTask = useCallback(() => {\n if (!taskTitle || taskTitle.trim() === '') {\n setAddTaskTitleMissing(true);\n return;\n }\n\n const newStatus = { title: taskTitle, status: 'Saving...' };\n try {\n addCreateTaskStatus(newStatus);\n\n const newTask = {\n project_id: projectId,\n title: taskTitle,\n description: taskDescription,\n status: columnTitle,\n assignee: assignee === 'no_assignee' ? null : assignee,\n reporter: context.accountId,\n };\n\n addTaskToColumn(columnIndex, newTask);\n\n Near.fakCalimeroCall(contract, 'create_task', {\n project_id: projectId,\n title: taskTitle,\n description: taskDescription,\n labels: [],\n assignee: assignee === 'no_assignee' ? null : assignee,\n }).then(() => {\n newStatus.status = 'Saved';\n addCreateTaskStatus(newStatus);\n setTimeout(() => cleanCreateTaskStatus(taskTitle), 3000);\n });\n } catch (e) {\n newStatus.status = 'Error saving';\n addCreateTaskStatus(newStatus);\n Storage.privateSet('tempColumns' + contract + projectId, '');\n setTimeout(() => cleanCreateTaskStatus(taskTitle), 3000);\n }\n setIsCreateTaskVisible(false);\n setTaskTitle('');\n setTaskDescription('');\n setAssignee(null);\n}, [\n taskTitle,\n taskDescription,\n setIsCreateTaskVisible,\n setTaskTitle,\n setTaskDescription,\n assignee,\n]);\n\nconst content = (\n <Overlay>\n <ModalContent>\n <PopupContainer>\n <Header>\n <DialogTitle>Create new Task</DialogTitle>\n <CloseButton onClick={handleOnClose}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <InputContainer>\n <Label>Title</Label>\n <TitleInput\n onChange={onChangeTaskTitle}\n value={taskTitle}\n placeholder=\"Set a Task Title\"\n />\n </InputContainer>\n {addTaskTitleMissing && <MissingTitle>Missing title</MissingTitle>}\n </FieldContainer>\n <FieldContainer>\n <InputContainer>\n <Label>Assignee</Label>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.AssigneeDropdown`}\n props={{\n componentOwnerId,\n assignee,\n setAssignee,\n contract,\n projectId,\n }}\n />\n </InputContainer>\n </FieldContainer>\n <FieldContainer>\n <DescriptionContainer>\n <Label>Description</Label>\n <DescriptionInput\n onChange={onChangeTaskDescription}\n value={taskDescription}\n placeholder=\"Add Description\"\n />\n </DescriptionContainer>\n </FieldContainer>\n <FunctionButton\n onClick={() =>\n handleCreateTask(\n taskTitle,\n taskDescription,\n setIsCreateTaskVisible,\n setAddTaskTitleMissing,\n setTaskTitle,\n setTaskDescription,\n )\n }\n >\n Create\n </FunctionButton>\n </PopupContainer>\n </ModalContent>\n </Overlay>\n);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.BaseModal`}\n props={{\n toggle: createTaskButton,\n content,\n open: isCreateTaskVisible,\n onOpenChange: (open) => setIsCreateTaskVisible(open),\n }}\n />\n);\n" }, "Calimero.Curb.SideSelector.DirectMessagesDropdown": { "": "const title = props.title;\nconst onToggleDMs = props.onToggleDMs;\nconst directMessagesOpen = props.directMessagesOpen;\n\nconst Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n color: #777583;\n :hover {\n color: #ffffff;\n }\n cursor: pointer;\n @media (max-width: 1024px) {\n width: 100%;\n }\n`;\nconst TextBold = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n`;\nconst IconChevronContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n font-size: 1.25rem;\n`;\n\nconst IconChevron = styled.i`\n transition: transform 5s ease;\n cursor: pointer;\n`;\n\nreturn (\n <Container onClick={() => onToggleDMs(!directMessagesOpen)}>\n <TextBold>{title}</TextBold>\n <IconChevronContainer>\n <IconChevron\n className={`${\n directMessagesOpen ? 'bi bi-chevron-down' : 'bi bi-chevron-up'\n }`}\n />\n </IconChevronContainer>\n </Container>\n);\n" }, "Calimero.Curb.EmojiSelector.EmojiSelector": { "": "const emojiObj = {\n People: [\n {\n emoji: '😀',\n title: 'Grinning Face',\n },\n {\n emoji: '😃',\n title: 'Grinning Face with Big Eyes',\n },\n {\n emoji: '😄',\n title: 'Grinning Face with Smiling Eyes',\n },\n {\n emoji: '😁',\n title: 'Beaming Face with Smiling Eyes',\n },\n {\n emoji: '😆',\n title: 'Grinning Squinting Face',\n },\n {\n emoji: '😅',\n title: 'Grinning Face with Sweat',\n },\n {\n emoji: '🤣',\n title: 'Rolling on the Floor Laughing',\n },\n {\n emoji: '😂',\n title: 'Face with Tears of Joy',\n },\n {\n emoji: '🙂',\n title: 'Slightly Smiling Face',\n },\n {\n emoji: '🙃',\n title: 'Upside-Down Face',\n },\n {\n emoji: '😉',\n title: 'Winking Face',\n },\n {\n emoji: '😊',\n title: 'Smiling Face with Smiling Eyes',\n },\n {\n emoji: '😇',\n title: 'Smiling Face with Halo',\n },\n {\n emoji: '🥰',\n title: 'Smiling Face with Hearts',\n },\n {\n emoji: '😍',\n title: 'Smiling Face with Heart-Eyes',\n },\n {\n emoji: '🤩',\n title: 'Star-Struck',\n },\n {\n emoji: '😘',\n title: 'Face Blowing a Kiss',\n },\n {\n emoji: '😗',\n title: 'Kissing Face',\n },\n {\n emoji: '☺️',\n title: 'Smiling Face',\n },\n {\n emoji: '😚',\n title: 'Kissing Face with Closed Eyes',\n },\n {\n emoji: '😙',\n title: 'Kissing Face with Smiling Eyes',\n },\n {\n emoji: '🥲',\n title: 'Smiling Face with Tear',\n },\n {\n emoji: '😋',\n title: 'Face Savoring Food',\n },\n {\n emoji: '😛',\n title: 'Face with Tongue',\n },\n {\n emoji: '😜',\n title: 'Winking Face with Tongue',\n },\n {\n emoji: '🤪',\n title: 'Zany Face',\n },\n {\n emoji: '😝',\n title: 'Squinting Face with Tongue',\n },\n {\n emoji: '🤑',\n title: 'Money-Mouth Face',\n },\n {\n emoji: '🤗',\n title: 'Smiling Face with Open Hands',\n },\n {\n emoji: '🤭',\n title: 'Face with Hand Over Mouth',\n },\n {\n emoji: '🤫',\n title: 'Shushing Face',\n },\n {\n emoji: '🤔',\n title: 'Thinking Face',\n },\n {\n emoji: '🤐',\n title: 'Zipper-Mouth Face',\n },\n {\n emoji: '🤨',\n title: 'Face with Raised Eyebrow',\n },\n {\n emoji: '😐',\n title: 'Neutral Face',\n },\n {\n emoji: '😑',\n title: 'Expressionless Face',\n },\n {\n emoji: '😶',\n title: 'Face Without Mouth',\n },\n {\n emoji: '😶‍🌫️',\n title: 'Face in Clouds',\n },\n {\n emoji: '😏',\n title: 'Smirking Face',\n },\n {\n emoji: '😒',\n title: 'Unamused Face',\n },\n {\n emoji: '🙄',\n title: 'Face with Rolling Eyes',\n },\n {\n emoji: '😬',\n title: 'Grimacing Face',\n },\n {\n emoji: '😮‍💨',\n title: 'Face Exhaling',\n },\n {\n emoji: '🤥',\n title: 'Lying Face',\n },\n {\n emoji: '😌',\n title: 'Relieved Face',\n },\n {\n emoji: '😔',\n title: 'Pensive Face',\n },\n {\n emoji: '😪',\n title: 'Sleepy Face',\n },\n {\n emoji: '🤤',\n title: 'Drooling Face',\n },\n {\n emoji: '😴',\n title: 'Sleeping Face',\n },\n {\n emoji: '😷',\n title: 'Face with Medical Mask',\n },\n {\n emoji: '🤒',\n title: 'Face with Thermometer',\n },\n {\n emoji: '🤕',\n title: 'Face with Head-Bandage',\n },\n {\n emoji: '🤢',\n title: 'Nauseated Face',\n },\n {\n emoji: '🤮',\n title: 'Face Vomiting',\n },\n {\n emoji: '🤧',\n title: 'Sneezing Face',\n },\n {\n emoji: '🥵',\n title: 'Hot Face',\n },\n {\n emoji: '🥶',\n title: 'Cold Face',\n },\n {\n emoji: '🥴',\n title: 'Woozy Face',\n },\n {\n emoji: '😵',\n title: 'Face with Crossed-Out Eyes',\n },\n {\n emoji: '😵‍💫',\n title: 'Face with Spiral Eyes',\n },\n {\n emoji: '🤯',\n title: 'Exploding Head',\n },\n {\n emoji: '🤠',\n title: 'Cowboy Hat Face',\n },\n {\n emoji: '🥳',\n title: 'Partying Face',\n },\n {\n emoji: '🥸',\n title: 'Disguised Face',\n },\n {\n emoji: '😎',\n title: 'Smiling Face with Sunglasses',\n },\n {\n emoji: '🤓',\n title: 'Nerd Face',\n },\n {\n emoji: '🧐',\n title: 'Face with Monocle',\n },\n {\n emoji: '😕',\n title: 'Confused Face',\n },\n {\n emoji: '😟',\n title: 'Worried Face',\n },\n {\n emoji: '🙁',\n title: 'Slightly Frowning Face',\n },\n {\n emoji: '☹️',\n title: 'Frowning Face',\n },\n {\n emoji: '😮',\n title: 'Face with Open Mouth',\n },\n {\n emoji: '😯',\n title: 'Hushed Face',\n },\n {\n emoji: '😲',\n title: 'Astonished Face',\n },\n {\n emoji: '😳',\n title: 'Flushed Face',\n },\n {\n emoji: '🥺',\n title: 'Pleading Face',\n },\n {\n emoji: '😦',\n title: 'Frowning Face with Open Mouth',\n },\n {\n emoji: '😧',\n title: 'Anguished Face',\n },\n {\n emoji: '😨',\n title: 'Fearful Face',\n },\n {\n emoji: '😰',\n title: 'Anxious Face with Sweat',\n },\n {\n emoji: '😥',\n title: 'Sad but Relieved Face',\n },\n {\n emoji: '😢',\n title: 'Crying Face',\n },\n {\n emoji: '😭',\n title: 'Loudly Crying Face',\n },\n {\n emoji: '😱',\n title: 'Face Screaming in Fear',\n },\n {\n emoji: '😖',\n title: 'Confounded Face',\n },\n {\n emoji: '😣',\n title: 'Persevering Face',\n },\n {\n emoji: '😞',\n title: 'Disappointed Face',\n },\n {\n emoji: '😓',\n title: 'Downcast Face with Sweat',\n },\n {\n emoji: '😩',\n title: 'Weary Face',\n },\n {\n emoji: '😫',\n title: 'Tired Face',\n },\n {\n emoji: '🥱',\n title: 'Yawning Face',\n },\n {\n emoji: '😤',\n title: 'Face with Steam From Nose',\n },\n {\n emoji: '😡',\n title: 'Enraged Face',\n },\n {\n emoji: '😠',\n title: 'Angry Face',\n },\n {\n emoji: '🤬',\n title: 'Face with Symbols on Mouth',\n },\n {\n emoji: '😈',\n title: 'Smiling Face with Horns',\n },\n {\n emoji: '👿',\n title: 'Angry Face with Horns',\n },\n {\n emoji: '💀',\n title: 'Skull',\n },\n {\n emoji: '☠️',\n title: 'Skull and Crossbones',\n },\n {\n emoji: '💩',\n title: 'Pile of Poo',\n },\n {\n emoji: '🤡',\n title: 'Clown Face',\n },\n {\n emoji: '👹',\n title: 'Ogre',\n },\n {\n emoji: '👺',\n title: 'Goblin',\n },\n {\n emoji: '👻',\n title: 'Ghost',\n },\n {\n emoji: '👽',\n title: 'Alien',\n },\n {\n emoji: '👾',\n title: 'Alien Monster',\n },\n {\n emoji: '🤖',\n title: 'Robot',\n },\n {\n emoji: '😺',\n title: 'Grinning Cat',\n },\n {\n emoji: '😸',\n title: 'Grinning Cat with Smiling Eyes',\n },\n {\n emoji: '😹',\n title: 'Cat with Tears of Joy',\n },\n {\n emoji: '😻',\n title: 'Smiling Cat with Heart-Eyes',\n },\n {\n emoji: '😼',\n title: 'Cat with Wry Smile',\n },\n {\n emoji: '😽',\n title: 'Kissing Cat',\n },\n {\n emoji: '🙀',\n title: 'Weary Cat',\n },\n {\n emoji: '😿',\n title: 'Crying Cat',\n },\n {\n emoji: '😾',\n title: 'Pouting Cat',\n },\n {\n emoji: '💋',\n title: 'Kiss Mark',\n },\n {\n emoji: '👋',\n title: 'Waving Hand',\n },\n {\n emoji: '🤚',\n title: 'Raised Back of Hand',\n },\n {\n emoji: '🖐️',\n title: 'Hand with Fingers Splayed',\n },\n {\n emoji: '✋',\n title: 'Raised Hand',\n },\n {\n emoji: '🖖',\n title: 'Vulcan Salute',\n },\n {\n emoji: '👌',\n title: 'OK Hand',\n },\n {\n emoji: '🤌',\n title: 'Pinched Fingers',\n },\n {\n emoji: '🤏',\n title: 'Pinching Hand',\n },\n {\n emoji: '✌️',\n title: 'Victory Hand',\n },\n {\n emoji: '🤞',\n title: 'Crossed Fingers',\n },\n {\n emoji: '🤟',\n title: 'Love-You Gesture',\n },\n {\n emoji: '🤘',\n title: 'Sign of the Horns',\n },\n {\n emoji: '🤙',\n title: 'Call Me Hand',\n },\n {\n emoji: '👈',\n title: 'Backhand Index Pointing Left',\n },\n {\n emoji: '👉',\n title: 'Backhand Index Pointing Right',\n },\n {\n emoji: '👆',\n title: 'Backhand Index Pointing Up',\n },\n {\n emoji: '🖕',\n title: 'Middle Finger',\n },\n {\n emoji: '👇',\n title: 'Backhand Index Pointing Down',\n },\n {\n emoji: '☝️',\n title: 'Index Pointing Up',\n },\n {\n emoji: '👍',\n title: 'Thumbs Up',\n },\n {\n emoji: '👎',\n title: 'Thumbs Down',\n },\n {\n emoji: '✊',\n title: 'Raised Fist',\n },\n {\n emoji: '👊',\n title: 'Oncoming Fist',\n },\n {\n emoji: '🤛',\n title: 'Left-Facing Fist',\n },\n {\n emoji: '🤜',\n title: 'Right-Facing Fist',\n },\n {\n emoji: '👏',\n title: 'Clapping Hands',\n },\n {\n emoji: '🙌',\n title: 'Raising Hands',\n },\n {\n emoji: '👐',\n title: 'Open Hands',\n },\n {\n emoji: '🤲',\n title: 'Palms Up Together',\n },\n {\n emoji: '🤝',\n title: 'Handshake',\n },\n {\n emoji: '🙏',\n title: 'Folded Hands',\n },\n {\n emoji: '✍️',\n title: 'Writing Hand',\n },\n {\n emoji: '💅',\n title: 'Nail Polish',\n },\n {\n emoji: '🤳',\n title: 'Selfie',\n },\n {\n emoji: '💪',\n title: 'Flexed Biceps',\n },\n {\n emoji: '🦾',\n title: 'Mechanical Arm',\n },\n {\n emoji: '🦿',\n title: 'Mechanical Leg',\n },\n {\n emoji: '🦵',\n title: 'Leg',\n },\n {\n emoji: '🦶',\n title: 'Foot',\n },\n {\n emoji: '👂',\n title: 'Ear',\n },\n {\n emoji: '🦻',\n title: 'Ear with Hearing Aid',\n },\n {\n emoji: '👃',\n title: 'Nose',\n },\n {\n emoji: '🧠',\n title: 'Brain',\n },\n {\n emoji: '🫀',\n title: 'Anatomical Heart',\n },\n {\n emoji: '🫁',\n title: 'Lungs',\n },\n {\n emoji: '🦷',\n title: 'Tooth',\n },\n {\n emoji: '🦴',\n title: 'Bone',\n },\n {\n emoji: '👀',\n title: 'Eyes',\n },\n {\n emoji: '👁️',\n title: 'Eye',\n },\n {\n emoji: '👅',\n title: 'Tongue',\n },\n {\n emoji: '👄',\n title: 'Mouth',\n },\n {\n emoji: '👶',\n title: 'Baby',\n },\n {\n emoji: '🧒',\n title: 'Child',\n },\n {\n emoji: '👦',\n title: 'Boy',\n },\n {\n emoji: '👧',\n title: 'Girl',\n },\n {\n emoji: '🧑',\n title: 'Person',\n },\n {\n emoji: '👱',\n title: 'Person: Blond Hair',\n },\n {\n emoji: '👨',\n title: 'Man',\n },\n {\n emoji: '🧔',\n title: 'Person: Beard',\n },\n {\n emoji: '👨‍🦰',\n title: 'Man: Red Hair',\n },\n {\n emoji: '👨‍🦱',\n title: 'Man: Curly Hair',\n },\n {\n emoji: '👨‍🦳',\n title: 'Man: White Hair',\n },\n {\n emoji: '👨‍🦲',\n title: 'Man: Bald',\n },\n {\n emoji: '👩',\n title: 'Woman',\n },\n {\n emoji: '👩‍🦰',\n title: 'Woman: Red Hair',\n },\n {\n emoji: '🧑‍🦰',\n title: 'Person: Red Hair',\n },\n {\n emoji: '👩‍🦱',\n title: 'Woman: Curly Hair',\n },\n {\n emoji: '🧑‍🦱',\n title: 'Person: Curly Hair',\n },\n {\n emoji: '👩‍🦳',\n title: 'Woman: White Hair',\n },\n {\n emoji: '🧑‍🦳',\n title: 'Person: White Hair',\n },\n {\n emoji: '👩‍🦲',\n title: 'Woman: Bald',\n },\n {\n emoji: '🧑‍🦲',\n title: 'Person: Bald',\n },\n {\n emoji: '👱‍♀️',\n title: 'Woman: Blond Hair',\n },\n {\n emoji: '👱‍♂️',\n title: 'Man: Blond Hair',\n },\n {\n emoji: '🧓',\n title: 'Older Person',\n },\n {\n emoji: '👴',\n title: 'Old Man',\n },\n {\n emoji: '👵',\n title: 'Old Woman',\n },\n {\n emoji: '🙍',\n title: 'Person Frowning',\n },\n {\n emoji: '🙍‍♂️',\n title: 'Man Frowning',\n },\n {\n emoji: '🙍‍♀️',\n title: 'Woman Frowning',\n },\n {\n emoji: '🙎',\n title: 'Person Pouting',\n },\n {\n emoji: '🙎‍♂️',\n title: 'Man Pouting',\n },\n {\n emoji: '🙎‍♀️',\n title: 'Woman Pouting',\n },\n {\n emoji: '🙅',\n title: 'Person Gesturing No',\n },\n {\n emoji: '🙅‍♂️',\n title: 'Man Gesturing No',\n },\n {\n emoji: '🙅‍♀️',\n title: 'Woman Gesturing No',\n },\n {\n emoji: '🙆',\n title: 'Person Gesturing OK',\n },\n {\n emoji: '🙆‍♂️',\n title: 'Man Gesturing OK',\n },\n {\n emoji: '🙆‍♀️',\n title: 'Woman Gesturing OK',\n },\n {\n emoji: '💁',\n title: 'Person Tipping Hand',\n },\n {\n emoji: '💁‍♂️',\n title: 'Man Tipping Hand',\n },\n {\n emoji: '💁‍♀️',\n title: 'Woman Tipping Hand',\n },\n {\n emoji: '🙋',\n title: 'Person Raising Hand',\n },\n {\n emoji: '🙋‍♂️',\n title: 'Man Raising Hand',\n },\n {\n emoji: '🙋‍♀️',\n title: 'Woman Raising Hand',\n },\n {\n emoji: '🧏',\n title: 'Deaf Person',\n },\n {\n emoji: '🧏‍♂️',\n title: 'Deaf Man',\n },\n {\n emoji: '🧏‍♀️',\n title: 'Deaf Woman',\n },\n {\n emoji: '🙇',\n title: 'Person Bowing',\n },\n {\n emoji: '🙇‍♂️',\n title: 'Man Bowing',\n },\n {\n emoji: '🙇‍♀️',\n title: 'Woman Bowing',\n },\n {\n emoji: '🤦',\n title: 'Person Facepalming',\n },\n {\n emoji: '🤦‍♂️',\n title: 'Man Facepalming',\n },\n {\n emoji: '🤦‍♀️',\n title: 'Woman Facepalming',\n },\n {\n emoji: '🤷',\n title: 'Person Shrugging',\n },\n {\n emoji: '🤷‍♂️',\n title: 'Man Shrugging',\n },\n {\n emoji: '🤷‍♀️',\n title: 'Woman Shrugging',\n },\n {\n emoji: '🧑‍⚕️',\n title: 'Health Worker',\n },\n {\n emoji: '👨‍⚕️',\n title: 'Man Health Worker',\n },\n {\n emoji: '👩‍⚕️',\n title: 'Woman Health Worker',\n },\n {\n emoji: '🧑‍🎓',\n title: 'Student',\n },\n {\n emoji: '👨‍🎓',\n title: 'Man Student',\n },\n {\n emoji: '👩‍🎓',\n title: 'Woman Student',\n },\n {\n emoji: '🧑‍🏫',\n title: 'Teacher',\n },\n {\n emoji: '👨‍🏫',\n title: 'Man Teacher',\n },\n {\n emoji: '👩‍🏫',\n title: 'Woman Teacher',\n },\n {\n emoji: '🧑‍⚖️',\n title: 'Judge',\n },\n {\n emoji: '👨‍⚖️',\n title: 'Man Judge',\n },\n {\n emoji: '👩‍⚖️',\n title: 'Woman Judge',\n },\n {\n emoji: '🧑‍🌾',\n title: 'Farmer',\n },\n {\n emoji: '👨‍🌾',\n title: 'Man Farmer',\n },\n {\n emoji: '👩‍🌾',\n title: 'Woman Farmer',\n },\n {\n emoji: '🧑‍🍳',\n title: 'Cook',\n },\n {\n emoji: '👨‍🍳',\n title: 'Man Cook',\n },\n {\n emoji: '👩‍🍳',\n title: 'Woman Cook',\n },\n {\n emoji: '🧑‍🔧',\n title: 'Mechanic',\n },\n {\n emoji: '👨‍🔧',\n title: 'Man Mechanic',\n },\n {\n emoji: '👩‍🔧',\n title: 'Woman Mechanic',\n },\n {\n emoji: '🧑‍🏭',\n title: 'Factory Worker',\n },\n {\n emoji: '👨‍🏭',\n title: 'Man Factory Worker',\n },\n {\n emoji: '👩‍🏭',\n title: 'Woman Factory Worker',\n },\n {\n emoji: '🧑‍💼',\n title: 'Office Worker',\n },\n {\n emoji: '👨‍💼',\n title: 'Man Office Worker',\n },\n {\n emoji: '👩‍💼',\n title: 'Woman Office Worker',\n },\n {\n emoji: '🧑‍🔬',\n title: 'Scientist',\n },\n {\n emoji: '👨‍🔬',\n title: 'Man Scientist',\n },\n {\n emoji: '👩‍🔬',\n title: 'Woman Scientist',\n },\n {\n emoji: '🧑‍💻',\n title: 'Technologist',\n },\n {\n emoji: '👨‍💻',\n title: 'Man Technologist',\n },\n {\n emoji: '👩‍💻',\n title: 'Woman Technologist',\n },\n {\n emoji: '🧑‍🎤',\n title: 'Singer',\n },\n {\n emoji: '👨‍🎤',\n title: 'Man Singer',\n },\n {\n emoji: '👩‍🎤',\n title: 'Woman Singer',\n },\n {\n emoji: '🧑‍🎨',\n title: 'Artist',\n },\n {\n emoji: '👨‍🎨',\n title: 'Man Artist',\n },\n {\n emoji: '👩‍🎨',\n title: 'Woman Artist',\n },\n {\n emoji: '🧑‍✈️',\n title: 'Pilot',\n },\n {\n emoji: '👨‍✈️',\n title: 'Man Pilot',\n },\n {\n emoji: '👩‍✈️',\n title: 'Woman Pilot',\n },\n {\n emoji: '🧑‍🚀',\n title: 'Astronaut',\n },\n {\n emoji: '👨‍🚀',\n title: 'Man Astronaut',\n },\n {\n emoji: '👩‍🚀',\n title: 'Woman Astronaut',\n },\n {\n emoji: '🧑‍🚒',\n title: 'Firefighter',\n },\n {\n emoji: '👨‍🚒',\n title: 'Man Firefighter',\n },\n {\n emoji: '👩‍🚒',\n title: 'Woman Firefighter',\n },\n {\n emoji: '👮',\n title: 'Police Officer',\n },\n {\n emoji: '👮‍♂️',\n title: 'Man Police Officer',\n },\n {\n emoji: '👮‍♀️',\n title: 'Woman Police Officer',\n },\n {\n emoji: '🕵️',\n title: 'Detective',\n },\n {\n emoji: '🕵️‍♂️',\n title: 'Man Detective',\n },\n {\n emoji: '🕵️‍♀️',\n title: 'Woman Detective',\n },\n {\n emoji: '💂',\n title: 'Guard',\n },\n {\n emoji: '💂‍♂️',\n title: 'Man Guard',\n },\n {\n emoji: '💂‍♀️',\n title: 'Woman Guard',\n },\n {\n emoji: '🥷',\n title: 'Ninja',\n },\n {\n emoji: '👷',\n title: 'Construction Worker',\n },\n {\n emoji: '👷‍♂️',\n title: 'Man Construction Worker',\n },\n {\n emoji: '👷‍♀️',\n title: 'Woman Construction Worker',\n },\n {\n emoji: '🤴',\n title: 'Prince',\n },\n {\n emoji: '👸',\n title: 'Princess',\n },\n {\n emoji: '👳',\n title: 'Person Wearing Turban',\n },\n {\n emoji: '👳‍♂️',\n title: 'Man Wearing Turban',\n },\n {\n emoji: '👳‍♀️',\n title: 'Woman Wearing Turban',\n },\n {\n emoji: '👲',\n title: 'Person with Skullcap',\n },\n {\n emoji: '🧕',\n title: 'Woman with Headscarf',\n },\n {\n emoji: '🤵',\n title: 'Person in Tuxedo',\n },\n {\n emoji: '🤵‍♂️',\n title: 'Man in Tuxedo',\n },\n {\n emoji: '🤵‍♀️',\n title: 'Woman in Tuxedo',\n },\n {\n emoji: '👰',\n title: 'Person with Veil',\n },\n {\n emoji: '👰‍♂️',\n title: 'Man with Veil',\n },\n {\n emoji: '👰‍♀️',\n title: 'Woman with Veil',\n },\n {\n emoji: '🤰',\n title: 'Pregnant Woman',\n },\n {\n emoji: '🤱',\n title: 'Breast-Feeding',\n },\n {\n emoji: '👩‍🍼',\n title: 'Woman Feeding Baby',\n },\n {\n emoji: '👨‍🍼',\n title: 'Man Feeding Baby',\n },\n {\n emoji: '🧑‍🍼',\n title: 'Person Feeding Baby',\n },\n {\n emoji: '👼',\n title: 'Baby Angel',\n },\n {\n emoji: '🎅',\n title: 'Santa Claus',\n },\n {\n emoji: '🤶',\n title: 'Mrs. Claus',\n },\n {\n emoji: '🧑‍🎄',\n title: 'Mx Claus',\n },\n {\n emoji: '🦸',\n title: 'Superhero',\n },\n {\n emoji: '🦸‍♂️',\n title: 'Man Superhero',\n },\n {\n emoji: '🦸‍♀️',\n title: 'Woman Superhero',\n },\n {\n emoji: '🦹',\n title: 'Supervillain',\n },\n {\n emoji: '🦹‍♂️',\n title: 'Man Supervillain',\n },\n {\n emoji: '🦹‍♀️',\n title: 'Woman Supervillain',\n },\n {\n emoji: '🧙',\n title: 'Mage',\n },\n {\n emoji: '🧙‍♂️',\n title: 'Man Mage',\n },\n {\n emoji: '🧙‍♀️',\n title: 'Woman Mage',\n },\n {\n emoji: '🧚',\n title: 'Fairy',\n },\n {\n emoji: '🧚‍♂️',\n title: 'Man Fairy',\n },\n {\n emoji: '🧚‍♀️',\n title: 'Woman Fairy',\n },\n {\n emoji: '🧛',\n title: 'Vampire',\n },\n {\n emoji: '🧛‍♂️',\n title: 'Man Vampire',\n },\n {\n emoji: '🧛‍♀️',\n title: 'Woman Vampire',\n },\n {\n emoji: '🧜',\n title: 'Merperson',\n },\n {\n emoji: '🧜‍♂️',\n title: 'Merman',\n },\n {\n emoji: '🧜‍♀️',\n title: 'Mermaid',\n },\n {\n emoji: '🧝',\n title: 'Elf',\n },\n {\n emoji: '🧝‍♂️',\n title: 'Man Elf',\n },\n {\n emoji: '🧝‍♀️',\n title: 'Woman Elf',\n },\n {\n emoji: '🧞',\n title: 'Genie',\n },\n {\n emoji: '🧞‍♂️',\n title: 'Man Genie',\n },\n {\n emoji: '🧞‍♀️',\n title: 'Woman Genie',\n },\n {\n emoji: '🧟',\n title: 'Zombie',\n },\n {\n emoji: '🧟‍♂️',\n title: 'Man Zombie',\n },\n {\n emoji: '🧟‍♀️',\n title: 'Woman Zombie',\n },\n {\n emoji: '💆',\n title: 'Person Getting Massage',\n },\n {\n emoji: '💆‍♂️',\n title: 'Man Getting Massage',\n },\n {\n emoji: '💆‍♀️',\n title: 'Woman Getting Massage',\n },\n {\n emoji: '💇',\n title: 'Person Getting Haircut',\n },\n {\n emoji: '💇‍♂️',\n title: 'Man Getting Haircut',\n },\n {\n emoji: '💇‍♀️',\n title: 'Woman Getting Haircut',\n },\n {\n emoji: '🚶',\n title: 'Person Walking',\n },\n {\n emoji: '🚶‍♂️',\n title: 'Man Walking',\n },\n {\n emoji: '🚶‍♀️',\n title: 'Woman Walking',\n },\n {\n emoji: '🧍',\n title: 'Person Standing',\n },\n {\n emoji: '🧍‍♂️',\n title: 'Man Standing',\n },\n {\n emoji: '🧍‍♀️',\n title: 'Woman Standing',\n },\n {\n emoji: '🧎',\n title: 'Person Kneeling',\n },\n {\n emoji: '🧎‍♂️',\n title: 'Man Kneeling',\n },\n {\n emoji: '🧎‍♀️',\n title: 'Woman Kneeling',\n },\n {\n emoji: '🧑‍🦯',\n title: 'Person with White Cane',\n },\n {\n emoji: '👨‍🦯',\n title: 'Man with White Cane',\n },\n {\n emoji: '👩‍🦯',\n title: 'Woman with White Cane',\n },\n {\n emoji: '🧑‍🦼',\n title: 'Person in Motorized Wheelchair',\n },\n {\n emoji: '👨‍🦼',\n title: 'Man in Motorized Wheelchair',\n },\n {\n emoji: '👩‍🦼',\n title: 'Woman in Motorized Wheelchair',\n },\n {\n emoji: '🧑‍🦽',\n title: 'Person in Manual Wheelchair',\n },\n {\n emoji: '👨‍🦽',\n title: 'Man in Manual Wheelchair',\n },\n {\n emoji: '👩‍🦽',\n title: 'Woman in Manual Wheelchair',\n },\n {\n emoji: '🏃',\n title: 'Person Running',\n },\n {\n emoji: '🏃‍♂️',\n title: 'Man Running',\n },\n {\n emoji: '🏃‍♀️',\n title: 'Woman Running',\n },\n {\n emoji: '💃',\n title: 'Woman Dancing',\n },\n {\n emoji: '🕺',\n title: 'Man Dancing',\n },\n {\n emoji: '🕴️',\n title: 'Person in Suit Levitating',\n },\n {\n emoji: '👯',\n title: 'People with Bunny Ears',\n },\n {\n emoji: '👯‍♂️',\n title: 'Men with Bunny Ears',\n },\n {\n emoji: '👯‍♀️',\n title: 'Women with Bunny Ears',\n },\n {\n emoji: '🧖',\n title: 'Person in Steamy Room',\n },\n {\n emoji: '🧖‍♂️',\n title: 'Man in Steamy Room',\n },\n {\n emoji: '🧖‍♀️',\n title: 'Woman in Steamy Room',\n },\n {\n emoji: '🧘',\n title: 'Person in Lotus Position',\n },\n {\n emoji: '🧑‍🤝‍🧑',\n title: 'People Holding Hands',\n },\n {\n emoji: '👭',\n title: 'Women Holding Hands',\n },\n {\n emoji: '👫',\n title: 'Woman and Man Holding Hands',\n },\n {\n emoji: '👬',\n title: 'Men Holding Hands',\n },\n {\n emoji: '💏',\n title: 'Kiss',\n },\n {\n emoji: '👩‍❤️‍💋‍👨',\n title: 'Kiss: Woman, Man',\n },\n {\n emoji: '👨‍❤️‍💋‍👨',\n title: 'Kiss: Man, Man',\n },\n {\n emoji: '👩‍❤️‍💋‍👩',\n title: 'Kiss: Woman, Woman',\n },\n {\n emoji: '💑',\n title: 'Couple with Heart',\n },\n {\n emoji: '👩‍❤️‍👨',\n title: 'Couple with Heart: Woman, Man',\n },\n {\n emoji: '👨‍❤️‍👨',\n title: 'Couple with Heart: Man, Man',\n },\n {\n emoji: '👩‍❤️‍👩',\n title: 'Couple with Heart: Woman, Woman',\n },\n {\n emoji: '👪',\n title: 'Family',\n },\n {\n emoji: '👨‍👩‍👦',\n title: 'Family: Man, Woman, Boy',\n },\n {\n emoji: '👨‍👩‍👧',\n title: 'Family: Man, Woman, Girl',\n },\n {\n emoji: '👨‍👩‍👧‍👦',\n title: 'Family: Man, Woman, Girl, Boy',\n },\n {\n emoji: '👨‍👩‍👦‍👦',\n title: 'Family: Man, Woman, Boy, Boy',\n },\n {\n emoji: '👨‍👩‍👧‍👧',\n title: 'Family: Man, Woman, Girl, Girl',\n },\n {\n emoji: '👨‍👨‍👦',\n title: 'Family: Man, Man, Boy',\n },\n {\n emoji: '👨‍👨‍👧',\n title: 'Family: Man, Man, Girl',\n },\n {\n emoji: '👨‍👨‍👧‍👦',\n title: 'Family: Man, Man, Girl, Boy',\n },\n {\n emoji: '👨‍👨‍👦‍👦',\n title: 'Family: Man, Man, Boy, Boy',\n },\n {\n emoji: '👨‍👨‍👧‍👧',\n title: 'Family: Man, Man, Girl, Girl',\n },\n {\n emoji: '👩‍👩‍👦',\n title: 'Family: Woman, Woman, Boy',\n },\n {\n emoji: '👩‍👩‍👧',\n title: 'Family: Woman, Woman, Girl',\n },\n {\n emoji: '👩‍👩‍👧‍👦',\n title: 'Family: Woman, Woman, Girl, Boy',\n },\n {\n emoji: '👩‍👩‍👦‍👦',\n title: 'Family: Woman, Woman, Boy, Boy',\n },\n {\n emoji: '👩‍👩‍👧‍👧',\n title: 'Family: Woman, Woman, Girl, Girl',\n },\n {\n emoji: '👨‍👦',\n title: 'Family: Man, Boy',\n },\n {\n emoji: '👨‍👦‍👦',\n title: 'Family: Man, Boy, Boy',\n },\n {\n emoji: '👨‍👧',\n title: 'Family: Man, Girl',\n },\n {\n emoji: '👨‍👧‍👦',\n title: 'Family: Man, Girl, Boy',\n },\n {\n emoji: '👨‍👧‍👧',\n title: 'Family: Man, Girl, Girl',\n },\n {\n emoji: '👩‍👦',\n title: 'Family: Woman, Boy',\n },\n {\n emoji: '👩‍👦‍👦',\n title: 'Family: Woman, Boy, Boy',\n },\n {\n emoji: '👩‍👧',\n title: 'Family: Woman, Girl',\n },\n {\n emoji: '👩‍👧‍👦',\n title: 'Family: Woman, Girl, Boy',\n },\n {\n emoji: '👩‍👧‍👧',\n title: 'Family: Woman, Girl, Girl',\n },\n {\n emoji: '🗣️',\n title: 'Speaking Head',\n },\n {\n emoji: '👤',\n title: 'Bust in Silhouette',\n },\n {\n emoji: '👥',\n title: 'Busts in Silhouette',\n },\n {\n emoji: '🫂',\n title: 'People Hugging',\n },\n {\n emoji: '👣',\n title: 'Footprints',\n },\n {\n emoji: '🧳',\n title: 'Luggage',\n },\n {\n emoji: '🌂',\n title: 'Closed Umbrella',\n },\n {\n emoji: '☂️',\n title: 'Umbrella',\n },\n {\n emoji: '🎃',\n title: 'Jack-O-Lantern',\n },\n {\n emoji: '🧵',\n title: 'Thread',\n },\n {\n emoji: '🧶',\n title: 'Yarn',\n },\n {\n emoji: '👓',\n title: 'Glasses',\n },\n {\n emoji: '🕶️',\n title: 'Sunglasses',\n },\n {\n emoji: '🥽',\n title: 'Goggles',\n },\n {\n emoji: '🥼',\n title: 'Lab Coat',\n },\n {\n emoji: '🦺',\n title: 'Safety Vest',\n },\n {\n emoji: '👔',\n title: 'Necktie',\n },\n {\n emoji: '👕',\n title: 'T-Shirt',\n },\n {\n emoji: '👖',\n title: 'Jeans',\n },\n {\n emoji: '🧣',\n title: 'Scarf',\n },\n {\n emoji: '🧤',\n title: 'Gloves',\n },\n {\n emoji: '🧥',\n title: 'Coat',\n },\n {\n emoji: '🧦',\n title: 'Socks',\n },\n {\n emoji: '👗',\n title: 'Dress',\n },\n {\n emoji: '👘',\n title: 'Kimono',\n },\n {\n emoji: '🥻',\n title: 'Sari',\n },\n {\n emoji: '🩱',\n title: 'One-Piece Swimsuit',\n },\n {\n emoji: '🩲',\n title: 'Briefs',\n },\n {\n emoji: '🩳',\n title: 'Shorts',\n },\n {\n emoji: '👙',\n title: 'Bikini',\n },\n {\n emoji: '👚',\n title: 'Woman’s Clothes',\n },\n {\n emoji: '👛',\n title: 'Purse',\n },\n {\n emoji: '👜',\n title: 'Handbag',\n },\n {\n emoji: '👝',\n title: 'Clutch Bag',\n },\n {\n emoji: '🎒',\n title: 'Backpack',\n },\n {\n emoji: '🩴',\n title: 'Thong Sandal',\n },\n {\n emoji: '👞',\n title: 'Man’s Shoe',\n },\n {\n emoji: '👟',\n title: 'Running Shoe',\n },\n {\n emoji: '🥾',\n title: 'Hiking Boot',\n },\n {\n emoji: '🥿',\n title: 'Flat Shoe',\n },\n {\n emoji: '👠',\n title: 'High-Heeled Shoe',\n },\n {\n emoji: '👡',\n title: 'Woman’s Sandal',\n },\n {\n emoji: '🩰',\n title: 'Ballet Shoes',\n },\n {\n emoji: '👢',\n title: 'Woman’s Boot',\n },\n {\n emoji: '👑',\n title: 'Crown',\n },\n {\n emoji: '👒',\n title: 'Woman’s Hat',\n },\n {\n emoji: '🎩',\n title: 'Top Hat',\n },\n {\n emoji: '🎓',\n title: 'Graduation Cap',\n },\n {\n emoji: '🧢',\n title: 'Billed Cap',\n },\n {\n emoji: '🪖',\n title: 'Military Helmet',\n },\n {\n emoji: '⛑️',\n title: 'Rescue Worker’s Helmet',\n },\n {\n emoji: '💄',\n title: 'Lipstick',\n },\n {\n emoji: '💍',\n title: 'Ring',\n },\n {\n emoji: '💼',\n title: 'Briefcase',\n },\n {\n emoji: '🩸',\n title: 'Drop of Blood',\n },\n ],\n Nature: [\n {\n emoji: '🙈',\n title: 'See-No-Evil Monkey',\n },\n {\n emoji: '🙉',\n title: 'Hear-No-Evil Monkey',\n },\n {\n emoji: '🙊',\n title: 'Speak-No-Evil Monkey',\n },\n {\n emoji: '💥',\n title: 'Collision',\n },\n {\n emoji: '💫',\n title: 'Dizzy',\n },\n {\n emoji: '💦',\n title: 'Sweat Droplets',\n },\n {\n emoji: '💨',\n title: 'Dashing Away',\n },\n {\n emoji: '🐵',\n title: 'Monkey Face',\n },\n {\n emoji: '🐒',\n title: 'Monkey',\n },\n {\n emoji: '🦍',\n title: 'Gorilla',\n },\n {\n emoji: '🦧',\n title: 'Orangutan',\n },\n {\n emoji: '🐶',\n title: 'Dog Face',\n },\n {\n emoji: '🐕',\n title: 'Dog',\n },\n {\n emoji: '🦮',\n title: 'Guide Dog',\n },\n {\n emoji: '🐕‍🦺',\n title: 'Service Dog',\n },\n {\n emoji: '🐩',\n title: 'Poodle',\n },\n {\n emoji: '🐺',\n title: 'Wolf',\n },\n {\n emoji: '🦊',\n title: 'Fox',\n },\n {\n emoji: '🦝',\n title: 'Raccoon',\n },\n {\n emoji: '🐱',\n title: 'Cat Face',\n },\n {\n emoji: '🐈',\n title: 'Cat',\n },\n {\n emoji: '🐈‍⬛',\n title: 'Black Cat',\n },\n {\n emoji: '🦁',\n title: 'Lion',\n },\n {\n emoji: '🐯',\n title: 'Tiger Face',\n },\n {\n emoji: '🐅',\n title: 'Tiger',\n },\n {\n emoji: '🐆',\n title: 'Leopard',\n },\n {\n emoji: '🐴',\n title: 'Horse Face',\n },\n {\n emoji: '🐎',\n title: 'Horse',\n },\n {\n emoji: '🦄',\n title: 'Unicorn',\n },\n {\n emoji: '🦓',\n title: 'Zebra',\n },\n {\n emoji: '🦌',\n title: 'Deer',\n },\n {\n emoji: '🦬',\n title: 'Bison',\n },\n {\n emoji: '🐮',\n title: 'Cow Face',\n },\n {\n emoji: '🐂',\n title: 'Ox',\n },\n {\n emoji: '🐃',\n title: 'Water Buffalo',\n },\n {\n emoji: '🐄',\n title: 'Cow',\n },\n {\n emoji: '🐷',\n title: 'Pig Face',\n },\n {\n emoji: '🐖',\n title: 'Pig',\n },\n {\n emoji: '🐗',\n title: 'Boar',\n },\n {\n emoji: '🐽',\n title: 'Pig Nose',\n },\n {\n emoji: '🐏',\n title: 'Ram',\n },\n {\n emoji: '🐑',\n title: 'Ewe',\n },\n {\n emoji: '🐐',\n title: 'Goat',\n },\n {\n emoji: '🐪',\n title: 'Camel',\n },\n {\n emoji: '🐫',\n title: 'Two-Hump Camel',\n },\n {\n emoji: '🦙',\n title: 'Llama',\n },\n {\n emoji: '🦒',\n title: 'Giraffe',\n },\n {\n emoji: '🐘',\n title: 'Elephant',\n },\n {\n emoji: '🦣',\n title: 'Mammoth',\n },\n {\n emoji: '🦏',\n title: 'Rhinoceros',\n },\n {\n emoji: '🦛',\n title: 'Hippopotamus',\n },\n {\n emoji: '🐭',\n title: 'Mouse Face',\n },\n {\n emoji: '🐁',\n title: 'Mouse',\n },\n {\n emoji: '🐀',\n title: 'Rat',\n },\n {\n emoji: '🐹',\n title: 'Hamster',\n },\n {\n emoji: '🐰',\n title: 'Rabbit Face',\n },\n {\n emoji: '🐇',\n title: 'Rabbit',\n },\n {\n emoji: '🐿️',\n title: 'Chipmunk',\n },\n {\n emoji: '🦫',\n title: 'Beaver',\n },\n {\n emoji: '🦔',\n title: 'Hedgehog',\n },\n {\n emoji: '🦇',\n title: 'Bat',\n },\n {\n emoji: '🐻',\n title: 'Bear',\n },\n {\n emoji: '🐻‍❄️',\n title: 'Polar Bear',\n },\n {\n emoji: '🐨',\n title: 'Koala',\n },\n {\n emoji: '🐼',\n title: 'Panda',\n },\n {\n emoji: '🦥',\n title: 'Sloth',\n },\n {\n emoji: '🦦',\n title: 'Otter',\n },\n {\n emoji: '🦨',\n title: 'Skunk',\n },\n {\n emoji: '🦘',\n title: 'Kangaroo',\n },\n {\n emoji: '🦡',\n title: 'Badger',\n },\n {\n emoji: '🐾',\n title: 'Paw Prints',\n },\n {\n emoji: '🦃',\n title: 'Turkey',\n },\n {\n emoji: '🐔',\n title: 'Chicken',\n },\n {\n emoji: '🐓',\n title: 'Rooster',\n },\n {\n emoji: '🐣',\n title: 'Hatching Chick',\n },\n {\n emoji: '🐤',\n title: 'Baby Chick',\n },\n {\n emoji: '🐥',\n title: 'Front-Facing Baby Chick',\n },\n {\n emoji: '🐦',\n title: 'Bird',\n },\n {\n emoji: '🐧',\n title: 'Penguin',\n },\n {\n emoji: '🕊️',\n title: 'Dove',\n },\n {\n emoji: '🦅',\n title: 'Eagle',\n },\n {\n emoji: '🦆',\n title: 'Duck',\n },\n {\n emoji: '🦢',\n title: 'Swan',\n },\n {\n emoji: '🦉',\n title: 'Owl',\n },\n {\n emoji: '🦤',\n title: 'Dodo',\n },\n {\n emoji: '🪶',\n title: 'Feather',\n },\n {\n emoji: '🦩',\n title: 'Flamingo',\n },\n {\n emoji: '🦚',\n title: 'Peacock',\n },\n {\n emoji: '🦜',\n title: 'Parrot',\n },\n {\n emoji: '🐸',\n title: 'Frog',\n },\n {\n emoji: '🐊',\n title: 'Crocodile',\n },\n {\n emoji: '🐢',\n title: 'Turtle',\n },\n {\n emoji: '🦎',\n title: 'Lizard',\n },\n {\n emoji: '🐍',\n title: 'Snake',\n },\n {\n emoji: '🐲',\n title: 'Dragon Face',\n },\n {\n emoji: '🐉',\n title: 'Dragon',\n },\n {\n emoji: '🦕',\n title: 'Sauropod',\n },\n {\n emoji: '🦖',\n title: 'T-Rex',\n },\n {\n emoji: '🐳',\n title: 'Spouting Whale',\n },\n {\n emoji: '🐋',\n title: 'Whale',\n },\n {\n emoji: '🐬',\n title: 'Dolphin',\n },\n {\n emoji: '🦭',\n title: 'Seal',\n },\n {\n emoji: '🐟',\n title: 'Fish',\n },\n {\n emoji: '🐠',\n title: 'Tropical Fish',\n },\n {\n emoji: '🐡',\n title: 'Blowfish',\n },\n {\n emoji: '🦈',\n title: 'Shark',\n },\n {\n emoji: '🐙',\n title: 'Octopus',\n },\n {\n emoji: '🐚',\n title: 'Spiral Shell',\n },\n {\n emoji: '🐌',\n title: 'Snail',\n },\n {\n emoji: '🦋',\n title: 'Butterfly',\n },\n {\n emoji: '🐛',\n title: 'Bug',\n },\n {\n emoji: '🐜',\n title: 'Ant',\n },\n {\n emoji: '🐝',\n title: 'Honeybee',\n },\n {\n emoji: '🪲',\n title: 'Beetle',\n },\n {\n emoji: '🐞',\n title: 'Lady Beetle',\n },\n {\n emoji: '🦗',\n title: 'Cricket',\n },\n {\n emoji: '🪳',\n title: 'Cockroach',\n },\n {\n emoji: '🕷️',\n title: 'Spider',\n },\n {\n emoji: '🕸️',\n title: 'Spider Web',\n },\n {\n emoji: '🦂',\n title: 'Scorpion',\n },\n {\n emoji: '🦟',\n title: 'Mosquito',\n },\n {\n emoji: '🪰',\n title: 'Fly',\n },\n {\n emoji: '🪱',\n title: 'Worm',\n },\n {\n emoji: '🦠',\n title: 'Microbe',\n },\n {\n emoji: '💐',\n title: 'Bouquet',\n },\n {\n emoji: '🌸',\n title: 'Cherry Blossom',\n },\n {\n emoji: '💮',\n title: 'White Flower',\n },\n {\n emoji: '🏵️',\n title: 'Rosette',\n },\n {\n emoji: '🌹',\n title: 'Rose',\n },\n {\n emoji: '🥀',\n title: 'Wilted Flower',\n },\n {\n emoji: '🌺',\n title: 'Hibiscus',\n },\n {\n emoji: '🌻',\n title: 'Sunflower',\n },\n {\n emoji: '🌼',\n title: 'Blossom',\n },\n {\n emoji: '🌷',\n title: 'Tulip',\n },\n {\n emoji: '🌱',\n title: 'Seedling',\n },\n {\n emoji: '🪴',\n title: 'Potted Plant',\n },\n {\n emoji: '🌲',\n title: 'Evergreen Tree',\n },\n {\n emoji: '🌳',\n title: 'Deciduous Tree',\n },\n {\n emoji: '🌴',\n title: 'Palm Tree',\n },\n {\n emoji: '🌵',\n title: 'Cactus',\n },\n {\n emoji: '🌾',\n title: 'Sheaf of Rice',\n },\n {\n emoji: '🌿',\n title: 'Herb',\n },\n {\n emoji: '☘️',\n title: 'Shamrock',\n },\n {\n emoji: '🍀',\n title: 'Four Leaf Clover',\n },\n {\n emoji: '🍁',\n title: 'Maple Leaf',\n },\n {\n emoji: '🍂',\n title: 'Fallen Leaf',\n },\n {\n emoji: '🍃',\n title: 'Leaf Fluttering in Wind',\n },\n {\n emoji: '🍄',\n title: 'Mushroom',\n },\n {\n emoji: '🌰',\n title: 'Chestnut',\n },\n {\n emoji: '🦀',\n title: 'Crab',\n },\n {\n emoji: '🦞',\n title: 'Lobster',\n },\n {\n emoji: '🦐',\n title: 'Shrimp',\n },\n {\n emoji: '🦑',\n title: 'Squid',\n },\n {\n emoji: '🌍',\n title: 'Globe Showing Europe-Africa',\n },\n {\n emoji: '🌎',\n title: 'Globe Showing Americas',\n },\n {\n emoji: '🌏',\n title: 'Globe Showing Asia-Australia',\n },\n {\n emoji: '🌐',\n title: 'Globe with Meridians',\n },\n {\n emoji: '🪨',\n title: 'Rock',\n },\n {\n emoji: '🌑',\n title: 'New Moon',\n },\n {\n emoji: '🌒',\n title: 'Waxing Crescent Moon',\n },\n {\n emoji: '🌓',\n title: 'First Quarter Moon',\n },\n {\n emoji: '🌔',\n title: 'Waxing Gibbous Moon',\n },\n {\n emoji: '🌕',\n title: 'Full Moon',\n },\n {\n emoji: '🌖',\n title: 'Waning Gibbous Moon',\n },\n {\n emoji: '🌗',\n title: 'Last Quarter Moon',\n },\n {\n emoji: '🌘',\n title: 'Waning Crescent Moon',\n },\n {\n emoji: '🌙',\n title: 'Crescent Moon',\n },\n {\n emoji: '🌚',\n title: 'New Moon Face',\n },\n {\n emoji: '🌛',\n title: 'First Quarter Moon Face',\n },\n {\n emoji: '🌜',\n title: 'Last Quarter Moon Face',\n },\n {\n emoji: '☀️',\n title: 'Sun',\n },\n {\n emoji: '🌝',\n title: 'Full Moon Face',\n },\n {\n emoji: '🌞',\n title: 'Sun with Face',\n },\n {\n emoji: '⭐',\n title: 'Star',\n },\n {\n emoji: '🌟',\n title: 'Glowing Star',\n },\n {\n emoji: '🌠',\n title: 'Shooting Star',\n },\n {\n emoji: '☁️',\n title: 'Cloud',\n },\n {\n emoji: '⛅',\n title: 'Sun Behind Cloud',\n },\n {\n emoji: '⛈️',\n title: 'Cloud with Lightning and Rain',\n },\n {\n emoji: '🌤️',\n title: 'Sun Behind Small Cloud',\n },\n {\n emoji: '🌥️',\n title: 'Sun Behind Large Cloud',\n },\n {\n emoji: '🌦️',\n title: 'Sun Behind Rain Cloud',\n },\n {\n emoji: '🌧️',\n title: 'Cloud with Rain',\n },\n {\n emoji: '🌨️',\n title: 'Cloud with Snow',\n },\n {\n emoji: '🌩️',\n title: 'Cloud with Lightning',\n },\n {\n emoji: '🌪️',\n title: 'Tornado',\n },\n {\n emoji: '🌫️',\n title: 'Fog',\n },\n {\n emoji: '🌬️',\n title: 'Wind Face',\n },\n {\n emoji: '🌈',\n title: 'Rainbow',\n },\n {\n emoji: '☂️',\n title: 'Umbrella',\n },\n {\n emoji: '☔',\n title: 'Umbrella with Rain Drops',\n },\n {\n emoji: '⚡',\n title: 'High Voltage',\n },\n {\n emoji: '❄️',\n title: 'Snowflake',\n },\n {\n emoji: '☃️',\n title: 'Snowman',\n },\n {\n emoji: '⛄',\n title: 'Snowman Without Snow',\n },\n {\n emoji: '☄️',\n title: 'Comet',\n },\n {\n emoji: '🔥',\n title: 'Fire',\n },\n {\n emoji: '💧',\n title: 'Droplet',\n },\n {\n emoji: '🌊',\n title: 'Water Wave',\n },\n {\n emoji: '🎄',\n title: 'Christmas Tree',\n },\n {\n emoji: '✨',\n title: 'Sparkles',\n },\n {\n emoji: '🎋',\n title: 'Tanabata Tree',\n },\n {\n emoji: '🎍',\n title: 'Pine Decoration',\n },\n ],\n 'Food-drink': [\n {\n emoji: '🍇',\n title: 'Grapes',\n },\n {\n emoji: '🍈',\n title: 'Melon',\n },\n {\n emoji: '🍉',\n title: 'Watermelon',\n },\n {\n emoji: '🍊',\n title: 'Tangerine',\n },\n {\n emoji: '🍋',\n title: 'Lemon',\n },\n {\n emoji: '🍌',\n title: 'Banana',\n },\n {\n emoji: '🍍',\n title: 'Pineapple',\n },\n {\n emoji: '🥭',\n title: 'Mango',\n },\n {\n emoji: '🍎',\n title: 'Red Apple',\n },\n {\n emoji: '🍏',\n title: 'Green Apple',\n },\n {\n emoji: '🍐',\n title: 'Pear',\n },\n {\n emoji: '🍑',\n title: 'Peach',\n },\n {\n emoji: '🍒',\n title: 'Cherries',\n },\n {\n emoji: '🍓',\n title: 'Strawberry',\n },\n {\n emoji: '🫐',\n title: 'Blueberries',\n },\n {\n emoji: '🥝',\n title: 'Kiwi Fruit',\n },\n {\n emoji: '🍅',\n title: 'Tomato',\n },\n {\n emoji: '🫒',\n title: 'Olive',\n },\n {\n emoji: '🥥',\n title: 'Coconut',\n },\n {\n emoji: '🥑',\n title: 'Avocado',\n },\n {\n emoji: '🍆',\n title: 'Eggplant',\n },\n {\n emoji: '🥔',\n title: 'Potato',\n },\n {\n emoji: '🥕',\n title: 'Carrot',\n },\n {\n emoji: '🌽',\n title: 'Ear of Corn',\n },\n {\n emoji: '🌶️',\n title: 'Hot Pepper',\n },\n {\n emoji: '🫑',\n title: 'Bell Pepper',\n },\n {\n emoji: '🥒',\n title: 'Cucumber',\n },\n {\n emoji: '🥬',\n title: 'Leafy Green',\n },\n {\n emoji: '🥦',\n title: 'Broccoli',\n },\n {\n emoji: '🧄',\n title: 'Garlic',\n },\n {\n emoji: '🧅',\n title: 'Onion',\n },\n {\n emoji: '🍄',\n title: 'Mushroom',\n },\n {\n emoji: '🥜',\n title: 'Peanuts',\n },\n {\n emoji: '🌰',\n title: 'Chestnut',\n },\n {\n emoji: '🍞',\n title: 'Bread',\n },\n {\n emoji: '🥐',\n title: 'Croissant',\n },\n {\n emoji: '🥖',\n title: 'Baguette Bread',\n },\n {\n emoji: '🫓',\n title: 'Flatbread',\n },\n {\n emoji: '🥨',\n title: 'Pretzel',\n },\n {\n emoji: '🥯',\n title: 'Bagel',\n },\n {\n emoji: '🥞',\n title: 'Pancakes',\n },\n {\n emoji: '🧇',\n title: 'Waffle',\n },\n {\n emoji: '🧀',\n title: 'Cheese Wedge',\n },\n {\n emoji: '🍖',\n title: 'Meat on Bone',\n },\n {\n emoji: '🍗',\n title: 'Poultry Leg',\n },\n {\n emoji: '🥩',\n title: 'Cut of Meat',\n },\n {\n emoji: '🥓',\n title: 'Bacon',\n },\n {\n emoji: '🍔',\n title: 'Hamburger',\n },\n {\n emoji: '🍟',\n title: 'French Fries',\n },\n {\n emoji: '🍕',\n title: 'Pizza',\n },\n {\n emoji: '🌭',\n title: 'Hot Dog',\n },\n {\n emoji: '🥪',\n title: 'Sandwich',\n },\n {\n emoji: '🌮',\n title: 'Taco',\n },\n {\n emoji: '🌯',\n title: 'Burrito',\n },\n {\n emoji: '🫔',\n title: 'Tamale',\n },\n {\n emoji: '🥙',\n title: 'Stuffed Flatbread',\n },\n {\n emoji: '🧆',\n title: 'Falafel',\n },\n {\n emoji: '🥚',\n title: 'Egg',\n },\n {\n emoji: '🍳',\n title: 'Cooking',\n },\n {\n emoji: '🥘',\n title: 'Shallow Pan of Food',\n },\n {\n emoji: '🍲',\n title: 'Pot of Food',\n },\n {\n emoji: '🫕',\n title: 'Fondue',\n },\n {\n emoji: '🥣',\n title: 'Bowl with Spoon',\n },\n {\n emoji: '🥗',\n title: 'Green Salad',\n },\n {\n emoji: '🍿',\n title: 'Popcorn',\n },\n {\n emoji: '🧈',\n title: 'Butter',\n },\n {\n emoji: '🧂',\n title: 'Salt',\n },\n {\n emoji: '🥫',\n title: 'Canned Food',\n },\n {\n emoji: '🍱',\n title: 'Bento Box',\n },\n {\n emoji: '🍘',\n title: 'Rice Cracker',\n },\n {\n emoji: '🍙',\n title: 'Rice Ball',\n },\n {\n emoji: '🍚',\n title: 'Cooked Rice',\n },\n {\n emoji: '🍛',\n title: 'Curry Rice',\n },\n {\n emoji: '🍜',\n title: 'Steaming Bowl',\n },\n {\n emoji: '🍝',\n title: 'Spaghetti',\n },\n {\n emoji: '🍠',\n title: 'Roasted Sweet Potato',\n },\n {\n emoji: '🍢',\n title: 'Oden',\n },\n {\n emoji: '🍣',\n title: 'Sushi',\n },\n {\n emoji: '🍤',\n title: 'Fried Shrimp',\n },\n {\n emoji: '🍥',\n title: 'Fish Cake with Swirl',\n },\n {\n emoji: '🥮',\n title: 'Moon Cake',\n },\n {\n emoji: '🍡',\n title: 'Dango',\n },\n {\n emoji: '🥟',\n title: 'Dumpling',\n },\n {\n emoji: '🥠',\n title: 'Fortune Cookie',\n },\n {\n emoji: '🥡',\n title: 'Takeout Box',\n },\n {\n emoji: '🦪',\n title: 'Oyster',\n },\n {\n emoji: '🍦',\n title: 'Soft Ice Cream',\n },\n {\n emoji: '🍧',\n title: 'Shaved Ice',\n },\n {\n emoji: '🍨',\n title: 'Ice Cream',\n },\n {\n emoji: '🍩',\n title: 'Doughnut',\n },\n {\n emoji: '🍪',\n title: 'Cookie',\n },\n {\n emoji: '🎂',\n title: 'Birthday Cake',\n },\n {\n emoji: '🍰',\n title: 'Shortcake',\n },\n {\n emoji: '🧁',\n title: 'Cupcake',\n },\n {\n emoji: '🥧',\n title: 'Pie',\n },\n {\n emoji: '🍫',\n title: 'Chocolate Bar',\n },\n {\n emoji: '🍬',\n title: 'Candy',\n },\n {\n emoji: '🍭',\n title: 'Lollipop',\n },\n {\n emoji: '🍮',\n title: 'Custard',\n },\n {\n emoji: '🍯',\n title: 'Honey Pot',\n },\n {\n emoji: '🍼',\n title: 'Baby Bottle',\n },\n {\n emoji: '🥛',\n title: 'Glass of Milk',\n },\n {\n emoji: '☕',\n title: 'Hot Beverage',\n },\n {\n emoji: '🫖',\n title: 'Teapot',\n },\n {\n emoji: '🍵',\n title: 'Teacup Without Handle',\n },\n {\n emoji: '🍶',\n title: 'Sake',\n },\n {\n emoji: '🍾',\n title: 'Bottle with Popping Cork',\n },\n {\n emoji: '🍷',\n title: 'Wine Glass',\n },\n {\n emoji: '🍸',\n title: 'Cocktail Glass',\n },\n {\n emoji: '🍹',\n title: 'Tropical Drink',\n },\n {\n emoji: '🍺',\n title: 'Beer Mug',\n },\n {\n emoji: '🍻',\n title: 'Clinking Beer Mugs',\n },\n {\n emoji: '🥂',\n title: 'Clinking Glasses',\n },\n {\n emoji: '🥃',\n title: 'Tumbler Glass',\n },\n {\n emoji: '🥤',\n title: 'Cup with Straw',\n },\n {\n emoji: '🧋',\n title: 'Bubble Tea',\n },\n {\n emoji: '🧃',\n title: 'Beverage Box',\n },\n {\n emoji: '🧉',\n title: 'Mate',\n },\n {\n emoji: '🧊',\n title: 'Ice',\n },\n {\n emoji: '🥢',\n title: 'Chopsticks',\n },\n {\n emoji: '🍽️',\n title: 'Fork and Knife with Plate',\n },\n {\n emoji: '🍴',\n title: 'Fork and Knife',\n },\n {\n emoji: '🥄',\n title: 'Spoon',\n },\n ],\n Activity: [\n {\n emoji: '🕴️',\n title: 'Person in Suit Levitating',\n },\n {\n emoji: '🧗',\n title: 'Person Climbing',\n },\n {\n emoji: '🧗‍♂️',\n title: 'Man Climbing',\n },\n {\n emoji: '🧗‍♀️',\n title: 'Woman Climbing',\n },\n {\n emoji: '🤺',\n title: 'Person Fencing',\n },\n {\n emoji: '🏇',\n title: 'Horse Racing',\n },\n {\n emoji: '⛷️',\n title: 'Skier',\n },\n {\n emoji: '🏂',\n title: 'Snowboarder',\n },\n {\n emoji: '🏌️',\n title: 'Person Golfing',\n },\n {\n emoji: '🏌️‍♂️',\n title: 'Man Golfing',\n },\n {\n emoji: '🏌️‍♀️',\n title: 'Woman Golfing',\n },\n {\n emoji: '🏄',\n title: 'Person Surfing',\n },\n {\n emoji: '🏄‍♂️',\n title: 'Man Surfing',\n },\n {\n emoji: '🏄‍♀️',\n title: 'Woman Surfing',\n },\n {\n emoji: '🚣',\n title: 'Person Rowing Boat',\n },\n {\n emoji: '🚣‍♂️',\n title: 'Man Rowing Boat',\n },\n {\n emoji: '🚣‍♀️',\n title: 'Woman Rowing Boat',\n },\n {\n emoji: '🏊',\n title: 'Person Swimming',\n },\n {\n emoji: '🏊‍♂️',\n title: 'Man Swimming',\n },\n {\n emoji: '🏊‍♀️',\n title: 'Woman Swimming',\n },\n {\n emoji: '⛹️',\n title: 'Person Bouncing Ball',\n },\n {\n emoji: '⛹️‍♂️',\n title: 'Man Bouncing Ball',\n },\n {\n emoji: '⛹️‍♀️',\n title: 'Woman Bouncing Ball',\n },\n {\n emoji: '🏋️',\n title: 'Person Lifting Weights',\n },\n {\n emoji: '🏋️‍♂️',\n title: 'Man Lifting Weights',\n },\n {\n emoji: '🏋️‍♀️',\n title: 'Woman Lifting Weights',\n },\n {\n emoji: '🚴',\n title: 'Person Biking',\n },\n {\n emoji: '🚴‍♂️',\n title: 'Man Biking',\n },\n {\n emoji: '🚴‍♀️',\n title: 'Woman Biking',\n },\n {\n emoji: '🚵',\n title: 'Person Mountain Biking',\n },\n {\n emoji: '🚵‍♂️',\n title: 'Man Mountain Biking',\n },\n {\n emoji: '🚵‍♀️',\n title: 'Woman Mountain Biking',\n },\n {\n emoji: '🤸',\n title: 'Person Cartwheeling',\n },\n {\n emoji: '🤸‍♂️',\n title: 'Man Cartwheeling',\n },\n {\n emoji: '🤸‍♀️',\n title: 'Woman Cartwheeling',\n },\n {\n emoji: '🤼',\n title: 'People Wrestling',\n },\n {\n emoji: '🤼‍♂️',\n title: 'Men Wrestling',\n },\n {\n emoji: '🤼‍♀️',\n title: 'Women Wrestling',\n },\n {\n emoji: '🤽',\n title: 'Person Playing Water Polo',\n },\n {\n emoji: '🤽‍♂️',\n title: 'Man Playing Water Polo',\n },\n {\n emoji: '🤽‍♀️',\n title: 'Woman Playing Water Polo',\n },\n {\n emoji: '🤾',\n title: 'Person Playing Handball',\n },\n {\n emoji: '🤾‍♂️',\n title: 'Man Playing Handball',\n },\n {\n emoji: '🤾‍♀️',\n title: 'Woman Playing Handball',\n },\n {\n emoji: '🤹',\n title: 'Person Juggling',\n },\n {\n emoji: '🤹‍♂️',\n title: 'Man Juggling',\n },\n {\n emoji: '🤹‍♀️',\n title: 'Woman Juggling',\n },\n {\n emoji: '🧘',\n title: 'Person in Lotus Position',\n },\n {\n emoji: '🧘‍♂️',\n title: 'Man in Lotus Position',\n },\n {\n emoji: '🧘‍♀️',\n title: 'Woman in Lotus Position',\n },\n {\n emoji: '🎪',\n title: 'Circus Tent',\n },\n {\n emoji: '🛹',\n title: 'Skateboard',\n },\n {\n emoji: '🛼',\n title: 'Roller Skate',\n },\n {\n emoji: '🛶',\n title: 'Canoe',\n },\n {\n emoji: '🎗️',\n title: 'Reminder Ribbon',\n },\n {\n emoji: '🎟️',\n title: 'Admission Tickets',\n },\n {\n emoji: '🎫',\n title: 'Ticket',\n },\n {\n emoji: '🎖️',\n title: 'Military Medal',\n },\n {\n emoji: '🏆',\n title: 'Trophy',\n },\n {\n emoji: '🏅',\n title: 'Sports Medal',\n },\n {\n emoji: '🥇',\n title: '1st Place Medal',\n },\n {\n emoji: '🥈',\n title: '2nd Place Medal',\n },\n {\n emoji: '🥉',\n title: '3rd Place Medal',\n },\n {\n emoji: '⚽',\n title: 'Soccer Ball',\n },\n {\n emoji: '⚾',\n title: 'Baseball',\n },\n {\n emoji: '🥎',\n title: 'Softball',\n },\n {\n emoji: '🏀',\n title: 'Basketball',\n },\n {\n emoji: '🏐',\n title: 'Volleyball',\n },\n {\n emoji: '🏈',\n title: 'American Football',\n },\n {\n emoji: '🏉',\n title: 'Rugby Football',\n },\n {\n emoji: '🎾',\n title: 'Tennis',\n },\n {\n emoji: '🥏',\n title: 'Flying Disc',\n },\n {\n emoji: '🎳',\n title: 'Bowling',\n },\n {\n emoji: '🏏',\n title: 'Cricket Game',\n },\n {\n emoji: '🏑',\n title: 'Field Hockey',\n },\n {\n emoji: '🏒',\n title: 'Ice Hockey',\n },\n {\n emoji: '🥍',\n title: 'Lacrosse',\n },\n {\n emoji: '🏓',\n title: 'Ping Pong',\n },\n {\n emoji: '🏸',\n title: 'Badminton',\n },\n {\n emoji: '🥊',\n title: 'Boxing Glove',\n },\n {\n emoji: '🥋',\n title: 'Martial Arts Uniform',\n },\n {\n emoji: '🥅',\n title: 'Goal Net',\n },\n {\n emoji: '⛳',\n title: 'Flag in Hole',\n },\n {\n emoji: '⛸️',\n title: 'Ice Skate',\n },\n {\n emoji: '🎣',\n title: 'Fishing Pole',\n },\n {\n emoji: '🎽',\n title: 'Running Shirt',\n },\n {\n emoji: '🎿',\n title: 'Skis',\n },\n {\n emoji: '🛷',\n title: 'Sled',\n },\n {\n emoji: '🥌',\n title: 'Curling Stone',\n },\n {\n emoji: '🎯',\n title: 'Bullseye',\n },\n {\n emoji: '🎱',\n title: 'Pool 8 Ball',\n },\n {\n emoji: '🎮',\n title: 'Video Game',\n },\n {\n emoji: '🎰',\n title: 'Slot Machine',\n },\n {\n emoji: '🎲',\n title: 'Game Die',\n },\n {\n emoji: '🧩',\n title: 'Puzzle Piece',\n },\n {\n emoji: '♟️',\n title: 'Chess Pawn',\n },\n {\n emoji: '🎭',\n title: 'Performing Arts',\n },\n {\n emoji: '🎨',\n title: 'Artist Palette',\n },\n {\n emoji: '🧵',\n title: 'Thread',\n },\n {\n emoji: '🧶',\n title: 'Yarn',\n },\n {\n emoji: '🎼',\n title: 'Musical Score',\n },\n {\n emoji: '🎤',\n title: 'Microphone',\n },\n {\n emoji: '🎧',\n title: 'Headphone',\n },\n {\n emoji: '🎷',\n title: 'Saxophone',\n },\n {\n emoji: '🪗',\n title: 'Accordion',\n },\n {\n emoji: '🎸',\n title: 'Guitar',\n },\n {\n emoji: '🎹',\n title: 'Musical Keyboard',\n },\n {\n emoji: '🎺',\n title: 'Trumpet',\n },\n {\n emoji: '🎻',\n title: 'Violin',\n },\n {\n emoji: '🥁',\n title: 'Drum',\n },\n {\n emoji: '🪘',\n title: 'Long Drum',\n },\n {\n emoji: '🎬',\n title: 'Clapper Board',\n },\n {\n emoji: '🏹',\n title: 'Bow and Arrow',\n },\n ],\n 'Travel-places': [\n {\n emoji: '🚣',\n title: 'Person Rowing Boat',\n },\n {\n emoji: '🗾',\n title: 'Map of Japan',\n },\n {\n emoji: '🏔️',\n title: 'Snow-Capped Mountain',\n },\n {\n emoji: '⛰️',\n title: 'Mountain',\n },\n {\n emoji: '🌋',\n title: 'Volcano',\n },\n {\n emoji: '🗻',\n title: 'Mount Fuji',\n },\n {\n emoji: '🏕️',\n title: 'Camping',\n },\n {\n emoji: '🏖️',\n title: 'Beach with Umbrella',\n },\n {\n emoji: '🏜️',\n title: 'Desert',\n },\n {\n emoji: '🏝️',\n title: 'Desert Island',\n },\n {\n emoji: '🏞️',\n title: 'National Park',\n },\n {\n emoji: '🏟️',\n title: 'Stadium',\n },\n {\n emoji: '🏛️',\n title: 'Classical Building',\n },\n {\n emoji: '🏗️',\n title: 'Building Construction',\n },\n {\n emoji: '🛖',\n title: 'Hut',\n },\n {\n emoji: '🏘️',\n title: 'Houses',\n },\n {\n emoji: '🏚️',\n title: 'Derelict House',\n },\n {\n emoji: '🏠',\n title: 'House',\n },\n {\n emoji: '🏡',\n title: 'House with Garden',\n },\n {\n emoji: '🏢',\n title: 'Office Building',\n },\n {\n emoji: '🏣',\n title: 'Japanese Post Office',\n },\n {\n emoji: '🏤',\n title: 'Post Office',\n },\n {\n emoji: '🏥',\n title: 'Hospital',\n },\n {\n emoji: '🏦',\n title: 'Bank',\n },\n {\n emoji: '🏨',\n title: 'Hotel',\n },\n {\n emoji: '🏩',\n title: 'Love Hotel',\n },\n {\n emoji: '🏪',\n title: 'Convenience Store',\n },\n {\n emoji: '🏫',\n title: 'School',\n },\n {\n emoji: '🏬',\n title: 'Department Store',\n },\n {\n emoji: '🏭',\n title: 'Factory',\n },\n {\n emoji: '🏯',\n title: 'Japanese Castle',\n },\n {\n emoji: '🏰',\n title: 'Castle',\n },\n {\n emoji: '💒',\n title: 'Wedding',\n },\n {\n emoji: '🗼',\n title: 'Tokyo Tower',\n },\n {\n emoji: '🗽',\n title: 'Statue of Liberty',\n },\n {\n emoji: '⛪',\n title: 'Church',\n },\n {\n emoji: '🕌',\n title: 'Mosque',\n },\n {\n emoji: '🛕',\n title: 'Hindu Temple',\n },\n {\n emoji: '🕍',\n title: 'Synagogue',\n },\n {\n emoji: '⛩️',\n title: 'Shinto Shrine',\n },\n {\n emoji: '🕋',\n title: 'Kaaba',\n },\n {\n emoji: '⛲',\n title: 'Fountain',\n },\n {\n emoji: '⛺',\n title: 'Tent',\n },\n {\n emoji: '🌁',\n title: 'Foggy',\n },\n {\n emoji: '🌃',\n title: 'Night with Stars',\n },\n {\n emoji: '🏙️',\n title: 'Cityscape',\n },\n {\n emoji: '🌄',\n title: 'Sunrise Over Mountains',\n },\n {\n emoji: '🌅',\n title: 'Sunrise',\n },\n {\n emoji: '🌆',\n title: 'Cityscape at Dusk',\n },\n {\n emoji: '🌇',\n title: 'Sunset',\n },\n {\n emoji: '🌉',\n title: 'Bridge at Night',\n },\n {\n emoji: '🎠',\n title: 'Carousel Horse',\n },\n {\n emoji: '🎡',\n title: 'Ferris Wheel',\n },\n {\n emoji: '🎢',\n title: 'Roller Coaster',\n },\n {\n emoji: '🚂',\n title: 'Locomotive',\n },\n {\n emoji: '🚃',\n title: 'Railway Car',\n },\n {\n emoji: '🚄',\n title: 'High-Speed Train',\n },\n {\n emoji: '🚅',\n title: 'Bullet Train',\n },\n {\n emoji: '🚆',\n title: 'Train',\n },\n {\n emoji: '🚇',\n title: 'Metro',\n },\n {\n emoji: '🚈',\n title: 'Light Rail',\n },\n {\n emoji: '🚉',\n title: 'Station',\n },\n {\n emoji: '🚊',\n title: 'Tram',\n },\n {\n emoji: '🚝',\n title: 'Monorail',\n },\n {\n emoji: '🚞',\n title: 'Mountain Railway',\n },\n {\n emoji: '🚋',\n title: 'Tram Car',\n },\n {\n emoji: '🚌',\n title: 'Bus',\n },\n {\n emoji: '🚍',\n title: 'Oncoming Bus',\n },\n {\n emoji: '🚎',\n title: 'Trolleybus',\n },\n {\n emoji: '🚐',\n title: 'Minibus',\n },\n {\n emoji: '🚑',\n title: 'Ambulance',\n },\n {\n emoji: '🚒',\n title: 'Fire Engine',\n },\n {\n emoji: '🚓',\n title: 'Police Car',\n },\n {\n emoji: '🚔',\n title: 'Oncoming Police Car',\n },\n {\n emoji: '🚕',\n title: 'Taxi',\n },\n {\n emoji: '🚖',\n title: 'Oncoming Taxi',\n },\n {\n emoji: '🚗',\n title: 'Automobile',\n },\n {\n emoji: '🚘',\n title: 'Oncoming Automobile',\n },\n {\n emoji: '🚙',\n title: 'Sport Utility Vehicle',\n },\n {\n emoji: '🛻',\n title: 'Pickup Truck',\n },\n {\n emoji: '🚚',\n title: 'Delivery Truck',\n },\n {\n emoji: '🚛',\n title: 'Articulated Lorry',\n },\n {\n emoji: '🚜',\n title: 'Tractor',\n },\n {\n emoji: '🏎️',\n title: 'Racing Car',\n },\n {\n emoji: '🏍️',\n title: 'Motorcycle',\n },\n {\n emoji: '🛵',\n title: 'Motor Scooter',\n },\n {\n emoji: '🛺',\n title: 'Auto Rickshaw',\n },\n {\n emoji: '🚲',\n title: 'Bicycle',\n },\n {\n emoji: '🛴',\n title: 'Kick Scooter',\n },\n {\n emoji: '🚏',\n title: 'Bus Stop',\n },\n {\n emoji: '🛣️',\n title: 'Motorway',\n },\n {\n emoji: '🛤️',\n title: 'Railway Track',\n },\n {\n emoji: '⛽',\n title: 'Fuel Pump',\n },\n {\n emoji: '🚨',\n title: 'Police Car Light',\n },\n {\n emoji: '🚥',\n title: 'Horizontal Traffic Light',\n },\n {\n emoji: '🚦',\n title: 'Vertical Traffic Light',\n },\n {\n emoji: '🚧',\n title: 'Construction',\n },\n {\n emoji: '⚓',\n title: 'Anchor',\n },\n {\n emoji: '⛵',\n title: 'Sailboat',\n },\n {\n emoji: '🚤',\n title: 'Speedboat',\n },\n {\n emoji: '🛳️',\n title: 'Passenger Ship',\n },\n {\n emoji: '⛴️',\n title: 'Ferry',\n },\n {\n emoji: '🛥️',\n title: 'Motor Boat',\n },\n {\n emoji: '🚢',\n title: 'Ship',\n },\n {\n emoji: '✈️',\n title: 'Airplane',\n },\n {\n emoji: '🛩️',\n title: 'Small Airplane',\n },\n {\n emoji: '🛫',\n title: 'Airplane Departure',\n },\n {\n emoji: '🛬',\n title: 'Airplane Arrival',\n },\n {\n emoji: '🪂',\n title: 'Parachute',\n },\n {\n emoji: '💺',\n title: 'Seat',\n },\n {\n emoji: '🚁',\n title: 'Helicopter',\n },\n {\n emoji: '🚟',\n title: 'Suspension Railway',\n },\n {\n emoji: '🚠',\n title: 'Mountain Cableway',\n },\n {\n emoji: '🚡',\n title: 'Aerial Tramway',\n },\n {\n emoji: '🛰️',\n title: 'Satellite',\n },\n {\n emoji: '🚀',\n title: 'Rocket',\n },\n {\n emoji: '🛸',\n title: 'Flying Saucer',\n },\n {\n emoji: '🪐',\n title: 'Ringed Planet',\n },\n {\n emoji: '🌠',\n title: 'Shooting Star',\n },\n {\n emoji: '🌌',\n title: 'Milky Way',\n },\n {\n emoji: '⛱️',\n title: 'Umbrella on Ground',\n },\n {\n emoji: '🎆',\n title: 'Fireworks',\n },\n {\n emoji: '🎇',\n title: 'Sparkler',\n },\n {\n emoji: '🎑',\n title: 'Moon Viewing Ceremony',\n },\n {\n emoji: '💴',\n title: 'Yen Banknote',\n },\n {\n emoji: '💵',\n title: 'Dollar Banknote',\n },\n {\n emoji: '💶',\n title: 'Euro Banknote',\n },\n {\n emoji: '💷',\n title: 'Pound Banknote',\n },\n {\n emoji: '🗿',\n title: 'Moai',\n },\n {\n emoji: '🛂',\n title: 'Passport Control',\n },\n {\n emoji: '🛃',\n title: 'Customs',\n },\n {\n emoji: '🛄',\n title: 'Baggage Claim',\n },\n {\n emoji: '🛅',\n title: 'Left Luggage',\n },\n ],\n Objects: [\n {\n emoji: '💌',\n title: 'Love Letter',\n },\n {\n emoji: '🕳️',\n title: 'Hole',\n },\n {\n emoji: '💣',\n title: 'Bomb',\n },\n {\n emoji: '🛀',\n title: 'Person Taking Bath',\n },\n {\n emoji: '🛌',\n title: 'Person in Bed',\n },\n {\n emoji: '🔪',\n title: 'Kitchen Knife',\n },\n {\n emoji: '🏺',\n title: 'Amphora',\n },\n {\n emoji: '🗺️',\n title: 'World Map',\n },\n {\n emoji: '🧭',\n title: 'Compass',\n },\n {\n emoji: '🧱',\n title: 'Brick',\n },\n {\n emoji: '💈',\n title: 'Barber Pole',\n },\n {\n emoji: '🦽',\n title: 'Manual Wheelchair',\n },\n {\n emoji: '🦼',\n title: 'Motorized Wheelchair',\n },\n {\n emoji: '🛢️',\n title: 'Oil Drum',\n },\n {\n emoji: '🛎️',\n title: 'Bellhop Bell',\n },\n {\n emoji: '🧳',\n title: 'Luggage',\n },\n {\n emoji: '⌛',\n title: 'Hourglass Done',\n },\n {\n emoji: '⏳',\n title: 'Hourglass Not Done',\n },\n {\n emoji: '⌚',\n title: 'Watch',\n },\n {\n emoji: '⏰',\n title: 'Alarm Clock',\n },\n {\n emoji: '⏱️',\n title: 'Stopwatch',\n },\n {\n emoji: '⏲️',\n title: 'Timer Clock',\n },\n {\n emoji: '🕰️',\n title: 'Mantelpiece Clock',\n },\n {\n emoji: '🌡️',\n title: 'Thermometer',\n },\n {\n emoji: '⛱️',\n title: 'Umbrella on Ground',\n },\n {\n emoji: '🧨',\n title: 'Firecracker',\n },\n {\n emoji: '🎈',\n title: 'Balloon',\n },\n {\n emoji: '🎉',\n title: 'Party Popper',\n },\n {\n emoji: '🎊',\n title: 'Confetti Ball',\n },\n {\n emoji: '🎎',\n title: 'Japanese Dolls',\n },\n {\n emoji: '🎏',\n title: 'Carp Streamer',\n },\n {\n emoji: '🎐',\n title: 'Wind Chime',\n },\n {\n emoji: '🧧',\n title: 'Red Envelope',\n },\n {\n emoji: '🎀',\n title: 'Ribbon',\n },\n {\n emoji: '🎁',\n title: 'Wrapped Gift',\n },\n {\n emoji: '🤿',\n title: 'Diving Mask',\n },\n {\n emoji: '🪀',\n title: 'Yo-Yo',\n },\n {\n emoji: '🪁',\n title: 'Kite',\n },\n {\n emoji: '🔮',\n title: 'Crystal Ball',\n },\n {\n emoji: '🪄',\n title: 'Magic Wand',\n },\n {\n emoji: '🧿',\n title: 'Nazar Amulet',\n },\n {\n emoji: '🕹️',\n title: 'Joystick',\n },\n {\n emoji: '🧸',\n title: 'Teddy Bear',\n },\n {\n emoji: '🪅',\n title: 'Piñata',\n },\n {\n emoji: '🪆',\n title: 'Nesting Dolls',\n },\n {\n emoji: '🖼️',\n title: 'Framed Picture',\n },\n {\n emoji: '🧵',\n title: 'Thread',\n },\n {\n emoji: '🪡',\n title: 'Sewing Needle',\n },\n {\n emoji: '🧶',\n title: 'Yarn',\n },\n {\n emoji: '🪢',\n title: 'Knot',\n },\n {\n emoji: '🛍️',\n title: 'Shopping Bags',\n },\n {\n emoji: '📿',\n title: 'Prayer Beads',\n },\n {\n emoji: '💎',\n title: 'Gem Stone',\n },\n {\n emoji: '📯',\n title: 'Postal Horn',\n },\n {\n emoji: '🎙️',\n title: 'Studio Microphone',\n },\n {\n emoji: '🎚️',\n title: 'Level Slider',\n },\n {\n emoji: '🎛️',\n title: 'Control Knobs',\n },\n {\n emoji: '📻',\n title: 'Radio',\n },\n {\n emoji: '🪕',\n title: 'Banjo',\n },\n {\n emoji: '📱',\n title: 'Mobile Phone',\n },\n {\n emoji: '📲',\n title: 'Mobile Phone with Arrow',\n },\n {\n emoji: '☎️',\n title: 'Telephone',\n },\n {\n emoji: '📞',\n title: 'Telephone Receiver',\n },\n {\n emoji: '📟',\n title: 'Pager',\n },\n {\n emoji: '📠',\n title: 'Fax Machine',\n },\n {\n emoji: '🔋',\n title: 'Battery',\n },\n {\n emoji: '🔌',\n title: 'Electric Plug',\n },\n {\n emoji: '💻',\n title: 'Laptop',\n },\n {\n emoji: '🖥️',\n title: 'Desktop Computer',\n },\n {\n emoji: '🖨️',\n title: 'Printer',\n },\n {\n emoji: '⌨️',\n title: 'Keyboard',\n },\n {\n emoji: '🖱️',\n title: 'Computer Mouse',\n },\n {\n emoji: '🖲️',\n title: 'Trackball',\n },\n {\n emoji: '💽',\n title: 'Computer Disk',\n },\n {\n emoji: '💾',\n title: 'Floppy Disk',\n },\n {\n emoji: '💿',\n title: 'Optical Disk',\n },\n {\n emoji: '📀',\n title: 'DVD',\n },\n {\n emoji: '🧮',\n title: 'Abacus',\n },\n {\n emoji: '🎥',\n title: 'Movie Camera',\n },\n {\n emoji: '🎞️',\n title: 'Film Frames',\n },\n {\n emoji: '📽️',\n title: 'Film Projector',\n },\n {\n emoji: '📺',\n title: 'Television',\n },\n {\n emoji: '📷',\n title: 'Camera',\n },\n {\n emoji: '📸',\n title: 'Camera with Flash',\n },\n {\n emoji: '📹',\n title: 'Video Camera',\n },\n {\n emoji: '📼',\n title: 'Videocassette',\n },\n {\n emoji: '🔍',\n title: 'Magnifying Glass Tilted Left',\n },\n {\n emoji: '🔎',\n title: 'Magnifying Glass Tilted Right',\n },\n {\n emoji: '🕯️',\n title: 'Candle',\n },\n {\n emoji: '💡',\n title: 'Light Bulb',\n },\n {\n emoji: '🔦',\n title: 'Flashlight',\n },\n {\n emoji: '🏮',\n title: 'Red Paper Lantern',\n },\n {\n emoji: '🪔',\n title: 'Diya Lamp',\n },\n {\n emoji: '📔',\n title: 'Notebook with Decorative Cover',\n },\n {\n emoji: '📕',\n title: 'Closed Book',\n },\n {\n emoji: '📖',\n title: 'Open Book',\n },\n {\n emoji: '📗',\n title: 'Green Book',\n },\n {\n emoji: '📘',\n title: 'Blue Book',\n },\n {\n emoji: '📙',\n title: 'Orange Book',\n },\n {\n emoji: '📚',\n title: 'Books',\n },\n {\n emoji: '📓',\n title: 'Notebook',\n },\n {\n emoji: '📒',\n title: 'Ledger',\n },\n {\n emoji: '📃',\n title: 'Page with Curl',\n },\n {\n emoji: '📜',\n title: 'Scroll',\n },\n {\n emoji: '📄',\n title: 'Page Facing Up',\n },\n {\n emoji: '📰',\n title: 'Newspaper',\n },\n {\n emoji: '🗞️',\n title: 'Rolled-Up Newspaper',\n },\n {\n emoji: '📑',\n title: 'Bookmark Tabs',\n },\n {\n emoji: '🔖',\n title: 'Bookmark',\n },\n {\n emoji: '🏷️',\n title: 'Label',\n },\n {\n emoji: '💰',\n title: 'Money Bag',\n },\n {\n emoji: '🪙',\n title: 'Coin',\n },\n {\n emoji: '💴',\n title: 'Yen Banknote',\n },\n {\n emoji: '💵',\n title: 'Dollar Banknote',\n },\n {\n emoji: '💶',\n title: 'Euro Banknote',\n },\n {\n emoji: '💷',\n title: 'Pound Banknote',\n },\n {\n emoji: '💸',\n title: 'Money with Wings',\n },\n {\n emoji: '💳',\n title: 'Credit Card',\n },\n {\n emoji: '🧾',\n title: 'Receipt',\n },\n {\n emoji: '✉️',\n title: 'Envelope',\n },\n {\n emoji: '📧',\n title: 'E-Mail',\n },\n {\n emoji: '📨',\n title: 'Incoming Envelope',\n },\n {\n emoji: '📩',\n title: 'Envelope with Arrow',\n },\n {\n emoji: '📤',\n title: 'Outbox Tray',\n },\n {\n emoji: '📥',\n title: 'Inbox Tray',\n },\n {\n emoji: '📦',\n title: 'Package',\n },\n {\n emoji: '📫',\n title: 'Closed Mailbox with Raised Flag',\n },\n {\n emoji: '📪',\n title: 'Closed Mailbox with Lowered Flag',\n },\n {\n emoji: '📬',\n title: 'Open Mailbox with Raised Flag',\n },\n {\n emoji: '📭',\n title: 'Open Mailbox with Lowered Flag',\n },\n {\n emoji: '📮',\n title: 'Postbox',\n },\n {\n emoji: '🗳️',\n title: 'Ballot Box with Ballot',\n },\n {\n emoji: '✏️',\n title: 'Pencil',\n },\n {\n emoji: '✒️',\n title: 'Black Nib',\n },\n {\n emoji: '🖋️',\n title: 'Fountain Pen',\n },\n {\n emoji: '🖊️',\n title: 'Pen',\n },\n {\n emoji: '🖌️',\n title: 'Paintbrush',\n },\n {\n emoji: '🖍️',\n title: 'Crayon',\n },\n {\n emoji: '📝',\n title: 'Memo',\n },\n {\n emoji: '📁',\n title: 'File Folder',\n },\n {\n emoji: '📂',\n title: 'Open File Folder',\n },\n {\n emoji: '🗂️',\n title: 'Card Index Dividers',\n },\n {\n emoji: '📅',\n title: 'Calendar',\n },\n {\n emoji: '📆',\n title: 'Tear-Off Calendar',\n },\n {\n emoji: '🗒️',\n title: 'Spiral Notepad',\n },\n {\n emoji: '🗓️',\n title: 'Spiral Calendar',\n },\n {\n emoji: '📇',\n title: 'Card Index',\n },\n {\n emoji: '📈',\n title: 'Chart Increasing',\n },\n {\n emoji: '📉',\n title: 'Chart Decreasing',\n },\n {\n emoji: '📊',\n title: 'Bar Chart',\n },\n {\n emoji: '📋',\n title: 'Clipboard',\n },\n {\n emoji: '📌',\n title: 'Pushpin',\n },\n {\n emoji: '📍',\n title: 'Round Pushpin',\n },\n {\n emoji: '📎',\n title: 'Paperclip',\n },\n {\n emoji: '🖇️',\n title: 'Linked Paperclips',\n },\n {\n emoji: '📏',\n title: 'Straight Ruler',\n },\n {\n emoji: '📐',\n title: 'Triangular Ruler',\n },\n {\n emoji: '✂️',\n title: 'Scissors',\n },\n {\n emoji: '🗃️',\n title: 'Card File Box',\n },\n {\n emoji: '🗄️',\n title: 'File Cabinet',\n },\n {\n emoji: '🗑️',\n title: 'Wastebasket',\n },\n {\n emoji: '🔒',\n title: 'Locked',\n },\n {\n emoji: '🔓',\n title: 'Unlocked',\n },\n {\n emoji: '🔏',\n title: 'Locked with Pen',\n },\n {\n emoji: '🔐',\n title: 'Locked with Key',\n },\n {\n emoji: '🔑',\n title: 'Key',\n },\n {\n emoji: '🗝️',\n title: 'Old Key',\n },\n {\n emoji: '🔨',\n title: 'Hammer',\n },\n {\n emoji: '🪓',\n title: 'Axe',\n },\n {\n emoji: '⛏️',\n title: 'Pick',\n },\n {\n emoji: '⚒️',\n title: 'Hammer and Pick',\n },\n {\n emoji: '🛠️',\n title: 'Hammer and Wrench',\n },\n {\n emoji: '🗡️',\n title: 'Dagger',\n },\n {\n emoji: '⚔️',\n title: 'Crossed Swords',\n },\n {\n emoji: '🔫',\n title: 'Water Pistol',\n },\n {\n emoji: '🪃',\n title: 'Boomerang',\n },\n {\n emoji: '🛡️',\n title: 'Shield',\n },\n {\n emoji: '🪚',\n title: 'Carpentry Saw',\n },\n {\n emoji: '🔧',\n title: 'Wrench',\n },\n {\n emoji: '🪛',\n title: 'Screwdriver',\n },\n {\n emoji: '🔩',\n title: 'Nut and Bolt',\n },\n {\n emoji: '⚙️',\n title: 'Gear',\n },\n {\n emoji: '🗜️',\n title: 'Clamp',\n },\n {\n emoji: '⚖️',\n title: 'Balance Scale',\n },\n {\n emoji: '🦯',\n title: 'White Cane',\n },\n {\n emoji: '🔗',\n title: 'Link',\n },\n {\n emoji: '⛓️',\n title: 'Chains',\n },\n {\n emoji: '🪝',\n title: 'Hook',\n },\n {\n emoji: '🧰',\n title: 'Toolbox',\n },\n {\n emoji: '🧲',\n title: 'Magnet',\n },\n {\n emoji: '🪜',\n title: 'Ladder',\n },\n {\n emoji: '⚗️',\n title: 'Alembic',\n },\n {\n emoji: '🧪',\n title: 'Test Tube',\n },\n {\n emoji: '🧫',\n title: 'Petri Dish',\n },\n {\n emoji: '🧬',\n title: 'DNA',\n },\n {\n emoji: '🔬',\n title: 'Microscope',\n },\n {\n emoji: '🔭',\n title: 'Telescope',\n },\n {\n emoji: '📡',\n title: 'Satellite Antenna',\n },\n {\n emoji: '💉',\n title: 'Syringe',\n },\n {\n emoji: '🩸',\n title: 'Drop of Blood',\n },\n {\n emoji: '💊',\n title: 'Pill',\n },\n {\n emoji: '🩹',\n title: 'Adhesive Bandage',\n },\n {\n emoji: '🩺',\n title: 'Stethoscope',\n },\n {\n emoji: '🚪',\n title: 'Door',\n },\n {\n emoji: '🪞',\n title: 'Mirror',\n },\n {\n emoji: '🪟',\n title: 'Window',\n },\n {\n emoji: '🛏️',\n title: 'Bed',\n },\n {\n emoji: '🛋️',\n title: 'Couch and Lamp',\n },\n {\n emoji: '🪑',\n title: 'Chair',\n },\n {\n emoji: '🚽',\n title: 'Toilet',\n },\n {\n emoji: '🪠',\n title: 'Plunger',\n },\n {\n emoji: '🚿',\n title: 'Shower',\n },\n {\n emoji: '🛁',\n title: 'Bathtub',\n },\n {\n emoji: '🪤',\n title: 'Mouse Trap',\n },\n {\n emoji: '🪒',\n title: 'Razor',\n },\n {\n emoji: '🧴',\n title: 'Lotion Bottle',\n },\n {\n emoji: '🧷',\n title: 'Safety Pin',\n },\n {\n emoji: '🧹',\n title: 'Broom',\n },\n {\n emoji: '🧺',\n title: 'Basket',\n },\n {\n emoji: '🧻',\n title: 'Roll of Paper',\n },\n {\n emoji: '🪣',\n title: 'Bucket',\n },\n {\n emoji: '🧼',\n title: 'Soap',\n },\n {\n emoji: '🪥',\n title: 'Toothbrush',\n },\n {\n emoji: '🧽',\n title: 'Sponge',\n },\n {\n emoji: '🧯',\n title: 'Fire Extinguisher',\n },\n {\n emoji: '🛒',\n title: 'Shopping Cart',\n },\n {\n emoji: '🚬',\n title: 'Cigarette',\n },\n {\n emoji: '⚰️',\n title: 'Coffin',\n },\n {\n emoji: '🪦',\n title: 'Headstone',\n },\n {\n emoji: '⚱️',\n title: 'Funeral Urn',\n },\n {\n emoji: '🗿',\n title: 'Moai',\n },\n {\n emoji: '🪧',\n title: 'Placard',\n },\n {\n emoji: '🚰',\n title: 'Potable Water',\n },\n ],\n Symbols: [\n {\n emoji: '💘',\n title: 'Heart with Arrow',\n },\n {\n emoji: '💝',\n title: 'Heart with Ribbon',\n },\n {\n emoji: '💖',\n title: 'Sparkling Heart',\n },\n {\n emoji: '💗',\n title: 'Growing Heart',\n },\n {\n emoji: '💓',\n title: 'Beating Heart',\n },\n {\n emoji: '💞',\n title: 'Revolving Hearts',\n },\n {\n emoji: '💕',\n title: 'Two Hearts',\n },\n {\n emoji: '💟',\n title: 'Heart Decoration',\n },\n {\n emoji: '❣️',\n title: 'Heart Exclamation',\n },\n {\n emoji: '💔',\n title: 'Broken Heart',\n },\n {\n emoji: '❤️‍🔥',\n title: 'Heart on Fire',\n },\n {\n emoji: '❤️‍🩹',\n title: 'Mending Heart',\n },\n {\n emoji: '❤️',\n title: 'Red Heart',\n },\n {\n emoji: '🧡',\n title: 'Orange Heart',\n },\n {\n emoji: '💛',\n title: 'Yellow Heart',\n },\n {\n emoji: '💚',\n title: 'Green Heart',\n },\n {\n emoji: '💙',\n title: 'Blue Heart',\n },\n {\n emoji: '💜',\n title: 'Purple Heart',\n },\n {\n emoji: '🤎',\n title: 'Brown Heart',\n },\n {\n emoji: '🖤',\n title: 'Black Heart',\n },\n {\n emoji: '🤍',\n title: 'White Heart',\n },\n {\n emoji: '💯',\n title: 'Hundred Points',\n },\n {\n emoji: '💢',\n title: 'Anger Symbol',\n },\n {\n emoji: '💬',\n title: 'Speech Balloon',\n },\n {\n emoji: '👁️‍🗨️',\n title: 'Eye in Speech Bubble',\n },\n {\n emoji: '🗨️',\n title: 'Left Speech Bubble',\n },\n {\n emoji: '🗯️',\n title: 'Right Anger Bubble',\n },\n {\n emoji: '💭',\n title: 'Thought Balloon',\n },\n {\n emoji: '💤',\n title: 'Zzz',\n },\n {\n emoji: '💮',\n title: 'White Flower',\n },\n {\n emoji: '♨️',\n title: 'Hot Springs',\n },\n {\n emoji: '💈',\n title: 'Barber Pole',\n },\n {\n emoji: '🛑',\n title: 'Stop Sign',\n },\n {\n emoji: '🕛',\n title: 'Twelve O’Clock',\n },\n {\n emoji: '🕧',\n title: 'Twelve-Thirty',\n },\n {\n emoji: '🕐',\n title: 'One O’Clock',\n },\n {\n emoji: '🕜',\n title: 'One-Thirty',\n },\n {\n emoji: '🕑',\n title: 'Two O’Clock',\n },\n {\n emoji: '🕝',\n title: 'Two-Thirty',\n },\n {\n emoji: '🕒',\n title: 'Three O’Clock',\n },\n {\n emoji: '🕞',\n title: 'Three-Thirty',\n },\n {\n emoji: '🕓',\n title: 'Four O’Clock',\n },\n {\n emoji: '🕟',\n title: 'Four-Thirty',\n },\n {\n emoji: '🕔',\n title: 'Five O’Clock',\n },\n {\n emoji: '🕠',\n title: 'Five-Thirty',\n },\n {\n emoji: '🕕',\n title: 'Six O’Clock',\n },\n {\n emoji: '🕡',\n title: 'Six-Thirty',\n },\n {\n emoji: '🕖',\n title: 'Seven O’Clock',\n },\n {\n emoji: '🕢',\n title: 'Seven-Thirty',\n },\n {\n emoji: '🕗',\n title: 'Eight O’Clock',\n },\n {\n emoji: '🕣',\n title: 'Eight-Thirty',\n },\n {\n emoji: '🕘',\n title: 'Nine O’Clock',\n },\n {\n emoji: '🕤',\n title: 'Nine-Thirty',\n },\n {\n emoji: '🕙',\n title: 'Ten O’Clock',\n },\n {\n emoji: '🕥',\n title: 'Ten-Thirty',\n },\n {\n emoji: '🕚',\n title: 'Eleven O’Clock',\n },\n {\n emoji: '🕦',\n title: 'Eleven-Thirty',\n },\n {\n emoji: '🌀',\n title: 'Cyclone',\n },\n {\n emoji: '♠️',\n title: 'Spade Suit',\n },\n {\n emoji: '♥️',\n title: 'Heart Suit',\n },\n {\n emoji: '♦️',\n title: 'Diamond Suit',\n },\n {\n emoji: '♣️',\n title: 'Club Suit',\n },\n {\n emoji: '🃏',\n title: 'Joker',\n },\n {\n emoji: '🀄',\n title: 'Mahjong Red Dragon',\n },\n {\n emoji: '🎴',\n title: 'Flower Playing Cards',\n },\n {\n emoji: '🔇',\n title: 'Muted Speaker',\n },\n {\n emoji: '🔈',\n title: 'Speaker Low Volume',\n },\n {\n emoji: '🔉',\n title: 'Speaker Medium Volume',\n },\n {\n emoji: '🔊',\n title: 'Speaker High Volume',\n },\n {\n emoji: '📢',\n title: 'Loudspeaker',\n },\n {\n emoji: '📣',\n title: 'Megaphone',\n },\n {\n emoji: '📯',\n title: 'Postal Horn',\n },\n {\n emoji: '🔔',\n title: 'Bell',\n },\n {\n emoji: '🔕',\n title: 'Bell with Slash',\n },\n {\n emoji: '🎵',\n title: 'Musical Note',\n },\n {\n emoji: '🎶',\n title: 'Musical Notes',\n },\n {\n emoji: '💹',\n title: 'Chart Increasing with Yen',\n },\n {\n emoji: '🛗',\n title: 'Elevator',\n },\n {\n emoji: '🏧',\n title: 'ATM Sign',\n },\n {\n emoji: '🚮',\n title: 'Litter in Bin Sign',\n },\n {\n emoji: '🚰',\n title: 'Potable Water',\n },\n {\n emoji: '♿',\n title: 'Wheelchair Symbol',\n },\n {\n emoji: '🚹',\n title: 'Men’s Room',\n },\n {\n emoji: '🚺',\n title: 'Women’s Room',\n },\n {\n emoji: '🚻',\n title: 'Restroom',\n },\n {\n emoji: '🚼',\n title: 'Baby Symbol',\n },\n {\n emoji: '🚾',\n title: 'Water Closet',\n },\n {\n emoji: '⚠️',\n title: 'Warning',\n },\n {\n emoji: '🚸',\n title: 'Children Crossing',\n },\n {\n emoji: '⛔',\n title: 'No Entry',\n },\n {\n emoji: '🚫',\n title: 'Prohibited',\n },\n {\n emoji: '🚳',\n title: 'No Bicycles',\n },\n {\n emoji: '🚭',\n title: 'No Smoking',\n },\n {\n emoji: '🚯',\n title: 'No Littering',\n },\n {\n emoji: '🚱',\n title: 'Non-Potable Water',\n },\n {\n emoji: '🚷',\n title: 'No Pedestrians',\n },\n {\n emoji: '📵',\n title: 'No Mobile Phones',\n },\n {\n emoji: '🔞',\n title: 'No One Under Eighteen',\n },\n {\n emoji: '☢️',\n title: 'Radioactive',\n },\n {\n emoji: '☣️',\n title: 'Biohazard',\n },\n {\n emoji: '⬆️',\n title: 'Up Arrow',\n },\n {\n emoji: '↗️',\n title: 'Up-Right Arrow',\n },\n {\n emoji: '➡️',\n title: 'Right Arrow',\n },\n {\n emoji: '↘️',\n title: 'Down-Right Arrow',\n },\n {\n emoji: '⬇️',\n title: 'Down Arrow',\n },\n {\n emoji: '↙️',\n title: 'Down-Left Arrow',\n },\n {\n emoji: '⬅️',\n title: 'Left Arrow',\n },\n {\n emoji: '↖️',\n title: 'Up-Left Arrow',\n },\n {\n emoji: '↕️',\n title: 'Up-Down Arrow',\n },\n {\n emoji: '↔️',\n title: 'Left-Right Arrow',\n },\n {\n emoji: '↩️',\n title: 'Right Arrow Curving Left',\n },\n {\n emoji: '↪️',\n title: 'Left Arrow Curving Right',\n },\n {\n emoji: '⤴️',\n title: 'Right Arrow Curving Up',\n },\n {\n emoji: '⤵️',\n title: 'Right Arrow Curving Down',\n },\n {\n emoji: '🔃',\n title: 'Clockwise Vertical Arrows',\n },\n {\n emoji: '🔄',\n title: 'Counterclockwise Arrows Button',\n },\n {\n emoji: '🔙',\n title: 'Back Arrow',\n },\n {\n emoji: '🔚',\n title: 'End Arrow',\n },\n {\n emoji: '🔛',\n title: 'On! Arrow',\n },\n {\n emoji: '🔜',\n title: 'Soon Arrow',\n },\n {\n emoji: '🔝',\n title: 'Top Arrow',\n },\n {\n emoji: '🛐',\n title: 'Place of Worship',\n },\n {\n emoji: '⚛️',\n title: 'Atom Symbol',\n },\n {\n emoji: '🕉️',\n title: 'Om',\n },\n {\n emoji: '✡️',\n title: 'Star of David',\n },\n {\n emoji: '☸️',\n title: 'Wheel of Dharma',\n },\n {\n emoji: '☯️',\n title: 'Yin Yang',\n },\n {\n emoji: '✝️',\n title: 'Latin Cross',\n },\n {\n emoji: '☦️',\n title: 'Orthodox Cross',\n },\n {\n emoji: '☪️',\n title: 'Star and Crescent',\n },\n {\n emoji: '☮️',\n title: 'Peace Symbol',\n },\n {\n emoji: '🕎',\n title: 'Menorah',\n },\n {\n emoji: '🔯',\n title: 'Dotted Six-Pointed Star',\n },\n {\n emoji: '♈',\n title: 'Aries',\n },\n {\n emoji: '♉',\n title: 'Taurus',\n },\n {\n emoji: '♊',\n title: 'Gemini',\n },\n {\n emoji: '♋',\n title: 'Cancer',\n },\n {\n emoji: '♌',\n title: 'Leo',\n },\n {\n emoji: '♍',\n title: 'Virgo',\n },\n {\n emoji: '♎',\n title: 'Libra',\n },\n {\n emoji: '♏',\n title: 'Scorpio',\n },\n {\n emoji: '♐',\n title: 'Sagittarius',\n },\n {\n emoji: '♑',\n title: 'Capricorn',\n },\n {\n emoji: '♒',\n title: 'Aquarius',\n },\n {\n emoji: '♓',\n title: 'Pisces',\n },\n {\n emoji: '⛎',\n title: 'Ophiuchus',\n },\n {\n emoji: '🔀',\n title: 'Shuffle Tracks Button',\n },\n {\n emoji: '🔁',\n title: 'Repeat Button',\n },\n {\n emoji: '🔂',\n title: 'Repeat Single Button',\n },\n {\n emoji: '▶️',\n title: 'Play Button',\n },\n {\n emoji: '⏩',\n title: 'Fast-Forward Button',\n },\n {\n emoji: '⏭️',\n title: 'Next Track Button',\n },\n {\n emoji: '⏯️',\n title: 'Play or Pause Button',\n },\n {\n emoji: '◀️',\n title: 'Reverse Button',\n },\n {\n emoji: '⏪',\n title: 'Fast Reverse Button',\n },\n {\n emoji: '⏮️',\n title: 'Last Track Button',\n },\n {\n emoji: '🔼',\n title: 'Upwards Button',\n },\n {\n emoji: '⏫',\n title: 'Fast Up Button',\n },\n {\n emoji: '🔽',\n title: 'Downwards Button',\n },\n {\n emoji: '⏬',\n title: 'Fast Down Button',\n },\n {\n emoji: '⏸️',\n title: 'Pause Button',\n },\n {\n emoji: '⏹️',\n title: 'Stop Button',\n },\n {\n emoji: '⏺️',\n title: 'Record Button',\n },\n {\n emoji: '⏏️',\n title: 'Eject Button',\n },\n {\n emoji: '🎦',\n title: 'Cinema',\n },\n {\n emoji: '🔅',\n title: 'Dim Button',\n },\n {\n emoji: '🔆',\n title: 'Bright Button',\n },\n {\n emoji: '📶',\n title: 'Antenna Bars',\n },\n {\n emoji: '📳',\n title: 'Vibration Mode',\n },\n {\n emoji: '📴',\n title: 'Mobile Phone Off',\n },\n {\n emoji: '♀️',\n title: 'Female Sign',\n },\n {\n emoji: '♂️',\n title: 'Male Sign',\n },\n {\n emoji: '✖️',\n title: 'Multiply',\n },\n {\n emoji: '➕',\n title: 'Plus',\n },\n {\n emoji: '➖',\n title: 'Minus',\n },\n {\n emoji: '➗',\n title: 'Divide',\n },\n {\n emoji: '♾️',\n title: 'Infinity',\n },\n {\n emoji: '‼️',\n title: '‼ Double Exclamation Mark',\n },\n {\n emoji: '⁉️',\n title: '⁉ Exclamation Question Mark',\n },\n {\n emoji: '❓',\n title: 'Red Question Mark',\n },\n {\n emoji: '❔',\n title: 'White Question Mark',\n },\n {\n emoji: '❕',\n title: 'White Exclamation Mark',\n },\n {\n emoji: '❗',\n title: 'Red Exclamation Mark',\n },\n {\n emoji: '〰️',\n title: '〰 Wavy Dash',\n },\n {\n emoji: '💱',\n title: 'Currency Exchange',\n },\n {\n emoji: '💲',\n title: 'Heavy Dollar Sign',\n },\n {\n emoji: '⚕️',\n title: 'Medical Symbol',\n },\n {\n emoji: '♻️',\n title: 'Recycling Symbol',\n },\n {\n emoji: '⚜️',\n title: 'Fleur-de-lis',\n },\n {\n emoji: '🔱',\n title: 'Trident Emblem',\n },\n {\n emoji: '📛',\n title: 'Name Badge',\n },\n {\n emoji: '🔰',\n title: 'Japanese Symbol for Beginner',\n },\n {\n emoji: '⭕',\n title: 'Hollow Red Circle',\n },\n {\n emoji: '✅',\n title: 'Check Mark Button',\n },\n {\n emoji: '☑️',\n title: 'Check Box with Check',\n },\n {\n emoji: '✔️',\n title: 'Check Mark',\n },\n {\n emoji: '❌',\n title: 'Cross Mark',\n },\n {\n emoji: '❎',\n title: 'Cross Mark Button',\n },\n {\n emoji: '➰',\n title: 'Curly Loop',\n },\n {\n emoji: '➿',\n title: 'Double Curly Loop',\n },\n {\n emoji: '〽️',\n title: '〽 Part Alternation Mark',\n },\n {\n emoji: '✳️',\n title: 'Eight-Spoked Asterisk',\n },\n {\n emoji: '✴️',\n title: 'Eight-Pointed Star',\n },\n {\n emoji: '❇️',\n title: 'Sparkle',\n },\n {\n emoji: '©️',\n title: 'Copyright',\n },\n {\n emoji: '®️',\n title: 'Registered',\n },\n {\n emoji: '™️',\n title: 'Trade Mark',\n },\n {\n emoji: '#️⃣',\n title: '# Keycap Number Sign',\n },\n {\n emoji: '*️⃣',\n title: '* Keycap Asterisk',\n },\n {\n emoji: '0️⃣',\n title: '0 Keycap Digit Zero',\n },\n {\n emoji: '1️⃣',\n title: '1 Keycap Digit One',\n },\n {\n emoji: '2️⃣',\n title: '2 Keycap Digit Two',\n },\n {\n emoji: '3️⃣',\n title: '3 Keycap Digit Three',\n },\n {\n emoji: '4️⃣',\n title: '4 Keycap Digit Four',\n },\n {\n emoji: '5️⃣',\n title: '5 Keycap Digit Five',\n },\n {\n emoji: '6️⃣',\n title: '6 Keycap Digit Six',\n },\n {\n emoji: '7️⃣',\n title: '7 Keycap Digit Seven',\n },\n {\n emoji: '8️⃣',\n title: '8 Keycap Digit Eight',\n },\n {\n emoji: '9️⃣',\n title: '9 Keycap Digit Nine',\n },\n {\n emoji: '🔟',\n title: 'Keycap: 10',\n },\n {\n emoji: '🔠',\n title: 'Input Latin Uppercase',\n },\n {\n emoji: '🔡',\n title: 'Input Latin Lowercase',\n },\n {\n emoji: '🔢',\n title: 'Input Numbers',\n },\n {\n emoji: '🔣',\n title: 'Input Symbols',\n },\n {\n emoji: '🔤',\n title: 'Input Latin Letters',\n },\n {\n emoji: '🅰️',\n title: 'A Button (Blood Type)',\n },\n {\n emoji: '🆎',\n title: 'AB Button (Blood Type)',\n },\n {\n emoji: '🅱️',\n title: 'B Button (Blood Type)',\n },\n {\n emoji: '🆑',\n title: 'CL Button',\n },\n {\n emoji: '🆒',\n title: 'Cool Button',\n },\n {\n emoji: '🆓',\n title: 'Free Button',\n },\n {\n emoji: 'ℹ️',\n title: 'ℹ Information',\n },\n {\n emoji: '🆔',\n title: 'ID Button',\n },\n {\n emoji: 'Ⓜ️',\n title: 'Circled M',\n },\n {\n emoji: '🆕',\n title: 'New Button',\n },\n {\n emoji: '🆖',\n title: 'NG Button',\n },\n {\n emoji: '🅾️',\n title: 'O Button (Blood Type)',\n },\n {\n emoji: '🆗',\n title: 'OK Button',\n },\n {\n emoji: '🅿️',\n title: 'P Button',\n },\n {\n emoji: '🆘',\n title: 'SOS Button',\n },\n {\n emoji: '🆙',\n title: 'Up! Button',\n },\n {\n emoji: '🆚',\n title: 'Vs Button',\n },\n {\n emoji: '🈁',\n title: 'Japanese “Here” Button',\n },\n {\n emoji: '🈂️',\n title: 'Japanese “Service Charge” Button',\n },\n {\n emoji: '🈷️',\n title: 'Japanese “Monthly Amount” Button',\n },\n {\n emoji: '🈶',\n title: 'Japanese “Not Free of Charge” Button',\n },\n {\n emoji: '🈯',\n title: 'Japanese “Reserved” Button',\n },\n {\n emoji: '🉐',\n title: 'Japanese “Bargain” Button',\n },\n {\n emoji: '🈹',\n title: 'Japanese “Discount” Button',\n },\n {\n emoji: '🈚',\n title: 'Japanese “Free of Charge” Button',\n },\n {\n emoji: '🈲',\n title: 'Japanese “Prohibited” Button',\n },\n {\n emoji: '🉑',\n title: 'Japanese “Acceptable” Button',\n },\n {\n emoji: '🈸',\n title: 'Japanese “Application” Button',\n },\n {\n emoji: '🈴',\n title: 'Japanese “Passing Grade” Button',\n },\n {\n emoji: '🈳',\n title: 'Japanese “Vacancy” Button',\n },\n {\n emoji: '㊗️',\n title: 'Japanese “Congratulations” Button',\n },\n {\n emoji: '㊙️',\n title: 'Japanese “Secret” Button',\n },\n {\n emoji: '🈺',\n title: 'Japanese “Open for Business” Button',\n },\n {\n emoji: '🈵',\n title: 'Japanese “No Vacancy” Button',\n },\n {\n emoji: '🔴',\n title: 'Red Circle',\n },\n {\n emoji: '🟠',\n title: 'Orange Circle',\n },\n {\n emoji: '🟡',\n title: 'Yellow Circle',\n },\n {\n emoji: '🟢',\n title: 'Green Circle',\n },\n {\n emoji: '🔵',\n title: 'Blue Circle',\n },\n {\n emoji: '🟣',\n title: 'Purple Circle',\n },\n {\n emoji: '🟤',\n title: 'Brown Circle',\n },\n {\n emoji: '⚫',\n title: 'Black Circle',\n },\n {\n emoji: '⚪',\n title: 'White Circle',\n },\n {\n emoji: '🟥',\n title: 'Red Square',\n },\n {\n emoji: '🟧',\n title: 'Orange Square',\n },\n {\n emoji: '🟨',\n title: 'Yellow Square',\n },\n {\n emoji: '🟩',\n title: 'Green Square',\n },\n {\n emoji: '🟦',\n title: 'Blue Square',\n },\n {\n emoji: '🟪',\n title: 'Purple Square',\n },\n {\n emoji: '🟫',\n title: 'Brown Square',\n },\n {\n emoji: '⬛',\n title: 'Black Large Square',\n },\n {\n emoji: '⬜',\n title: 'White Large Square',\n },\n {\n emoji: '◼️',\n title: 'Black Medium Square',\n },\n {\n emoji: '◻️',\n title: 'White Medium Square',\n },\n {\n emoji: '◾',\n title: 'Black Medium-Small Square',\n },\n {\n emoji: '◽',\n title: 'White Medium-Small Square',\n },\n {\n emoji: '▪️',\n title: 'Black Small Square',\n },\n {\n emoji: '▫️',\n title: 'White Small Square',\n },\n {\n emoji: '🔶',\n title: 'Large Orange Diamond',\n },\n {\n emoji: '🔷',\n title: 'Large Blue Diamond',\n },\n {\n emoji: '🔸',\n title: 'Small Orange Diamond',\n },\n {\n emoji: '🔹',\n title: 'Small Blue Diamond',\n },\n {\n emoji: '🔺',\n title: 'Red Triangle Pointed Up',\n },\n {\n emoji: '🔻',\n title: 'Red Triangle Pointed Down',\n },\n {\n emoji: '💠',\n title: 'Diamond with a Dot',\n },\n {\n emoji: '🔘',\n title: 'Radio Button',\n },\n {\n emoji: '🔳',\n title: 'White Square Button',\n },\n {\n emoji: '🔲',\n title: 'Black Square Button',\n },\n ],\n Flags: [\n {\n emoji: '🏁',\n title: 'Chequered Flag',\n },\n {\n emoji: '🚩',\n title: 'Triangular Flag',\n },\n {\n emoji: '🎌',\n title: 'Crossed Flags',\n },\n {\n emoji: '🏴',\n title: 'Black Flag',\n },\n {\n emoji: '🏳️',\n title: 'White Flag',\n },\n {\n emoji: '🏳️‍🌈',\n title: 'Rainbow Flag',\n },\n {\n emoji: '🏳️‍⚧️',\n title: 'Transgender Flag',\n },\n {\n emoji: '🏴‍☠️',\n title: 'Pirate Flag',\n },\n {\n emoji: '🇦🇨',\n title: 'Flag: Ascension Island',\n },\n {\n emoji: '🇦🇩',\n title: 'Flag: Andorra',\n },\n {\n emoji: '🇦🇪',\n title: 'Flag: United Arab Emirates',\n },\n {\n emoji: '🇦🇫',\n title: 'Flag: Afghanistan',\n },\n {\n emoji: '🇦🇬',\n title: 'Flag: Antigua & Barbuda',\n },\n {\n emoji: '🇦🇮',\n title: 'Flag: Anguilla',\n },\n {\n emoji: '🇦🇱',\n title: 'Flag: Albania',\n },\n {\n emoji: '🇦🇲',\n title: 'Flag: Armenia',\n },\n {\n emoji: '🇦🇴',\n title: 'Flag: Angola',\n },\n {\n emoji: '🇦🇶',\n title: 'Flag: Antarctica',\n },\n {\n emoji: '🇦🇷',\n title: 'Flag: Argentina',\n },\n {\n emoji: '🇦🇸',\n title: 'Flag: American Samoa',\n },\n {\n emoji: '🇦🇹',\n title: 'Flag: Austria',\n },\n {\n emoji: '🇦🇺',\n title: 'Flag: Australia',\n },\n {\n emoji: '🇦🇼',\n title: 'Flag: Aruba',\n },\n {\n emoji: '🇦🇽',\n title: 'Flag: Åland Islands',\n },\n {\n emoji: '🇦🇿',\n title: 'Flag: Azerbaijan',\n },\n {\n emoji: '🇧🇦',\n title: 'Flag: Bosnia & Herzegovina',\n },\n {\n emoji: '🇧🇧',\n title: 'Flag: Barbados',\n },\n {\n emoji: '🇧🇩',\n title: 'Flag: Bangladesh',\n },\n {\n emoji: '🇧🇪',\n title: 'Flag: Belgium',\n },\n {\n emoji: '🇧🇫',\n title: 'Flag: Burkina Faso',\n },\n {\n emoji: '🇧🇬',\n title: 'Flag: Bulgaria',\n },\n {\n emoji: '🇧🇭',\n title: 'Flag: Bahrain',\n },\n {\n emoji: '🇧🇮',\n title: 'Flag: Burundi',\n },\n {\n emoji: '🇧🇯',\n title: 'Flag: Benin',\n },\n {\n emoji: '🇧🇱',\n title: 'Flag: St. Barthélemy',\n },\n {\n emoji: '🇧🇲',\n title: 'Flag: Bermuda',\n },\n {\n emoji: '🇧🇳',\n title: 'Flag: Brunei',\n },\n {\n emoji: '🇧🇴',\n title: 'Flag: Bolivia',\n },\n {\n emoji: '🇧🇶',\n title: 'Flag: Caribbean Netherlands',\n },\n {\n emoji: '🇧🇷',\n title: 'Flag: Brazil',\n },\n {\n emoji: '🇧🇸',\n title: 'Flag: Bahamas',\n },\n {\n emoji: '🇧🇹',\n title: 'Flag: Bhutan',\n },\n {\n emoji: '🇧🇻',\n title: 'Flag: Bouvet Island',\n },\n {\n emoji: '🇧🇼',\n title: 'Flag: Botswana',\n },\n {\n emoji: '🇧🇾',\n title: 'Flag: Belarus',\n },\n {\n emoji: '🇧🇿',\n title: 'Flag: Belize',\n },\n {\n emoji: '🇨🇦',\n title: 'Flag: Canada',\n },\n {\n emoji: '🇨🇨',\n title: 'Flag: Cocos (Keeling) Islands',\n },\n {\n emoji: '🇨🇩',\n title: 'Flag: Congo - Kinshasa',\n },\n {\n emoji: '🇨🇫',\n title: 'Flag: Central African Republic',\n },\n {\n emoji: '🇨🇬',\n title: 'Flag: Congo - Brazzaville',\n },\n {\n emoji: '🇨🇭',\n title: 'Flag: Switzerland',\n },\n {\n emoji: '🇨🇮',\n title: 'Flag: Côte d’Ivoire',\n },\n {\n emoji: '🇨🇰',\n title: 'Flag: Cook Islands',\n },\n {\n emoji: '🇨🇱',\n title: 'Flag: Chile',\n },\n {\n emoji: '🇨🇲',\n title: 'Flag: Cameroon',\n },\n {\n emoji: '🇨🇳',\n title: 'Flag: China',\n },\n {\n emoji: '🇨🇴',\n title: 'Flag: Colombia',\n },\n {\n emoji: '🇨🇵',\n title: 'Flag: Clipperton Island',\n },\n {\n emoji: '🇨🇷',\n title: 'Flag: Costa Rica',\n },\n {\n emoji: '🇨🇺',\n title: 'Flag: Cuba',\n },\n {\n emoji: '🇨🇻',\n title: 'Flag: Cape Verde',\n },\n {\n emoji: '🇨🇼',\n title: 'Flag: Curaçao',\n },\n {\n emoji: '🇨🇽',\n title: 'Flag: Christmas Island',\n },\n {\n emoji: '🇨🇾',\n title: 'Flag: Cyprus',\n },\n {\n emoji: '🇨🇿',\n title: 'Flag: Czechia',\n },\n {\n emoji: '🇩🇪',\n title: 'Flag: Germany',\n },\n {\n emoji: '🇩🇬',\n title: 'Flag: Diego Garcia',\n },\n {\n emoji: '🇩🇯',\n title: 'Flag: Djibouti',\n },\n {\n emoji: '🇩🇰',\n title: 'Flag: Denmark',\n },\n {\n emoji: '🇩🇲',\n title: 'Flag: Dominica',\n },\n {\n emoji: '🇩🇴',\n title: 'Flag: Dominican Republic',\n },\n {\n emoji: '🇩🇿',\n title: 'Flag: Algeria',\n },\n {\n emoji: '🇪🇦',\n title: 'Flag: Ceuta & Melilla',\n },\n {\n emoji: '🇪🇨',\n title: 'Flag: Ecuador',\n },\n {\n emoji: '🇪🇪',\n title: 'Flag: Estonia',\n },\n {\n emoji: '🇪🇬',\n title: 'Flag: Egypt',\n },\n {\n emoji: '🇪🇭',\n title: 'Flag: Western Sahara',\n },\n {\n emoji: '🇪🇷',\n title: 'Flag: Eritrea',\n },\n {\n emoji: '🇪🇸',\n title: 'Flag: Spain',\n },\n {\n emoji: '🇪🇹',\n title: 'Flag: Ethiopia',\n },\n {\n emoji: '🇪🇺',\n title: 'Flag: European Union',\n },\n {\n emoji: '🇫🇮',\n title: 'Flag: Finland',\n },\n {\n emoji: '🇫🇯',\n title: 'Flag: Fiji',\n },\n {\n emoji: '🇫🇰',\n title: 'Flag: Falkland Islands',\n },\n {\n emoji: '🇫🇲',\n title: 'Flag: Micronesia',\n },\n {\n emoji: '🇫🇴',\n title: 'Flag: Faroe Islands',\n },\n {\n emoji: '🇫🇷',\n title: 'Flag: France',\n },\n {\n emoji: '🇬🇦',\n title: 'Flag: Gabon',\n },\n {\n emoji: '🇬🇧',\n title: 'Flag: United Kingdom',\n },\n {\n emoji: '🇬🇩',\n title: 'Flag: Grenada',\n },\n {\n emoji: '🇬🇪',\n title: 'Flag: Georgia',\n },\n {\n emoji: '🇬🇫',\n title: 'Flag: French Guiana',\n },\n {\n emoji: '🇬🇬',\n title: 'Flag: Guernsey',\n },\n {\n emoji: '🇬🇭',\n title: 'Flag: Ghana',\n },\n {\n emoji: '🇬🇮',\n title: 'Flag: Gibraltar',\n },\n {\n emoji: '🇬🇱',\n title: 'Flag: Greenland',\n },\n {\n emoji: '🇬🇲',\n title: 'Flag: Gambia',\n },\n {\n emoji: '🇬🇳',\n title: 'Flag: Guinea',\n },\n {\n emoji: '🇬🇵',\n title: 'Flag: Guadeloupe',\n },\n {\n emoji: '🇬🇶',\n title: 'Flag: Equatorial Guinea',\n },\n {\n emoji: '🇬🇷',\n title: 'Flag: Greece',\n },\n {\n emoji: '🇬🇸',\n title: 'Flag: South Georgia & South Sandwich Islands',\n },\n {\n emoji: '🇬🇹',\n title: 'Flag: Guatemala',\n },\n {\n emoji: '🇬🇺',\n title: 'Flag: Guam',\n },\n {\n emoji: '🇬🇼',\n title: 'Flag: Guinea-Bissau',\n },\n {\n emoji: '🇬🇾',\n title: 'Flag: Guyana',\n },\n {\n emoji: '🇭🇰',\n title: 'Flag: Hong Kong SAR China',\n },\n {\n emoji: '🇭🇲',\n title: 'Flag: Heard & McDonald Islands',\n },\n {\n emoji: '🇭🇳',\n title: 'Flag: Honduras',\n },\n {\n emoji: '🇭🇷',\n title: 'Flag: Croatia',\n },\n {\n emoji: '🇭🇹',\n title: 'Flag: Haiti',\n },\n {\n emoji: '🇭🇺',\n title: 'Flag: Hungary',\n },\n {\n emoji: '🇮🇨',\n title: 'Flag: Canary Islands',\n },\n {\n emoji: '🇮🇩',\n title: 'Flag: Indonesia',\n },\n {\n emoji: '🇮🇪',\n title: 'Flag: Ireland',\n },\n {\n emoji: '🇮🇱',\n title: 'Flag: Israel',\n },\n {\n emoji: '🇮🇲',\n title: 'Flag: Isle of Man',\n },\n {\n emoji: '🇮🇳',\n title: 'Flag: India',\n },\n {\n emoji: '🇮🇴',\n title: 'Flag: British Indian Ocean Territory',\n },\n {\n emoji: '🇮🇶',\n title: 'Flag: Iraq',\n },\n {\n emoji: '🇮🇷',\n title: 'Flag: Iran',\n },\n {\n emoji: '🇮🇸',\n title: 'Flag: Iceland',\n },\n {\n emoji: '🇮🇹',\n title: 'Flag: Italy',\n },\n {\n emoji: '🇯🇪',\n title: 'Flag: Jersey',\n },\n {\n emoji: '🇯🇲',\n title: 'Flag: Jamaica',\n },\n {\n emoji: '🇯🇴',\n title: 'Flag: Jordan',\n },\n {\n emoji: '🇯🇵',\n title: 'Flag: Japan',\n },\n {\n emoji: '🇰🇪',\n title: 'Flag: Kenya',\n },\n {\n emoji: '🇰🇬',\n title: 'Flag: Kyrgyzstan',\n },\n {\n emoji: '🇰🇭',\n title: 'Flag: Cambodia',\n },\n {\n emoji: '🇰🇮',\n title: 'Flag: Kiribati',\n },\n {\n emoji: '🇰🇲',\n title: 'Flag: Comoros',\n },\n {\n emoji: '🇰🇳',\n title: 'Flag: St. Kitts & Nevis',\n },\n {\n emoji: '🇰🇵',\n title: 'Flag: North Korea',\n },\n {\n emoji: '🇰🇷',\n title: 'Flag: South Korea',\n },\n {\n emoji: '🇰🇼',\n title: 'Flag: Kuwait',\n },\n {\n emoji: '🇰🇾',\n title: 'Flag: Cayman Islands',\n },\n {\n emoji: '🇰🇿',\n title: 'Flag: Kazakhstan',\n },\n {\n emoji: '🇱🇦',\n title: 'Flag: Laos',\n },\n {\n emoji: '🇱🇧',\n title: 'Flag: Lebanon',\n },\n {\n emoji: '🇱🇨',\n title: 'Flag: St. Lucia',\n },\n {\n emoji: '🇱🇮',\n title: 'Flag: Liechtenstein',\n },\n {\n emoji: '🇱🇰',\n title: 'Flag: Sri Lanka',\n },\n {\n emoji: '🇱🇷',\n title: 'Flag: Liberia',\n },\n {\n emoji: '🇱🇸',\n title: 'Flag: Lesotho',\n },\n {\n emoji: '🇱🇹',\n title: 'Flag: Lithuania',\n },\n {\n emoji: '🇱🇺',\n title: 'Flag: Luxembourg',\n },\n {\n emoji: '🇱🇻',\n title: 'Flag: Latvia',\n },\n {\n emoji: '🇱🇾',\n title: 'Flag: Libya',\n },\n {\n emoji: '🇲🇦',\n title: 'Flag: Morocco',\n },\n {\n emoji: '🇲🇨',\n title: 'Flag: Monaco',\n },\n {\n emoji: '🇲🇩',\n title: 'Flag: Moldova',\n },\n {\n emoji: '🇲🇪',\n title: 'Flag: Montenegro',\n },\n {\n emoji: '🇲🇫',\n title: 'Flag: St. Martin',\n },\n {\n emoji: '🇲🇬',\n title: 'Flag: Madagascar',\n },\n {\n emoji: '🇲🇭',\n title: 'Flag: Marshall Islands',\n },\n {\n emoji: '🇲🇰',\n title: 'Flag: North Macedonia',\n },\n {\n emoji: '🇲🇱',\n title: 'Flag: Mali',\n },\n {\n emoji: '🇲🇲',\n title: 'Flag: Myanmar (Burma)',\n },\n {\n emoji: '🇲🇳',\n title: 'Flag: Mongolia',\n },\n {\n emoji: '🇲🇴',\n title: 'Flag: Macao Sar China',\n },\n {\n emoji: '🇲🇵',\n title: 'Flag: Northern Mariana Islands',\n },\n {\n emoji: '🇲🇶',\n title: 'Flag: Martinique',\n },\n {\n emoji: '🇲🇷',\n title: 'Flag: Mauritania',\n },\n {\n emoji: '🇲🇸',\n title: 'Flag: Montserrat',\n },\n {\n emoji: '🇲🇹',\n title: 'Flag: Malta',\n },\n {\n emoji: '🇲🇺',\n title: 'Flag: Mauritius',\n },\n {\n emoji: '🇲🇻',\n title: 'Flag: Maldives',\n },\n {\n emoji: '🇲🇼',\n title: 'Flag: Malawi',\n },\n {\n emoji: '🇲🇽',\n title: 'Flag: Mexico',\n },\n {\n emoji: '🇲🇾',\n title: 'Flag: Malaysia',\n },\n {\n emoji: '🇲🇿',\n title: 'Flag: Mozambique',\n },\n {\n emoji: '🇳🇦',\n title: 'Flag: Namibia',\n },\n {\n emoji: '🇳🇨',\n title: 'Flag: New Caledonia',\n },\n {\n emoji: '🇳🇪',\n title: 'Flag: Niger',\n },\n {\n emoji: '🇳🇫',\n title: 'Flag: Norfolk Island',\n },\n {\n emoji: '🇳🇬',\n title: 'Flag: Nigeria',\n },\n {\n emoji: '🇳🇮',\n title: 'Flag: Nicaragua',\n },\n {\n emoji: '🇳🇱',\n title: 'Flag: Netherlands',\n },\n {\n emoji: '🇳🇴',\n title: 'Flag: Norway',\n },\n {\n emoji: '🇳🇵',\n title: 'Flag: Nepal',\n },\n {\n emoji: '🇳🇷',\n title: 'Flag: Nauru',\n },\n {\n emoji: '🇳🇺',\n title: 'Flag: Niue',\n },\n {\n emoji: '🇳🇿',\n title: 'Flag: New Zealand',\n },\n {\n emoji: '🇴🇲',\n title: 'Flag: Oman',\n },\n {\n emoji: '🇵🇦',\n title: 'Flag: Panama',\n },\n {\n emoji: '🇵🇪',\n title: 'Flag: Peru',\n },\n {\n emoji: '🇵🇫',\n title: 'Flag: French Polynesia',\n },\n {\n emoji: '🇵🇬',\n title: 'Flag: Papua New Guinea',\n },\n {\n emoji: '🇵🇭',\n title: 'Flag: Philippines',\n },\n {\n emoji: '🇵🇰',\n title: 'Flag: Pakistan',\n },\n {\n emoji: '🇵🇱',\n title: 'Flag: Poland',\n },\n {\n emoji: '🇵🇲',\n title: 'Flag: St. Pierre & Miquelon',\n },\n {\n emoji: '🇵🇳',\n title: 'Flag: Pitcairn Islands',\n },\n {\n emoji: '🇵🇷',\n title: 'Flag: Puerto Rico',\n },\n {\n emoji: '🇵🇸',\n title: 'Flag: Palestinian Territories',\n },\n {\n emoji: '🇵🇹',\n title: 'Flag: Portugal',\n },\n {\n emoji: '🇵🇼',\n title: 'Flag: Palau',\n },\n {\n emoji: '🇵🇾',\n title: 'Flag: Paraguay',\n },\n {\n emoji: '🇶🇦',\n title: 'Flag: Qatar',\n },\n {\n emoji: '🇷🇪',\n title: 'Flag: Réunion',\n },\n {\n emoji: '🇷🇴',\n title: 'Flag: Romania',\n },\n {\n emoji: '🇷🇸',\n title: 'Flag: Serbia',\n },\n {\n emoji: '🇷🇺',\n title: 'Flag: Russia',\n },\n {\n emoji: '🇷🇼',\n title: 'Flag: Rwanda',\n },\n {\n emoji: '🇸🇦',\n title: 'Flag: Saudi Arabia',\n },\n {\n emoji: '🇸🇧',\n title: 'Flag: Solomon Islands',\n },\n {\n emoji: '🇸🇨',\n title: 'Flag: Seychelles',\n },\n {\n emoji: '🇸🇩',\n title: 'Flag: Sudan',\n },\n {\n emoji: '🇸🇪',\n title: 'Flag: Sweden',\n },\n {\n emoji: '🇸🇬',\n title: 'Flag: Singapore',\n },\n {\n emoji: '🇸🇭',\n title: 'Flag: St. Helena',\n },\n {\n emoji: '🇸🇮',\n title: 'Flag: Slovenia',\n },\n {\n emoji: '🇸🇯',\n title: 'Flag: Svalbard & Jan Mayen',\n },\n {\n emoji: '🇸🇰',\n title: 'Flag: Slovakia',\n },\n {\n emoji: '🇸🇱',\n title: 'Flag: Sierra Leone',\n },\n {\n emoji: '🇸🇲',\n title: 'Flag: San Marino',\n },\n {\n emoji: '🇸🇳',\n title: 'Flag: Senegal',\n },\n {\n emoji: '🇸🇴',\n title: 'Flag: Somalia',\n },\n {\n emoji: '🇸🇷',\n title: 'Flag: Suriname',\n },\n {\n emoji: '🇸🇸',\n title: 'Flag: South Sudan',\n },\n {\n emoji: '🇸🇹',\n title: 'Flag: São Tomé & Príncipe',\n },\n {\n emoji: '🇸🇻',\n title: 'Flag: El Salvador',\n },\n {\n emoji: '🇸🇽',\n title: 'Flag: Sint Maarten',\n },\n {\n emoji: '🇸🇾',\n title: 'Flag: Syria',\n },\n {\n emoji: '🇸🇿',\n title: 'Flag: Eswatini',\n },\n {\n emoji: '🇹🇦',\n title: 'Flag: Tristan Da Cunha',\n },\n {\n emoji: '🇹🇨',\n title: 'Flag: Turks & Caicos Islands',\n },\n {\n emoji: '🇹🇩',\n title: 'Flag: Chad',\n },\n {\n emoji: '🇹🇫',\n title: 'Flag: French Southern Territories',\n },\n {\n emoji: '🇹🇬',\n title: 'Flag: Togo',\n },\n {\n emoji: '🇹🇭',\n title: 'Flag: Thailand',\n },\n {\n emoji: '🇹🇯',\n title: 'Flag: Tajikistan',\n },\n {\n emoji: '🇹🇰',\n title: 'Flag: Tokelau',\n },\n {\n emoji: '🇹🇱',\n title: 'Flag: Timor-Leste',\n },\n {\n emoji: '🇹🇲',\n title: 'Flag: Turkmenistan',\n },\n {\n emoji: '🇹🇳',\n title: 'Flag: Tunisia',\n },\n {\n emoji: '🇹🇴',\n title: 'Flag: Tonga',\n },\n {\n emoji: '🇹🇷',\n title: 'Flag: Turkey',\n },\n {\n emoji: '🇹🇹',\n title: 'Flag: Trinidad & Tobago',\n },\n {\n emoji: '🇹🇻',\n title: 'Flag: Tuvalu',\n },\n {\n emoji: '🇹🇼',\n title: 'Flag: Taiwan',\n },\n {\n emoji: '🇹🇿',\n title: 'Flag: Tanzania',\n },\n {\n emoji: '🇺🇦',\n title: 'Flag: Ukraine',\n },\n {\n emoji: '🇺🇬',\n title: 'Flag: Uganda',\n },\n {\n emoji: '🇺🇲',\n title: 'Flag: U.S. Outlying Islands',\n },\n {\n emoji: '🇺🇳',\n title: 'Flag: United Nations',\n },\n {\n emoji: '🇺🇸',\n title: 'Flag: United States',\n },\n {\n emoji: '🇺🇾',\n title: 'Flag: Uruguay',\n },\n {\n emoji: '🇺🇿',\n title: 'Flag: Uzbekistan',\n },\n {\n emoji: '🇻🇦',\n title: 'Flag: Vatican City',\n },\n {\n emoji: '🇻🇨',\n title: 'Flag: St. Vincent & Grenadines',\n },\n {\n emoji: '🇻🇪',\n title: 'Flag: Venezuela',\n },\n {\n emoji: '🇻🇬',\n title: 'Flag: British Virgin Islands',\n },\n {\n emoji: '🇻🇮',\n title: 'Flag: U.S. Virgin Islands',\n },\n {\n emoji: '🇻🇳',\n title: 'Flag: Vietnam',\n },\n {\n emoji: '🇻🇺',\n title: 'Flag: Vanuatu',\n },\n {\n emoji: '🇼🇫',\n title: 'Flag: Wallis & Futuna',\n },\n {\n emoji: '🇼🇸',\n title: 'Flag: Samoa',\n },\n {\n emoji: '🇽🇰',\n title: 'Flag: Kosovo',\n },\n {\n emoji: '🇾🇪',\n title: 'Flag: Yemen',\n },\n {\n emoji: '🇾🇹',\n title: 'Flag: Mayotte',\n },\n {\n emoji: '🇿🇦',\n title: 'Flag: South Africa',\n },\n {\n emoji: '🇿🇲',\n title: 'Flag: Zambia',\n },\n {\n emoji: '🇿🇼',\n title: 'Flag: Zimbabwe',\n },\n {\n emoji: '🏴󠁧󠁢󠁥󠁮󠁧󠁿',\n title: 'Flag: England',\n },\n {\n emoji: '🏴󠁧󠁢󠁳󠁣󠁴󠁿',\n title: 'Flag: Scotland',\n },\n {\n emoji: '🏴󠁧󠁢󠁷󠁬󠁳󠁿',\n title: 'Flag: Wales',\n },\n {\n emoji: '🏴󠁵󠁳󠁴󠁸󠁿',\n title: 'Flag for Texas (US-TX)',\n },\n ],\n};\n\nState.init({\n emojiPopup: true,\n selected: 0,\n hoverStateHeader: -1,\n hoveredEmoji: null,\n});\n\nconst updateHoveredEmoji = (emoji) => {\n State.update({ hoveredEmoji: emoji });\n};\nconst updateHoverStateHeader = (id) => {\n State.update({\n hoverStateHeader: id,\n });\n};\n\nconst CloseEmojiPopup = () =>\n State.update({\n emojiPopup: false,\n });\n\nconst updateSelectedType = (id) => {\n State.update({\n selected: id,\n });\n};\n\nconst TypeSelectorHeader = styled.div`\n display: flex;\n border-top-left-radius: 0.375rem;\n border-top-right-radius: 0.375rem;\n padding-left: 4px;\n pading-right: 4px;\n border-bottom: 1px solid #777583;\n`;\nconst EmojiComponentContainer = styled.div`\n width: fit-content;\n height: 420px;\n background-color: #0e0e10;\n border: 1px solid #777583;\n border-radius: 0.375rem;\n @media (max-width: 1024px) {\n height: 362px;\n }\n`;\n\nconst IconContainer = styled.div`\n width: 32px;\n height: 32px;\n padding: 8px;\n display: flex;\n justify-content: center;\n align-items: center;\n cursor: pointer;\n ${({ selected }) => selected && 'border-bottom: 1px solid #009900;'}\n transition: background-color 500ms ease\n`;\n\nconst IconContainerClose = styled.div`\n width: 32px;\n height: 32px;\n padding: 8px;\n display: flex;\n justify-content: center;\n align-items: center;\n background-color: #fa932b;\n :hover {\n background-color: #ffac58;\n }\n cursor: pointer;\n`;\n\nconst SelectedTypeTitle = styled.div`\n font-style: normal;\n font-weight: 600;\n line-height: normal;\n font-size: 12px;\n padding-left: 24px;\n margin-top: 0.725rem;\n color: #777583;\n`;\n\nconst types = [\n <i className=\"bi bi-emoji-smile text-light\"></i>,\n <i className=\"bi bi-tree text-light\"></i>,\n <i className=\"bi bi-egg-fill text-light\"></i>,\n <i className=\"bi bi-controller text-light\"></i>,\n <i className=\"bi bi-globe-americas text-light\"></i>,\n <i className=\"bi bi-lamp-fill text-light\"></i>,\n <i className=\"bi bi-shield text-light\"></i>,\n <i className=\"bi bi-flag-fill text-light\"></i>,\n];\n\nconst typesNames = [\n 'People',\n 'Nature',\n 'Food and Drinks',\n 'Activity',\n 'Travel Places',\n 'Objects',\n 'Symbols',\n 'Flags',\n];\n\nconst typesIds = [\n 'People',\n 'Nature',\n 'Food-drink',\n 'Activity',\n 'Travel-places',\n 'Objects',\n 'Symbols',\n 'Flags',\n];\n\nconst EmojiGrid = styled.div`\n height: 300px;\n display: grid;\n grid-template-columns: repeat(7, 32px);\n padding-top: 0.5rem;\n padding-left: 16px;\n padding-right: 8px:\n column-gap: 2px;\n row-gap: 2px;\n overflow: scroll;\n`;\n\nconst EmojiItemDesktop = styled.div`\n cursor: pointer;\n text-align: center;\n padding-bottom: 5px;\n padding-top: 5px;\n height: 30px;\n width: 30px;\n border-radius: 0.375rem;\n :hover {\n background-color: #34343a;\n }\n @media (max-width: 1024px) {\n display: none;\n }\n`;\n\nconst EmojiItemMobile = styled.div`\n cursor: pointer;\n text-align: center;\n padding-bottom: 5px;\n padding-top: 5px;\n height: 30px;\n width: 30px;\n border-radius: 0.375rem;\n :hover {\n background-color: #34343a;\n }\n @media (min-width: 1024px) {\n display: none;\n }\n`;\n\nconst Page = styled.div`\n background-color: #bf4f74;\n padding: 4rem;\n`;\n\nconst DisplayEmojiGrid = styled.div`\n display: flex;\n align-items: center;\n column-gap: 4px;\n width: 100%;\n height: 58px;\n border-bottom-left-radius: 0.375rem;\n border-bottom-right-radius: 0.375rem;\n border-top: 1px solid #777583;\n font-style: normal;\n font-weight: 600;\n padding-top: 1rem;\n line-height: normal;\n font-size: 12px;\n color: #777583;\n padding-left: 8px;\n padding-right: 8px;\n overflow: hidden;\n @media (max-width: 1024px) {\n display: none;\n }\n`;\n\nconst EmojiDisplay = styled.div`\n display: flex;\n align-items: center;\n overflow: hidden;\n font-style: normal;\n font-weight: 600;\n line-height: normal;\n font-size: 12px;\n color: #777583;\n`;\n\nreturn (\n <EmojiComponentContainer>\n <TypeSelectorHeader>\n {types.map((icon, id) => {\n return (\n <IconContainer\n selected={id === state.selected || id === state.hoverStateHeader}\n onClick={() => updateSelectedType(id)}\n key={`types-${id}`}\n onMouseEnter={() => updateHoverStateHeader(id)}\n onMouseLeave={() => updateHoverStateHeader(-1)}\n >\n {icon}\n </IconContainer>\n );\n })}\n </TypeSelectorHeader>\n <SelectedTypeTitle>{typesNames[state.selected]}</SelectedTypeTitle>\n <EmojiGrid>\n {emojiObj[typesIds[state.selected]].map((item, id) => (\n <div key={id}>\n <EmojiItemDesktop\n key={`desktop-${id}`}\n onMouseEnter={() => updateHoveredEmoji(item)}\n onMouseLeave={() => updateHoveredEmoji(undefined)}\n onClick={() => {\n props.OnEmojiSelected(item.emoji);\n }}\n >\n <p>{item.emoji}</p>\n </EmojiItemDesktop>\n <EmojiItemMobile\n key={`mobile-${id}`}\n onClick={() => {\n props.OnEmojiSelected(item.emoji);\n }}\n >\n <p>{item.emoji}</p>\n </EmojiItemMobile>\n </div>\n ))}\n </EmojiGrid>\n <DisplayEmojiGrid>\n <p>Emoji: </p>\n {state.hoveredEmoji && (\n <EmojiDisplay>\n <p>{state.hoveredEmoji.emoji}</p>\n <p>{state.hoveredEmoji.title}</p>\n </EmojiDisplay>\n )}\n </DisplayEmojiGrid>\n </EmojiComponentContainer>\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.CommentInput": { "": "const Container = styled.div`\n width: 100%\n display: flex;\n flex-direction: column;\n justify-content: start;\n align-items: center;\n`;\n\nconst ButtonContainer = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: start;\n align-items: center;\n gap: 10px;\n margin-top: 8px;\n`;\n\nconst InputCommentField = styled.input`\n color: #fff;\n height: 40px;\n width: 100%;\n padding: 8px 16px 8px 16px;\n border-radius: 6px;\n background-color: #0e0e10;\n border: none;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n`;\n\nconst FunctionButton = styled.button`\n ${({ backgroundColor }) =>\n backgroundColor && `background-color: ${backgroundColor};`}\n ${({ color }) => (color ? `color: ${color};` : 'color: #fff;')}\n :hover {\n ${({ hoverColor }) => hoverColor && `background-color: ${hoverColor};`}\n ${({ hoverOutline }) =>\n hoverOutline &&\n `outline-color: ${hoverOutline};\n outline-style: solid;\n outline-width: 1px;`}\n }\n border-radius: 4px;\n padding: 8px 16px 8px 16px;\n border: none;\n`;\n\nconst [comment, setComment] = useState('');\nconst [buttonVisible, setButtonsVisible] = useState(false);\n\nconst handleCommentChange = (e) => {\n setButtonsVisible(true);\n setComment(e.target.value);\n};\n\nconst handleOnClose = () => {\n setButtonsVisible(false);\n setComment('');\n};\n\nconst handlePostComment = useCallback(() => {\n props.addComment(comment);\n setComment('');\n setButtonsVisible(false);\n}, [comment, props.addComment]);\n\nreturn (\n <Container>\n <InputCommentField\n value={comment}\n onClick={() => setButtonsVisible(true)}\n onChange={handleCommentChange}\n placeholder=\"Add a comment\"\n autoFocus\n />\n {buttonVisible && (\n <ButtonContainer>\n <FunctionButton\n backgroundColor=\"#D0FC42\"\n color=\"black\"\n hoverColor=\"#BBE33B\"\n onClick={handlePostComment}\n >\n Post\n </FunctionButton>\n <FunctionButton\n backgroundColor=\"transparent\"\n color=\"#fff\"\n hoverOutline=\"#D0FC42\"\n onClick={handleOnClose}\n >\n Cancel\n </FunctionButton>\n </ButtonContainer>\n )}\n </Container>\n);\n" }, "Calimero.DocsChain.Authors": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst allAuthors = Near.calimeroView(contract, 'get_accounts_paged', {});\nconsole.log('AUTHORS');\nconsole.log(allAuthors);\n\nreturn (\n <>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.MainNavigation'}\n config={redirectConfig}\n props={{ currentNavPill: 'authors' }}\n />\n <h6>Total authors: {allAuthors.length}</h6>\n <ul>\n {allAuthors.map((data) => (\n <li>\n <a\n href={transformUrl(\n `https://near.social/#/mob.near/widget/ProfilePage?accountId=${author}`,\n )}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {data[0]}\n </a>{' '}\n -\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.ArticlesByAuthor?author=${data[0]}`,\n )}\n >\n {data[1].articles.length}\n </a>\n </li>\n ))}\n </ul>\n </>\n);\n" }, "Calimero.DocsChain.ArticlesByAuthor": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst author = props.author;\n\nconst getDateLastEdit = (timestamp) => {\n const date = new Date(Number(timestamp) / 1e6);\n const dateString = `${date.toLocaleDateString()} / ${date.toLocaleTimeString()}`;\n return dateString;\n};\n\nconst allArticleIds = Near.calimeroView(contract, 'get_article_ids_paged', {});\nconsole.log(allArticleIds);\n\nconst allArticles = allArticleIds\n .map((articleId) => {\n return {\n id: articleId,\n data: Near.calimeroView(contract, 'get_article', {\n article_id: articleId,\n }),\n };\n })\n .filter((article) => article.data.author == author);\nconsole.log(allArticles);\n\nreturn (\n <ol>\n {allArticles &&\n allArticles.map((article) => (\n <li key={article.id}>\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.ArticleView?articleId=${article.id}&blockHeight=${article.data.blockHeight}&lastEditor=${article.data.lastEditor}`,\n )}\n >\n {article.id}{' '}\n <small>\n (author: {article.data.author}\n {getDateLastEdit(article.data.timestamp)})\n </small>\n </a>\n </li>\n ))}\n </ol>\n);\n" }, "Calimero.Curb.Chat.ChatDisplaySplit": { "": "const componentOwnerId = props.componentOwnerId;\nconst readMessage = props.readMessage;\nconst handleReaction = props.handleReaction;\nconst openThread = props.openThread;\nconst setOpenThread = props.setOpenThread;\nconst activeChat = props.activeChat;\nconst curbApi = props.curbApi;\nconst accountId = props.accountId;\nconst incomingMessages = props.incomingMessages;\nconst updatedMessages = props.updatedMessages;\nconst resetImage = props.resetImage;\nconst sendMessage = props.sendMessage;\nconst getIconFromCache = props.getIconFromCache;\nconst isThread = props.isThread;\nconst isReadOnly = props.isReadOnly;\nconst toggleEmojiSelector = props.toggleEmojiSelector;\nconst isEmojiSelectorVisible = props.isEmojiSelectorVisible;\nconst channelMeta = props.channelMeta;\nconst channelUserList = props.channelUserList;\nconst setOpenMobileReactions = props.setOpenMobileReactions;\nconst openMobileReactions = props.openMobileReactions;\nconst onMessageDeletion = props.onMessageDeletion;\nconst onEditModeRequested = props.onEditModeRequested;\nconst onEditModeCancelled = props.onEditModeCancelled;\nconst onMessageUpdated = props.onMessageUpdated;\n\nconst ContainerPadding = styled.div`\n @media (max-width: 1024px) {\n height: calc(100vh - 160px) !important;\n padding-left: 0px !important;\n padding-right: 0px !important;\n }\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst ThreadTitle = styled.div`\n color: #fff;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%;\n`;\n\nconst ThreadContainer = styled.div`\n position: relative;\n padding-bottom: 20px;\n border-bottom: 2px solid #282933;\n display: flex;\n justify-content: space-between;\n @media (max-width: 1024px) {\n margin-right: 16px;\n padding-top: 104px;\n }\n`;\n\nconst CloseSvg = styled.svg`\n fill: #777583;\n :hover {\n fill: #fff;\n }\n cursor: pointer;\n`;\n\nconst Wrapper = styled.div`\n @media (max-width: 1024px) {\n width: 100% !important;\n }\n`;\n\nconst containerPaddingStyle = {\n display: 'flex',\n flexDirection: 'row',\n paddingTop: '1rem',\n paddingLeft: '2.5rem',\n paddingRight: '2.5rem',\n paddingBottom: '2.5rem',\n scrollBehavior: 'smooth',\n height: 'calc(100vh - 241px)',\n};\nconst chatStyle = {\n height: '',\n width: '',\n};\n\nconst wrapperStyle = {\n height: '100%',\n width: '100%',\n};\n\nconst CloseButtonSvg = ({ onClose }) => (\n <CloseSvg\n onClick={onClose}\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n className=\"bi bi-x-circle\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z\" />\n <path d=\"M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z\" />\n </CloseSvg>\n);\n\nconst ThreadHeader = ({ onClose }) => (\n <ThreadContainer>\n <ThreadTitle>Thread</ThreadTitle>\n <CloseButtonSvg onClose={onClose} />\n </ThreadContainer>\n);\n\nconst loadInitialMessages = () => {\n if (isThread && openThread.id) {\n return curbApi.fetchMessages({\n chat: activeChat,\n limit: 20,\n parentMessageId: openThread.id,\n });\n }\n return curbApi.fetchMessages({ chat: activeChat, limit: 20 });\n};\n\nconst loadPrevMessages = (id) => {\n if (isThread && id) {\n return curbApi.fetchMessages({\n chat: activeChat,\n beforeId: id,\n limit: 20,\n parentMessageId: openThread.id,\n });\n }\n return curbApi.fetchMessages({ chat: activeChat, beforeId: id, limit: 20 });\n};\n\nconst setThread = useCallback((message) => {\n setOpenThread(message);\n}, []);\n\nconst isModerator = useMemo(\n () =>\n channelUserList?.some(\n (user) => user.id === accountId && user.moderator === true,\n ),\n [channelUserList, accountId],\n);\n\nconst isOwner = accountId === channelMeta.createdBy;\n\nconst renderMessage = useMemo(\n () =>\n createMessageRenderer({\n handleReaction,\n getIconFromCache,\n accountId,\n isThread,\n openMobileReactions,\n setOpenMobileReactions,\n setThread: setThread ? setThread : undefined,\n toggleEmojiSelector,\n onEditModeRequested: onEditModeRequested,\n onEditModeCancelled: onEditModeCancelled,\n onMessageUpdated: onMessageUpdated,\n editable: () => false,\n deleteable: (message) => {\n if (message.sender === accountId) {\n return true;\n }\n return isOwner || isModerator;\n },\n onDeleteMessageRequested: (message) => {\n onMessageDeletion(message);\n },\n }),\n [accountId, isOwner, isModerator, openMobileReactions],\n);\n\nif (openThread && isThread) {\n chatStyle.height = 'calc(100% - 124px)';\n chatStyle.width = '100%';\n chatStyle['overflow'] = 'hidden';\n containerPaddingStyle.flexDirection = 'column';\n containerPaddingStyle.paddingLeft = '0px';\n containerPaddingStyle.height = '100%';\n containerPaddingStyle['width'] = '100%';\n wrapperStyle.width = '100%';\n} else if (openThread && !isThread) {\n chatStyle.height = '100%';\n chatStyle.width = '100%';\n containerPaddingStyle.paddingRight = '0px';\n wrapperStyle.width = '60%';\n} else {\n chatStyle.height = '100%';\n chatStyle.width = '100%';\n chatStyle['overflow'] = 'hidden';\n}\n\nconst EmojiPopupContainer = styled.div`\n position: absolute;\n bottom: 70px;\n right: 2.5rem;\n`;\n\nreturn (\n <Wrapper style={wrapperStyle}>\n <ContainerPadding style={containerPaddingStyle}>\n {openThread && isThread && (\n <ThreadHeader\n openThread={openThread}\n onClose={() => setOpenThread(undefined)}\n />\n )}\n <VirtualizedChat\n loadInitialMessages={loadInitialMessages}\n loadPrevMessages={loadPrevMessages}\n deletedMessages={deletedMessages}\n incomingMessages={incomingMessages}\n updatedMessages={updatedMessages}\n onItemNewItemRender={readMessage}\n shouldTriggerNewItemIndicator={(message) =>\n message.sender !== accountId\n }\n render={renderMessage}\n chatId={isThread ? openThread.id : activeChat} // re-render the chat when the chat/thread changes\n style={chatStyle}\n />\n </ContainerPadding>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.MessageInput`}\n props={{\n componentOwnerId,\n selectedChat:\n activeChat.type === 'channel' ? activeChat.name : activeChat.id,\n sendMessage: sendMessage,\n resetImage: resetImage,\n openThread,\n isThread,\n isReadOnly,\n isOwner,\n isModerator,\n }}\n />\n </Wrapper>\n);\n" }, "Calimero.Curb.Chat.ChatContainer": { "": "const componentOwnerId = props.componentOwnerId;\nconst contract = props.contract;\nconst curbApi = props.curbApi;\nconst activeChat = props.activeChat;\nconst accountId = props.accountId;\nconst wsApi = props.wsApi;\nconst isWsConnectionActive = props.isWsConnectionActive;\n\nconst ChatContainer = styled.div`\n display: flex;\n @media (min-width: 1025px) {\n padding-left: 4px;\n height: calc(100vh - 169px);\n }\n @media (max-width: 1024px) {\n display: flex;\n flex-direction: column;\n padding-top: 104px;\n }\n`;\n\nconst ThreadWrapper = styled.div`\n height: 100%;\n flex: 1;\n border-left: 2px solid #282933;\n padding-left: 20px;\n @media (max-width: 1024px) {\n border-left: none;\n position: fixed;\n left: 0;\n right: 0; /* Set both left and right to 0 to stretch to full width */\n top: 50%; /* Vertically center the element */\n transform: translateY(-50%); /* Center it vertically */\n z-index: 30;\n background-color: #0e0e10;\n }\n`;\n\nconst [openThread, setOpenThread] = useState(undefined);\nconst [incomingMessages, setIncomingMessages] = useState([]);\nconst [updatedMessages, setUpdatedMessages] = useState([]);\nconst [incomingThreadMessages, setIncomingThreadMessages] = useState([]);\nconst [updatedThreadMessages, setUpdatedThreadMessages] = useState([]);\nconst [isEmojiSelectorVisible, setIsEmojiSelectorVisible] = useState(false);\nconst [messageWithEmojiSelector, setMessageWithEmojiSelector] = useState(false);\nconst [channelMeta, setChannelMeta] = useState({});\nconst [channelUserList, setChannelUserList] = useState([]);\n\nconst [openMobileReactions, setOpenMobileReactions] = useState('');\n\nuseEffect(() => {\n if (activeChat.type === 'channel') {\n curbApi.getChannelMeta(activeChat.name).then(setChannelMeta);\n curbApi.getChannelMembers(activeChat.name).then(setChannelUserList);\n }\n}, [activeChat]);\n\nconst toggleEmojiSelector = useCallback(\n (message) => {\n setMessageWithEmojiSelector(message);\n setIsEmojiSelectorVisible((prev) => !prev);\n },\n [setIsEmojiSelectorVisible],\n);\n\nconst activeChatRef = useRef(activeChat);\nconst openThreadRef = useRef(openThread);\n\nuseEffect(() => {\n if (activeChat.type === 'channel' && isWsConnectionActive) {\n // todo! move this to page initialization\n // todo! basically, once the page has loaded,\n // todo! and we've fetched channels, subscribe to them\n wsApi.methods.subscribe(\n { [contract]: [activeChat.name] },\n (err, result) => {\n if (err) return console.log('error subscribing to channel', err);\n console.log('subscribed to', activeChat, err, result);\n curbApi.emit('subscribed', activeChat.name);\n },\n );\n }\n activeChatRef.current = activeChat;\n}, [activeChat, isWsConnectionActive]);\n\nuseEffect(() => {\n setOpenThread(undefined);\n}, [activeChat]);\n\nuseEffect(() => {\n openThreadRef.current = openThread;\n}, [openThread]);\n\nconst computeReaction = useCallback((message, reaction, sender) => {\n const accounts = message.reactions[reaction] ?? [];\n let update;\n if (accounts.includes(sender)) {\n update = accounts.filter((a) => a !== sender);\n } else {\n update = [...accounts, sender];\n }\n return { reactions: { ...message.reactions, [reaction]: update } };\n}, []);\n\nuseEffect(() => {\n const messageListener = (event) => {\n if (\n (activeChatRef.current.type === 'direct_message' &&\n event.sender === activeChatRef.current.id) ||\n (activeChatRef.current.type === 'channel' &&\n event.receiver.type === 'channel' &&\n event.receiver.name === activeChatRef.current.name)\n ) {\n curbApi\n .fetchKey({ chat: activeChatRef.current })\n .then((key) => {\n const decryptedMessages = curbApi.decryptMessages([event], key);\n const newChannelMessages = [];\n const openedThreadMessages = [];\n //record of keys which are id of parent messages and number of new messages that represents addition on threadsCount\n const notOpenedThreadsMessagesSet = {};\n //for each decrypted message check if it is in channel or in thread\n decryptedMessages.map((message) => {\n //thread message\n if (message.parent_message) {\n if (message.parent_message === openThreadRef.current.id) {\n //currently opened thread message\n openedThreadMessages.push(message);\n }\n //update threads count\n if (message.parent_message in notOpenedThreadsMessagesSet) {\n notOpenedThreadsMessagesSet[message.parent_message]++;\n } else {\n // If key doesn't exist, initialize with 1\n notOpenedThreadsMessagesSet[message.parent_message] = 1;\n }\n } else {\n //channel message\n newChannelMessages.push(message);\n }\n });\n\n //update messages count in closed threads\n Object.entries(notOpenedThreadsMessagesSet).forEach(\n ([key, value]) => {\n const parentUpdate = [\n {\n id: key,\n descriptor: {\n updateFunction: (message) => ({\n threadCount: message.threadCount + value,\n threadLastTimestamp: Date.now(),\n }),\n },\n },\n ];\n setUpdatedMessages(parentUpdate);\n },\n );\n\n //update messages in opened thread\n if (openedThreadMessages.length > 0) {\n console.log('update messages in opened thread');\n setOpenThread({\n ...openThreadRef.current,\n threadCount: openThreadRef.current.threadCount + 1,\n });\n setIncomingThreadMessages(openedThreadMessages);\n }\n\n if (newChannelMessages.length > 0) {\n //update messages outside thread\n setIncomingMessages(newChannelMessages);\n }\n })\n .catch((err) => {\n console.log('Failing to decrypt message', err);\n });\n }\n };\n const reactionListener = (event) => {\n const { message_id, reaction, sender } = event;\n const updateFunction = (message) =>\n computeReaction(message, reaction, sender);\n setUpdatedMessages([{ id: message_id, descriptor: { updateFunction } }]);\n };\n\n const editsListener = (event) => {\n console.log('edited message', event);\n if (\n (activeChatRef.current.type === 'direct_message' &&\n event.sender === activeChatRef.current.id) ||\n (activeChatRef.current.type === 'channel' &&\n event.receiver.type === 'channel' &&\n event.receiver.name === activeChatRef.current.name)\n ) {\n curbApi\n .fetchKey({ chat: activeChatRef.current })\n .then((key) => {\n const updates = curbApi.deleteMessage([event], key).map((m) => ({\n id: m.id,\n descriptor: {\n updatedFields: { ...m },\n },\n }));\n if (event.parent_message === openThreadRef.current.id) {\n console.log('updating thread messages', updates);\n setUpdatedThreadMessages(updates);\n } else {\n console.log('updating messages', updates);\n // TODO update thread parent if required\n setUpdatedMessages(updates);\n }\n })\n .catch((err) => {\n console.log('Failing to decrypt message', err);\n });\n }\n };\n\n const deleteListener = (event) => {\n if (\n (activeChatRef.current.type === 'direct_message' &&\n event.sender === activeChatRef.current.id) ||\n (activeChatRef.current.type === 'channel' &&\n event.receiver.type === 'channel' &&\n event.receiver.name === activeChatRef.current.name)\n ) {\n handleDeleteMessage(event);\n }\n };\n\n wsApi.notifications\n .on('ChannelMessage', messageListener)\n .on('MessageReaction', reactionListener)\n .on('EditedMessage', editsListener)\n .on('DeletedMessage', deleteListener);\n return () => {\n wsApi.notifications\n .off('ChannelMessage', messageListener)\n .off('MessageReaction', reactionListener)\n .off('EditedMessage', editsListener)\n .off('DeletedMessage', deleteListener);\n };\n}, []);\n\nconst readMessage = useCallback(\n (message) => {\n if (message?.id && message?.sender !== accountId) {\n // we don't report our own messages\n curbApi.readMessage({\n chat: activeChatRef.current,\n messageId: message.id,\n });\n }\n },\n [curbApi, accountId],\n);\n\nconst handleReaction = useCallback(\n (message, reaction) => {\n const updates = [\n {\n id: message.id,\n descriptor: {\n updateFunction: (message) =>\n computeReaction(message, reaction, accountId),\n },\n },\n ];\n setUpdatedMessages(updates);\n setUpdatedThreadMessages(updates);\n curbApi.toggleReaction({ messageId: message.id, reaction });\n },\n [curbApi],\n);\n\nconst convertCidToUrl = (file) =>\n file\n ? { ...file, ipfs_cid: `https://ipfs.near.social/ipfs/${file.cid}` }\n : null;\n\nconst createTemporalMessage = (text, img, uploadedFile, parentMessage) => {\n const images = img?.file ? [convertCidToUrl(img.file)] : [];\n const files = uploadedFile?.file ? [convertCidToUrl(uploadedFile.file)] : [];\n const temporalId = curbApi.utils.toHexString(Crypto.randomBytes(16));\n\n return {\n id: temporalId,\n nonce: curbApi.utils.toHexString(Crypto.randomBytes(16)),\n reactions: [],\n images,\n files,\n sender: accountId,\n text,\n timestamp: Date.now(),\n temporalId,\n parent_message: parentMessage?.id,\n };\n};\n\nconst submitMessage = (chat, message) =>\n new Promise((resolve, reject) => {\n curbApi.sendMessage(\n {\n message: message.text,\n chat,\n images: message.images,\n files: message.files,\n threadId: message.parent_message ? message.parent_message : undefined,\n },\n (err, result) => {\n if (result) {\n resolve(result.result.id);\n } else {\n reject(err);\n }\n },\n );\n });\n\nconst createSendMessageHandler =\n (chat, parentMessage) => (text, img, uploadedFile) => {\n const temporalMessage = createTemporalMessage(\n text,\n img,\n uploadedFile,\n parentMessage,\n );\n\n const incomingCall = parentMessage\n ? setIncomingThreadMessages\n : setIncomingMessages;\n incomingCall([temporalMessage]);\n\n submitMessage(chat, temporalMessage)\n .then((realMessageId) => {\n const update = [\n {\n id: temporalMessage.id,\n descriptor: {\n updatedFields: { id: realMessageId, temporalId: undefined },\n },\n },\n ];\n\n if (parentMessage) {\n const parentUpdate = [\n {\n id: parentMessage.id,\n descriptor: {\n updateFunction: (message) => ({\n threadCount: message.threadCount + 1,\n threadLastTimestamp: Date.now(),\n }),\n },\n },\n ];\n setUpdatedThreadMessages(update);\n setUpdatedMessages(parentUpdate);\n } else {\n setUpdatedMessages(update);\n }\n })\n .catch((err) => {\n console.log(err);\n });\n };\n\nconst sendMessage = useMemo(\n () => createSendMessageHandler(activeChat),\n [activeChat],\n);\nconst sendThreadMessage = useMemo(\n () => createSendMessageHandler(activeChat, openThread),\n [activeChat, openThread],\n);\n\nconst getIconFromCache = useCallback((accountId) => {\n const fallbackImage = 'https://i.imgur.com/e8buxpa.png';\n return new Promise((resolve, _reject) => {\n const profile = Social.getr(`${accountId}/profile`);\n const baseImageObject = profile.image;\n if (\n !profile &&\n !baseImageObject.url &&\n !baseImageObject.ipfs_cid &&\n !baseImageObject.nft\n ) {\n resolve(fallbackImage);\n } else {\n resolve(\n `https://i.near.social/magic/thumbnail/https://near.social/magic/img/account/${accountId}`,\n );\n }\n });\n}, []);\n\nconst updateDeletedMessage = ({ id, parent_message }) => {\n const update = [{ id, descriptor: { updatedFields: { deleted: true } } }];\n if (parent_message) {\n const parentUpdate = [\n {\n id: parent_message,\n descriptor: {\n updateFunction: (message) => ({\n threadCount: message.threadCount - 1,\n }),\n },\n },\n ];\n setUpdatedThreadMessages(update);\n setUpdatedMessages(parentUpdate);\n } else {\n if (id === openThreadRef?.current?.id) {\n setOpenThread(undefined);\n }\n setUpdatedMessages(update);\n }\n};\n\nconst handleDeleteMessage = useCallback(\n (message) => {\n updateDeletedMessage(message);\n curbApi.deleteMessage({ message, chat: activeChatRef.current });\n },\n [curbApi, activeChat],\n);\n\nconst handleEditMode = useCallback((message) => {\n console.log('edit mode', message);\n const update = [\n {\n id: message.id,\n descriptor: {\n updatedFields: { editMode: !message.editMode },\n },\n },\n ];\n setUpdatedMessages(update);\n}, []);\n\nconst handleEditedMessage = useCallback(\n (message) => {\n curbApi.editMessage({ message, chat: activeChat });\n const update = [\n {\n id: message.id,\n descriptor: {\n updatedFields: {\n text: message.text,\n editMode: false,\n },\n },\n },\n ];\n setUpdatedMessages(update);\n },\n [activeChat, curbApi],\n);\n\nconst handleEditedThreadMessage = useCallback(\n (message) => {\n curbApi.editMessage({ message, chat: activeChat });\n const update = [\n {\n id: message.id,\n descriptor: {\n updatedFields: {\n text: message.text,\n editMode: false,\n },\n },\n },\n ];\n setUpdatedThreadMessages(update);\n },\n [activeChat, curbApi],\n);\n\nconst selectThread = useCallback((message) => {\n console.log('select thread', message);\n setOpenThread(message);\n // reset thread updates\n setIncomingThreadMessages([]);\n setUpdatedThreadMessages([]);\n}, []);\n\nreturn (\n <ChatContainer>\n <>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.ChatDisplaySplit`}\n props={{\n componentOwnerId,\n readMessage,\n handleReaction,\n openThread,\n setOpenThread: selectThread,\n activeChat,\n accountId,\n curbApi,\n incomingMessages,\n updatedMessages,\n resetImage: props.resetImage,\n sendMessage,\n getIconFromCache,\n isThread: false,\n isReadOnly: activeChat.readOnly,\n toggleEmojiSelector,\n channelMeta,\n channelUserList,\n openMobileReactions,\n setOpenMobileReactions,\n onMessageDeletion: handleDeleteMessage,\n onEditModeRequested: handleEditMode,\n onEditModeCancelled: handleEditMode,\n onMessageUpdated: handleEditedMessage,\n }}\n />\n </>\n {openThread.id && (\n <ThreadWrapper>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.ChatDisplaySplit`}\n props={{\n componentOwnerId,\n readMessage,\n handleReaction,\n openThread,\n setOpenThread: selectThread,\n activeChat,\n accountId,\n curbApi,\n incomingMessages: incomingThreadMessages,\n updatedMessages: updatedThreadMessages,\n resetImage: props.resetImage,\n sendMessage: sendThreadMessage,\n getIconFromCache,\n isThread: true,\n isReadOnly: activeChat.readOnly,\n toggleEmojiSelector,\n channelMeta,\n channelUserList,\n openMobileReactions,\n setOpenMobileReactions,\n onMessageDeletion: handleDeleteMessage,\n onEditModeRequested: handleEditMode,\n onEditModeCancelled: handleEditMode,\n onMessageUpdated: handleEditedThreadMessage,\n }}\n />\n </ThreadWrapper>\n )}\n {isEmojiSelectorVisible && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.EmojiSelector.EmojiSelectorPopup`}\n props={{\n OnEmojiSelected: (emoji) => {\n handleReaction(messageWithEmojiSelector, emoji);\n setIsEmojiSelectorVisible(false);\n },\n onClose: () => setIsEmojiSelectorVisible(false),\n componentOwnerId,\n }}\n key=\"chat-emojis-component\"\n />\n )}\n </ChatContainer>\n);\n" }, "Calimero.Curb.SideSelector.UserItem": { "": "const UserListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n :hover {\n color: #ffffff;\n }\n :hover {\n background-color: #25252a;\n }\n cursor: pointer;\n ${({ selected }) =>\n selected\n ? 'color: #fff; background-color: #25252a;'\n : 'color: #777583; background-color: #0E0E10;'}\n`;\n\nconst UserInfoContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst NameContainer = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst handleClick = useCallback(() => {\n props.onDMSelected(user);\n}, [user, props.onDMSelected]);\n\nconst user = props.user;\nreturn (\n <UserListItem selected={props.selected} onClick={handleClick}>\n <UserInfoContainer>\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n active: user.active,\n componentOwnerId: props.componentOwnerId,\n }}\n />\n <NameContainer>{`${user.id}`}</NameContainer>\n </UserInfoContainer>\n {user.unreadMessages.count > 0 && (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.SideSelector.UnreadMessagesBadge`}\n props={{\n messageCount: user.unreadMessages.count,\n backgroundColor: '#777583',\n }}\n />\n )}\n </UserListItem>\n);\n" }, "Calimero.DocsChain.Logo.DocsChainLogo": { "": "const LogoContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n ${({ justify }) => justify && 'justify-content: center;'}\n`;\n\nconst TextContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-size: 20.923px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-right: 0.875rem;\n`;\n\nreturn (\n <LogoContainer>\n <svg\n width=\"28\"\n height=\"28\"\n viewBox=\"0 0 28 28\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <rect width=\"28\" height=\"28\" rx=\"6\" fill=\"#4E95FF\" />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M24.1568 13.9999C24.1568 8.69352 19.8554 4.39209 14.549 4.39209C9.2426 4.39209 4.94117 8.69352 4.94117 13.9999C4.93884 15.7503 5.41672 17.4677 6.32278 18.9653L5.46576 21.8784C5.41655 22.0456 5.41331 22.223 5.45639 22.392C5.49946 22.5609 5.58725 22.7151 5.71053 22.8384C5.83381 22.9617 5.98802 23.0495 6.15696 23.0926C6.3259 23.1356 6.50333 23.1324 6.67058 23.0832L9.58368 22.2262C11.0811 23.1326 12.7986 23.6105 14.549 23.6078C15.4677 23.6078 16.3564 23.4788 17.1977 23.238C17.1509 22.8993 17.1267 22.5534 17.1267 22.2017C17.1267 18.2267 20.2197 14.974 24.1303 14.719C24.1479 14.4816 24.1568 14.2418 24.1568 13.9999ZM10.1672 12.9809C10.4375 12.7106 10.804 12.5588 11.1863 12.5588C11.5685 12.5588 11.9351 12.7106 12.2053 12.9809C12.4756 13.2511 12.6274 13.6177 12.6274 13.9999C12.6274 14.3822 12.4756 14.7487 12.2053 15.019C11.9351 15.2893 11.5685 15.4411 11.1863 15.4411C10.804 15.4411 10.4375 15.2893 10.1672 15.019C9.89693 14.7487 9.74509 14.3822 9.74509 13.9999C9.74509 13.6177 9.89693 13.2511 10.1672 12.9809ZM16.8927 12.9809C17.163 12.7106 17.5295 12.5588 17.9118 12.5588C18.294 12.5588 18.6605 12.7106 18.9308 12.9809C19.2011 13.2511 19.3529 13.6177 19.3529 13.9999C19.3529 14.3822 19.2011 14.7487 18.9308 15.019C18.6605 15.2893 18.294 15.4411 17.9118 15.4411C17.5295 15.4411 17.163 15.2893 16.8927 15.019C16.6224 14.7487 16.4706 14.3822 16.4706 13.9999C16.4706 13.6177 16.6224 13.2511 16.8927 12.9809Z\"\n fill=\"#111111\"\n />\n </svg>\n <TextContainer>DocsChain</TextContainer>\n </LogoContainer>\n);\n" }, "Calimero.Common.Login.index": { "": "const {\n loginApi,\n accountId,\n componentOwnerId,\n onValidLogin,\n logo,\n onAppJoin,\n refresh,\n} = props;\n\nconst [hasBootstrapped, setHasBootstrapped] = useState(false);\nconst [hasValidKey, setHasValidKey] = useState(false);\nconst [loading, setLoading] = useState(false);\n\nconst checkMembership = (accountId) => {\n return loginApi\n .getMembers()\n .then((memberList) =>\n memberList.map((member) => member.id).includes(accountId),\n );\n};\n\nconst bootstrap = () => {\n setLoading(true);\n\n loginApi\n .validateKey()\n .then((isValidKey) => {\n if (!isValidKey) {\n console.log('Invalid key');\n setHasValidKey(false);\n return Promise.reject('Invalid key');\n }\n\n setHasValidKey(true);\n console.log('Valid key');\n return checkMembership(accountId);\n })\n .then((isAlreadyMember) => {\n if (isAlreadyMember) {\n console.log('Is member');\n return true;\n }\n\n console.log('Not a member, attempting to join...');\n return loginApi.join().then(() => checkMembership(accountId));\n })\n .then((isNowMember) => {\n if (isNowMember) {\n onValidLogin();\n } else {\n console.log('Failed to join');\n }\n })\n .catch((error) => {\n console.log('Bootstrap error:', error);\n })\n .finally(() => {\n setLoading(false);\n setHasBootstrapped(true);\n });\n};\n\nconst join = () => {\n setLoading(true);\n loginApi\n .join()\n .then((result) => {\n if (result) {\n setIsMember(true);\n onValidLogin();\n }\n })\n .finally(() => {\n setLoading(false);\n });\n};\n\nuseEffect(() => {\n if (accountId && !hasBootstrapped && !loading) {\n bootstrap();\n }\n}, [accountId, hasBootstrapped, loading]);\n\nif (loading) {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.Loading`}\n props={{ logo }}\n />\n );\n}\n\nif (!accountId) {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Login.LoginPopup`}\n props={{ logo }}\n />\n );\n}\n\nif (!hasValidKey) {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Login.JoinPopup`}\n props={{\n logo,\n join: loginApi.login,\n refresh,\n }}\n />\n );\n}\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.AutoRefresh`}\n props={{ logo, refresh }}\n />\n);\n" }, "Calimero.Curb.ProfileIcon.Image": { "": "// Forked from: rubycoptest.testnet/widget/Image\n\nconst accountId = props.accountId;\nconst className = props.className;\nconst style = props.style;\nconst alt = props.alt;\nconst fallbackUrl = props.fallbackUrl;\nconst thumbnail = props.thumbnail;\nconst componentOwnerId = props.componentOwnerId;\n\nconst [imageUrl, setImageUrl] = useState(null);\nconst [nftImage, setNftImage] = useState(null);\nconst profile = Social.getr(`${accountId}/profile`);\n\nfunction toUrl(image) {\n return (\n (image.ipfs_cid\n ? `https://ipfs.near.social/ipfs/${image.ipfs_cid}`\n : image.url) || fallbackUrl\n );\n}\n\nconst thumb = (imageUrl) =>\n thumbnail && imageUrl && !imageUrl.startsWith('data:image/')\n ? `https://i.near.social/${thumbnail}/${imageUrl}`\n : imageUrl;\n\nuseEffect(() => {\n const image = profile.image;\n if (image && image.nft) {\n setNftImage(image);\n } else if (image) {\n const thumbImg = thumb(toUrl(image));\n setImageUrl(thumbImg);\n } else {\n setImageUrl(fallbackUrl);\n }\n}, [accountId, profile.image, fallbackUrl, thumbnail]);\n\nreturn nftImage.nft.contractId && nftImage.nft.tokenId ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ProfileIcon.NftImage`}\n props={{\n className,\n style,\n alt,\n nft: nftImage.nft,\n thumbnail,\n fallbackUrl,\n }}\n />\n) : (\n <img className={className} style={style} src={imageUrl} alt={alt} />\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.TaskDetailsDialog": { "": "const {\n task,\n componentOwnerId,\n onCloseTaskDetails,\n removeTaskFromColumn,\n projectId,\n contract,\n addActionStatus,\n taskColumn,\n columnIndex,\n updateTaskInColumn,\n} = props;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 734px;\n height: fit-content;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Title = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: transparent;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Input = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n flex: 1;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: transparent;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 104px;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n border: none;\n white-space: nowrap;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 68px;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 185px;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst LabelContainer = styled.div`\n width: 100%;\n`;\n\nconst ReporterInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n padding: 1rem;\n`;\n\nconst ReporterId = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n color: #fff;\n`;\n\nconst AssigneeContainer = styled.div`\n margin-left: 16px;\n`;\n\nconst FunctionButton = styled.button`\n ${({ backgroundColor }) =>\n backgroundColor && `background-color: ${backgroundColor};`}\n ${({ color }) => (color ? `color: ${color};` : 'color: #fff;')}\n ${({ disabled }) =>\n disabled ? 'cursor: not-allowed; opacity: 0.5;' : 'cursor: pointer;'}\n :hover {\n ${({ hoverColor }) =>\n hoverColor && !disabled && `background-color: ${hoverColor};`}\n }\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DialogTitle = styled.p`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 15px;\n font-style: normal;\n font-weight: 700;\n line-height: 100%;\n padding: 0;\n margin: 0;\n`;\n\nconst ButtonsContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 24px;\n`;\n\nconst [editTaskTitle, setEditTaskTitle] = useState(task.title);\nconst [editTaskDescription, setEditTaskDescription] = useState(\n task.description,\n);\nconst [assignee, setAssignee] = useState(task.assignee);\nconst [editTaskTitleMissing, setEditTaskTitleMissing] = useState(false);\nconst [isDeleteTaskVisible, setIsDeleteTaskVisible] = useState(false);\n\nconst onEditTaskTitle = (e) => {\n setEditTaskTitleMissing(false);\n setEditTaskTitle(e.target.value);\n};\n\nconst onEditTaskDescription = (e) => {\n setEditTaskDescription(e.target.value);\n};\n\nconst updateTask = useCallback(() => {\n try {\n const editedTask = {\n ...task,\n title: editTaskTitle,\n description: editTaskDescription,\n assignee: assignee === 'no_assignee' ? null : assignee,\n };\n\n let promises = [];\n\n if (task.title !== editTaskTitle) {\n promises.push(\n Near.fakCalimeroCall(contract, 'change_task_name', {\n project_id: projectId,\n task_id: task.id,\n new_title: editTaskTitle,\n }),\n );\n }\n\n if (task.description !== editTaskDescription) {\n promises.push(\n Near.fakCalimeroCall(contract, 'edit_task_description', {\n project_id: projectId,\n task_id: task.id,\n new_description: editTaskDescription,\n }),\n );\n }\n\n if (task.assignee !== assignee) {\n promises.push(\n Near.fakCalimeroCall(\n contract,\n assignee === 'no_assignee' ? 'remove_assignee' : 'assign_task',\n {\n project_id: projectId,\n task_id: task.id,\n ...(assignee !== 'no_assignee' && { assignee: assignee }),\n },\n ),\n );\n }\n\n if (promises.length > 0) {\n updateTaskInColumn(columnIndex, editedTask);\n Promise.all(promises);\n }\n } catch (e) {\n console.log('updateTask', e);\n }\n onCloseTaskDetails();\n}, [\n task,\n editTaskTitle,\n editTaskDescription,\n projectId,\n contract,\n taskColumn,\n assignee,\n columnIndex,\n]);\n\nconst deleteTask = useCallback(\n (task) => {\n const actionStatusPrefix = 'Delete task';\n let newActionStatus = {\n id: actionStatusPrefix + taskId,\n status: `Deleting task: ${task.id}`,\n };\n try {\n addActionStatus(newActionStatus);\n removeTaskFromColumn(task.id, columnIndex);\n\n Near.fakCalimeroCall(contract, 'delete_task_by_id', {\n project_id: projectId,\n task_id: task.id,\n }).then(() => {\n newActionStatus.status = `Deleted task: ${taskId}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n newActionStatus.status = `Error deleting task: ${taskId}`;\n addActionStatus(newActionStatus);\n }\n setIsDeleteTaskVisible(false);\n onCloseTaskDetails();\n },\n [projectId, contract, addActionStatus, columnIndex],\n);\n\nconst handleUpdateTask = useCallback(() => {\n if (!editTaskTitle || editTaskTitle.trim() === '') {\n setEditTaskTitleMissing(true);\n setTimeout(() => {\n setEditTaskTitleMissing(false);\n setEditTaskTitle(task.title);\n }, 3000);\n return;\n } else if (\n editTaskTitle !== task.title ||\n editTaskDescription !== task.description ||\n assignee !== task.assignee\n ) {\n updateTask();\n }\n}, [\n task,\n editTaskTitle,\n editTaskDescription,\n setEditTaskTitleMissing,\n setEditTaskTitle,\n updateTask,\n assignee,\n]);\n\nreturn (\n <>\n {isDeleteTaskVisible && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ConfirmationPopup`}\n props={{\n onClick: () => deleteTask(task),\n onClose: () => setIsDeleteTaskVisible(false),\n title: 'Are you sure you want to delete the task?',\n }}\n />\n )}\n <OverlayContainer>\n <PopupContainer>\n <Header>\n <DialogTitle>Edit task</DialogTitle>\n <CloseButton onClick={onCloseTaskDetails}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <InputContainer>\n <Label>Title</Label>\n <Title\n onChange={onEditTaskTitle}\n value={editTaskTitle}\n placeholder=\"Add Title\"\n />\n {editTaskTitleMissing && <MissingTitle>Missing title</MissingTitle>}\n </InputContainer>\n </FieldContainer>\n <FieldContainer>\n <InputContainer>\n <Label>Assignee</Label>\n <AssigneeContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.AssigneeDropdown`}\n props={{\n componentOwnerId,\n assignee,\n setAssignee,\n contract,\n projectId,\n }}\n />\n </AssigneeContainer>\n </InputContainer>\n </FieldContainer>\n <LabelContainer>\n <InputContainer>\n <Label>Created by</Label>\n <ReporterInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: task.reporter,\n componentOwnerId,\n }}\n />\n <ReporterId>{task.reporter}</ReporterId>\n </ReporterInfo>\n </InputContainer>\n </LabelContainer>\n <FieldContainer>\n <InputContainer>\n <Label>Description</Label>\n <Input\n onChange={onEditTaskDescription}\n value={editTaskDescription}\n onBlur={updateTaskDescription}\n placeholder=\"Add Description\"\n />\n </InputContainer>\n </FieldContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.Comments`}\n props={{\n componentOwnerId,\n comments: task.comments,\n contract,\n projectId,\n taskId: task.id,\n }}\n />\n <ButtonsContainer>\n <FunctionButton\n backgroundColor=\"#D0FC42\"\n color=\"black\"\n hoverColor=\"#BBE33B\"\n disabled={\n editTaskTitle === task.title &&\n editTaskDescription === task.description &&\n assignee === task.assignee\n }\n onClick={handleUpdateTask}\n >\n Save\n </FunctionButton>\n <FunctionButton\n backgroundColor=\"transparent\"\n hoverColor=\"#F25757\"\n onClick={() => setIsDeleteTaskVisible(true)}\n >\n Delete\n </FunctionButton>\n </ButtonsContainer>\n </PopupContainer>\n </OverlayContainer>\n </>\n);\n" }, "Calimero.TaskChain.SideMenu.SideMenu": { "": "const {\n contract,\n selectedProjectId,\n setSelectedProjectId,\n functionLoader,\n setFunctionLoader,\n componentOwnerId,\n accountId,\n addActionStatus,\n} = props;\n\nconst SideMenuContainer = styled.div`\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n width: 230px;\n padding-left: 0;\n background-color: #0e0e10;\n color: white;\n padding: 3px;\n gap: 16px;\n`;\n\nconst AddBoardsContainer = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n`;\n\nconst AddBoardText = styled.p`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 700;\n line-height: 150%;\n padding: 0;\n margin: 0;\n`;\n\nconst AddBoardButton = styled.button`\n background-color: transparent;\n border: none;\n :focus {\n outline-width: 0px;\n }\n`;\n\nconst AddBoardIcon = styled.div`\n color: #777583;\n :hover {\n color: #ffffff;\n }\n`;\n\nconst createBoardButton = (\n <AddBoardButton>\n <AddBoardIcon>\n <i className=\"bi bi-plus-circle\"></i>\n </AddBoardIcon>\n </AddBoardButton>\n);\n\nconst [selectedUser, setSelectedUser] = useState(undefined);\nconst [users, setUsers] = useState([]);\nconst [boards, setBoards] = useState([]);\nconst [showEditBoardDialog, setShowEditBoardDialog] = useState(false);\nconst [deleteBoardId, setDeleteBoardId] = useState(undefined);\nconst [showDeleteBoardDialog, setShowDeleteBoardDialog] = useState(false);\nconst [createBoardStatus, setCreateBoardStatus] = useState([]);\n\nconst [editedBoard, setEditedBoard] = useState(undefined);\n\nconst addBoard = useCallback(\n (newBoard) => {\n setBoards((previousBoards) => [...previousBoards, newBoard]);\n },\n [setBoards],\n);\n\nconst updateBoardName = useCallback(\n (updatedBoard) => {\n setBoards((previousBoards) =>\n previousBoards.map((board) =>\n board[0] === updatedBoard[0] ? updatedBoard : board,\n ),\n );\n },\n [setBoards],\n);\n\nconst deleteBoard = useCallback(\n (deletedBoardId) => {\n setSelectedProjectId(boards.length > 0 ? boards[0][0] : undefined);\n setBoards((previousBoards) =>\n previousBoards.filter((board) => board[0] !== deletedBoardId),\n );\n },\n [setBoards, setSelectedProjectId, boards],\n);\n\nconst addCreateBoardStatus = useCallback((newStatus) => {\n let isNew = true;\n let newCreateBoardStatuses = createBoardStatus.map(\n (status) => {\n if (status.name === newStatus.name) {\n isNew = false;\n return newStatus;\n } else {\n return status;\n }\n setCreateBoardStatus(newCreateBoardStatuses);\n },\n [createBoardStatus],\n );\n});\n\nuseEffect(() => {\n createBoardStatus.forEach((board, index) => {\n if (board.status === 'Saved' || board.status === 'Error') {\n const timer = setTimeout(() => {\n const newStatus = createBoardStatus.slice();\n newStatus.splice(index, 1);\n setCreateBoardStatus(newStatus);\n getBoards();\n }, 3000);\n return () => clearTimeout(timer);\n }\n });\n}, [createBoardStatus]);\n\nconst isApplicationUser = (accountId, users) => {\n return users.includes(accountId);\n};\n\nconst onChangeShowEditBoardDialog = (open, board) => {\n setShowEditBoardDialog(open);\n setEditedBoard(board);\n};\n\nconst removeBoardMember = useCallback(\n (member) => {\n setBoardMembers(boardMembers.filter((m) => m.id !== member.id));\n },\n [boardMembers, setBoardMembers],\n);\n\nconst getBoards = useCallback(async () => {\n try {\n Near.fakCalimeroCall(contract, 'get_members_projects', {\n member_id: accountId,\n }).then((boards) => {\n setBoards(boards);\n setSelectedProjectId(boards.length > 0 ? boards[0][0] : undefined);\n });\n } catch (e) {\n console.log('getBoards', e);\n }\n}, [contract, accountId]);\n\nconst getUsers = useCallback(async () => {\n try {\n Near.asyncCalimeroView(contract, 'get_application_members', {}).then(\n (usersResponse) => {\n setUsers(usersResponse.map((user) => ({ id: user })));\n if (!isApplicationUser(accountId, usersResponse)) {\n Near.fakCalimeroCall(contract, 'add_application_member', {\n new_member: accountId,\n });\n }\n },\n );\n } catch (e) {\n console.log('getUsers', e);\n }\n}, [contract, accountId]);\n\nuseEffect(() => {\n getBoards();\n getUsers();\n}, [accountId]);\n\nreturn (\n <>\n {showEditBoardDialog && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.EditBoardDialog`}\n props={{\n createBoard,\n onClose: () => onChangeShowEditBoardDialog(false, undefined),\n functionLoader,\n onChangeEditBoardName,\n boardName,\n editedBoard,\n componentOwnerId,\n selectedProjectId,\n users,\n selectedUser,\n setSelectedUser,\n addActionStatus,\n contract,\n updateBoardName,\n deleteBoard,\n onChangeShowEditBoardDialog,\n }}\n />\n )}\n <SideMenuContainer>\n <AddBoardsContainer>\n <AddBoardText>Your boards</AddBoardText>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.CreateBoardDialog`}\n props={{\n addCreateBoardStatus,\n componentOwnerId,\n accountId,\n handleBoards,\n contract,\n createBoardButton,\n addBoard,\n }}\n />\n </AddBoardsContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.BoardList`}\n props={{\n componentOwnerId,\n selectedProjectId,\n setSelectedProjectId,\n boards,\n onEditBoardClick: () => {},\n editBoard: () => {},\n onChangeShowEditBoardDialog,\n createBoardStatus,\n }}\n />\n </SideMenuContainer>\n </>\n);\n" }, "Calimero.Curb.Popups.InputPopup": { "": "const OverlayContainer = styled.div`\n left: 0px;\n right: 0px;\n bottom: 0px;\n top: 0px;\n position: absolute;\n width: 100%;\n height: 100vh;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n align-items: center;\n @media (max-width: 1024px) {\n height: 100vh;\n }\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n border-radius: 8px;\n width: 489px;\n @media (max-width: 1024px) {\n width: 90%;\n }\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #5765f2;\n :hover {\n background-color: #717cf0;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n position: absolute;\n right: 1rem;\n cursor: pointer;\n`;\nconst [inputValue, setInputValue] = useState('');\n\nconst handleInputChange = useCallback((e) => {\n setInputValue(e.target.value);\n}, []);\nconst handleSubmit = () => {\n props.handleClickEvent(inputValue);\n};\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <CloseButton onClick={props.handleClosePopup}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n <Text>{props.title}</Text>\n <Input onChange={handleInputChange} placeholder={props.placeholder} />\n <FunctionButton onClick={handleSubmit}>\n {props.functionLoader ? (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n props.buttonText\n )}\n </FunctionButton>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.DocsChain.ArticleDialog.DocumentHeader": { "": "const contract = props.contract || 'docs.testnet';\nconst componentOwnerId = props.componentOwnerId || 'fran-cali.testnet';\n\nconst Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n pading-left: 35px;\n padding-right: 35px;\n`;\n\nconst AddButton = styled.div`\n width: 24px;\n height: 24px;\n border-radius: 50%;\n display: flex;\n justify-content: center;\n font-size: 1.5rem;\n color: #4e95ff;\n align-items: center;\n border: solid 1px #0e0e10;\n :hover {\n border: solid 1px #fff;\n }\n`;\n\nconst Text = styled.div`\n color: #797978;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n`;\n\nconst IconPlus = styled.div`\n :hover {\n border: solid 1px #fff;\n }\n cursor: pointer;\n width: 24px;\n height: 24px;\n background-color: #4e95ff;\n color: #fff;\n margin-left: 0.5rem;\n display: flex;\n justify-content: center;\n align-items: center;\n border-radius: 50%;\n padding-top: 2px;\n`;\n\nconst Icon = styled.div`\n color: #797978;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n`;\n\nState.init({\n userList: [],\n userDialogOpen: false,\n openUserDialog: () => State.update({ userDialogOpen: true }),\n});\n\nconst setUserList = () =>\n Near.asyncCalimeroView(\n contract,\n 'get_accounts_paged',\n {},\n undefined,\n false,\n ).then((users) => {\n const userList = users.map((user) => user[0]);\n State.update({ userList: userList });\n });\n\nuseEffect(() => {\n setUserList();\n}, [state.userList]);\n\nconst OptionsContainer = styled.div`\n display: flex;\n gap: 1rem;\n align-items: center;\n`;\n\nreturn (\n <Container>\n <div className=\"d-flex gap-2\">\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.UsersButtonGroup`}\n props={{\n componentOwnerId: componentOwnerId,\n channelUserList: state.userList,\n openMemberList: state.openUserDialog,\n }}\n />\n <IconPlus onClick={() => console.log('add user dialog')}>\n <i className=\"bi bi-plus-lg\" />\n </IconPlus>\n </div>\n <OptionsContainer>\n <div className=\"d-flex gap-2\">\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.Common.Button`}\n props={{\n onClick: () => console.log('save'),\n text: 'Save',\n color: '#4E95FF',\n hoverColor: '#267DFF',\n width: '80px',\n }}\n />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.Common.Button`}\n props={{\n onClick: () => console.log('preview'),\n text: 'Preview',\n width: '80px',\n }}\n />\n </div>\n <div className=\"d-flex gap-4\">\n <Text>\n Last edited\n {props.lastEdit ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.TimeFormatting`}\n props={{\n time: (Date.now() - props.lastEdit) / 1000,\n }}\n />\n ) : (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.TimeFormatting`}\n props={{\n time: Date.now() - Date.now(),\n }}\n />\n )}\n </Text>\n <div className=\"d-flex gap-3\">\n <Icon>\n <i className=\"bi bi-share\"></i>\n </Icon>\n <Icon>\n <i className=\"bi bi-trash3\"></i>\n </Icon>\n </div>\n </div>\n </OptionsContainer>\n </Container>\n);\n" }, "Calimero.TaskChain.ActionStatus.StatusContainer": { "": "const { actionStatuses, componentOwnerId, setActionStatusNotVisible } = props;\n\nconst StatusContainer = styled.div`\n position: absolute;\n top: 1rem;\n right: 1rem;\n align-items: center;\n justify-content: center;\n`;\n\nconst newActionStatuses = actionStatuses.filter(\n (actionStatus) => !actionStatus.seen,\n);\n\nreturn (\n newActionStatuses.length > 0 && (\n <StatusContainer>\n {newActionStatuses.map((actionStatus, index) => (\n <Widget\n key={actionStatus.id + index}\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ActionStatus.Status`}\n props={{\n actionStatus,\n setActionStatusNotVisible,\n }}\n />\n ))}\n </StatusContainer>\n )\n);\n" }, "Calimero.TaskChain.ConfirmationPopup": { "": "const title = props.title ?? 'Are you sure?';\nconst deleteButtonText = props.deleteButtonText ?? 'Delete';\nconst closeButtonText = props.closeButtonText ?? 'Close';\nconst onClick = props.onClick;\nconst onClose = props.onClose;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 30;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n width: 60%;\n justify-content: center;\n text-align: center;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n padding-bottom: 10px;\n`;\n\nconst TextContainer = styled.div`\n display: flex;\n justify-content: center;\n width: 100%;\n`;\n\nconst Input = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n`;\n\nconst FunctionButton = styled.button`\n background-color: red;\n :hover {\n opacity: 0.8;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n background-color: transparent;\n display: flex;\n justify-content: center;\n color: #6b7280;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <TextContainer>\n <Text>{title}</Text>\n </TextContainer>\n <FunctionButton onClick={onClick}>{deleteButtonText}</FunctionButton>\n <CloseButton onClick={onClose}>{closeButtonText}</CloseButton>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.TaskChain.JoinApp": { "": "const { contractName, componentOwnerId } = props;\n\nconst OverlayContainer = styled.div`\n display: flex;\n width: 100%;\n height: 100%;\n background-color: #0e0e10;\n justify-content: center;\n align-items: center;\n padding: 100px;\n`;\n\nconst PopupContainer = styled.div`\n background-color: #1e1f28;\n padding: 1rem;\n border-radius: 8px;\n width: 489px;\n height: 200px;\n`;\n\nconst JoinContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n text: center;\n`;\n\nconst JoinHeader = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst user = context.accountId;\n\nconst joinTaskChain = useCallback(\n () =>\n Near.requestCalimeroFak(contractName)\n .then((res) => {\n console.log('requestCalimeroFak', res);\n })\n .then(() => {\n Near.asyncCalimeroView(\n contractName,\n 'get_application_members',\n {},\n ).then((res) => {\n if (user in res) {\n return Near.asyncCalimeroCall(\n contractName,\n 'add_application_member',\n {\n member: user,\n },\n );\n }\n });\n }),\n [],\n);\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <JoinContainer>\n <JoinHeader>\n <div className=\"pt-3\" />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.TaskChainLogo`}\n props={{\n justify: true,\n }}\n />\n <div className=\"pt-3\" />\n <Text>Join Boards</Text>\n <div className=\"pt-3\" />\n <FunctionButton onClick={joinTaskChain}>Join</FunctionButton>\n </JoinHeader>\n </JoinContainer>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.TaskChain.BoardContainer.Board": { "": "const {\n projectId,\n setSelectedProjectId,\n contract,\n status,\n componentOwnerId,\n addActionStatus,\n columnIndex,\n} = props;\n\nconst TopBar = styled.div`\n width: 100%;\n height: 50px;\n display: flex;\n flex-direction: row;\n`;\n\nconst AddColumnContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n`;\n\nconst HorizontalFlexContainer = styled.div`\n display: flex;\n flex-direction: row;\n overflow-x: scroll;\n overflow-y: scroll;\n height: 100%;\n width: 100%;\n scroll-behavior: smooth;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst AddColumnButton = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n background-color: transparent;\n margin-top: 1.2rem;\n color: #6b7280;\n width: 12rem;\n gap: 4px;\n border-radius: 4px;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n`;\n\nconst AddIcon = styled.div``;\n\nconst AddColumnText = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n margin-left: 0.25rem;\n`;\n\nconst LoadingContainer = styled.div`\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst addColumnButton = (\n <AddColumnButton>\n <AddIcon>\n <i className=\"bi bi-plus-circle-fill\"></i>\n </AddIcon>\n <AddColumnText>Add new Column</AddColumnText>\n </AddColumnButton>\n);\n\nconst [columns, setColumns] = useState([]);\nconst [draggingTask, setDraggingTask] = useState(null);\nconst [saveInProgress, setSaveInProgress] = useState(false);\nconst [draggingColumn, setDraggingColumn] = useState(null);\nconst [columnsLoading, setColumnsLoading] = useState(false);\n\nconst addNewColumn = useCallback(\n (newColumn) => {\n setColumns([...columns, newColumn]);\n },\n [columns],\n);\n\nconst removeColumn = useCallback(\n (columnIndex) => {\n setColumns((prevColumns) =>\n prevColumns.filter((_, idx) => idx !== columnIndex),\n );\n },\n [columns, setColumns],\n);\n\nconst addTaskToColumn = useCallback(\n (columnIndex, newTask) => {\n setColumns((previousColumns) => {\n const updatedColumns = [...previousColumns];\n updatedColumns[columnIndex] = {\n ...updatedColumns[columnIndex],\n tasks: [...updatedColumns[columnIndex].tasks, newTask],\n };\n return updatedColumns;\n });\n },\n [setColumns],\n);\n\nconst updateTaskInColumn = useCallback(\n (columnIndex, updatedTask) => {\n setColumns((previousColumns) => {\n const updatedColumns = [...previousColumns];\n updatedColumns[columnIndex] = {\n ...updatedColumns[columnIndex],\n tasks: updatedColumns[columnIndex].tasks.map((task) =>\n task.id === updatedTask.id ? updatedTask : task,\n ),\n };\n return updatedColumns;\n });\n },\n [setColumns],\n);\n\nconst removeTaskFromColumn = useCallback(\n (taskId, columnIndex) => {\n setColumns(\n columns.map((col, idx) =>\n idx === columnIndex\n ? { ...col, tasks: col.tasks.filter((task) => task.id !== taskId) }\n : col,\n ),\n );\n },\n [columns, setColumns],\n);\n\nconst submitComment = (task_id, message) => {\n Near.fakCalimeroCall(contract, 'comment_on_task', {\n project_id,\n task_id,\n message,\n });\n};\n\nconst onTaskDragStart = (id, title, originalStatus, originalIndex) => {\n try {\n setDraggingTask({ id, title, originalStatus, originalIndex });\n } catch (e) {\n console.log('onTaskDragStart', e);\n }\n};\n\nconst onTaskDragStop = (newStatus, index) => {\n const actionStatusPrefix = 'Task drag';\n let newActionStatus = {\n id:\n actionStatusPrefix +\n draggingTask.id +\n draggingTask.originalStatus +\n draggingTask.originalIndex,\n status: `Saving task ${draggingTask.title} to status ${newStatus}`,\n };\n\n try {\n addActionStatus(newActionStatus);\n // Create a deep copy of the statuses\n let statusesCopy = JSON.parse(JSON.stringify(columns));\n\n // Access and remove the task from the original status using the original index\n const originalStatusObj = statusesCopy.find(\n (status) => status.name === draggingTask.originalStatus,\n );\n\n const newStatusIndex = statusesCopy.findIndex(\n (status) => status.name === newStatus,\n );\n\n if (!originalStatusObj) {\n console.log('Original status not found: ', draggingTask.originalStatus);\n return;\n }\n\n const removedTask = originalStatusObj.tasks.splice(\n draggingTask.originalIndex,\n 1,\n )[0];\n\n // If the task was not found, you can choose how to handle it. In this case, we just log an error and return.\n if (!removedTask) {\n console.log('Task not found: ', draggingTask);\n return;\n }\n\n // Add the task to the new status at the given index\n const newStatusObj = statusesCopy.find(\n (status) => status.name === newStatus,\n );\n if (!newStatusObj) {\n console.log('New status not found: ', newStatus);\n return;\n }\n const newIndex = index ?? newStatusObj.tasks.length;\n\n newStatusObj.tasks.splice(newIndex, 0, removedTask);\n\n // Update the state with the modified statuses\n setSaveInProgress(true);\n\n Near.fakCalimeroCall(contract, 'move_task', {\n project_id: projectId,\n task_id: draggingTask.id,\n to_status_index: newStatusIndex,\n position: newIndex,\n }).then(() => {\n newActionStatus.status = `Saved task ${draggingTask.title} to status ${newStatus}`;\n addActionStatus(newActionStatus);\n setSaveInProgress(false);\n console.log('contract updated');\n });\n\n setDraggingTask(null);\n setColumns(statusesCopy);\n } catch (e) {\n newActionStatus.status = `Error saving task ${draggingTask.title} to status ${newStatus}`;\n addActionStatus(newActionStatus);\n console.log('onTaskDragStop', e);\n }\n};\n\nconst onTaskDragOver = () => {\n try {\n } catch (e) {\n console.log('onTaskDragOver', e);\n }\n};\n\nconst onColumnDragStart = (index, name) => {\n try {\n setDraggingColumn({ index, name });\n } catch (e) {\n console.log('onColumnDragStart', e);\n }\n};\n\nconst onColumnDragStop = (newPositionIndex) => {\n if (newPositionIndex === draggingColumn.index || draggingTask) {\n return;\n }\n const actionStatusPrefix = 'Column drag';\n let newActionStatus = {\n id: actionStatusPrefix + draggingColumn.name + draggingColumn.index,\n status: `Saving column ${draggingColumn.name} to new position ${\n newPositionIndex + 1\n }`,\n seen: false,\n };\n\n try {\n addActionStatus(newActionStatus);\n\n let statusDataCopy = swapByName(\n columns,\n draggingColumn.index,\n newPositionIndex,\n );\n setColumns(statusDataCopy);\n\n Near.fakCalimeroCall(contract, 'reorder_statuses', {\n project_id: projectId,\n old_position: draggingColumn.index,\n new_position: newPositionIndex,\n }).then(() => {\n //updating status\n newActionStatus.status = `Saved column ${\n draggingColumn.name\n } to new position ${newPositionIndex + 1}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n //updating status\n newActionStatus.status = `Error saving column ${\n draggingColumn.name\n } to new position ${newPositionIndex + 1}`;\n addActionStatus(newActionStatus);\n }\n};\n\nconst swapByName = (arr, index1, index2) => {\n let temp = arr[index1];\n arr[index1] = arr[index2];\n arr[index2] = temp;\n return arr;\n};\n\nconst createTask = useCallback(\n async (projectId, taskTitle, taskDescription) => {\n try {\n return Near.fakCalimeroCall(contract, 'create_task', {\n project_id: projectId,\n title: taskTitle,\n description: taskDescription,\n labels: [],\n });\n } catch (e) {\n console.log('createTask', e);\n }\n },\n [],\n);\n\nconst getStatuses = useCallback(async (projectId) => {\n setColumnsLoading(true);\n try {\n Near.asyncCalimeroView(contract, 'get_statuses', {\n project_id: projectId,\n }).then((data) => {\n setColumns(data);\n setColumnsLoading(false);\n });\n } catch (e) {\n console.log('getStatuses', e);\n }\n}, []);\n\nuseEffect(() => {\n if (projectId) {\n getStatuses(projectId);\n }\n}, [projectId]);\n\nreturn (\n <>\n {columnsLoading ? (\n <LoadingContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 128 }}\n />\n </LoadingContainer>\n ) : (\n <HorizontalFlexContainer>\n {columns.map((column, columnIndex) => {\n return (\n <div\n key={columnIndex}\n draggable=\"true\"\n droppable=\"true\"\n value={columnIndex}\n onDragStart={() => onColumnDragStart(columnIndex, column.name)}\n onDrop={() => onColumnDragStop(columnIndex)}\n >\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Column`}\n props={{\n columnIndex,\n componentOwnerId,\n title: column.name,\n tasks: column.tasks,\n submitComment: submitComment,\n onTaskDragStop: onTaskDragStop,\n onTaskDragStart: onTaskDragStart,\n onTaskDragOver: onTaskDragOver,\n createStatus,\n createTask,\n projectId,\n contract,\n setColumns,\n columns,\n addActionStatus,\n removeColumn,\n removeTaskFromColumn,\n addTaskToColumn,\n updateTaskInColumn,\n }}\n />\n </div>\n );\n })}\n <AddColumnContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.AddColumnPopup`}\n props={{\n componentOwnerId,\n addNewColumn,\n addColumnButton,\n addColumn,\n addActionStatus,\n contract,\n projectId,\n }}\n />\n </AddColumnContainer>\n </HorizontalFlexContainer>\n )}\n </>\n);\n" }, "Calimero.Common.Popups.AutoRefresh": { "": "const OverlayContainer = styled.div`\n left: 0px;\n right: 0px;\n bottom: 0px;\n top: 0px;\n position: fixed;\n z-index: 20;\n display: flex;\n background-color: #0e0e10;\n justify-content: center;\n align-items: center;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n border-radius: 8px;\n width: 489px;\n @media (max-width: 1024px) {\n width: 90%;\n }\n`;\n\nconst LoadingContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;zz\n text: center;\n`;\n\nconst LoadingHeader = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n @media (max-width: 1024px) {\n font-size: 20px;\n }\n`;\n\nsetTimeout(() => {\n console.log('autorefresh');\n props.refresh();\n}, 3000);\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <LoadingContainer>\n <LoadingHeader>\n {props.logo}\n <Text>Loading...</Text>\n </LoadingHeader>\n </LoadingContainer>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.Curb.EventEmitter": { "": "const render = props.render;\n\nfunction EventEmitter() {\n let store = new Map();\n\n let register = (persist, append) => (event, listener, isExclusive) => {\n isExclusive = !!isExclusive;\n if (typeof listener !== 'function') throw 'listener is not a function';\n let listeners = store.get(event) || store.set(event, []).get(event);\n if (\n listeners.length &&\n (isExclusive || listeners.some((entry) => entry.isExclusive))\n )\n throw \"Can't add listener to exclusive event\";\n listeners[append ? 'push' : 'unshift']({\n persist,\n listener,\n isExclusive,\n });\n };\n\n return {\n on: register(true, false),\n once: register(false, false),\n prependListener: register(true, true),\n prependOnceListener: register(false, true),\n off: (event, listener) => {\n if (typeof listener !== 'function') throw 'listener is not a function';\n let idx,\n listeners = store.get(event);\n if (\n listeners &&\n -1 < (idx = listeners.findIndex((entry) => entry.listener === listener))\n ) {\n listeners.splice(idx, 1);\n if (!listeners.length) store.delete(event);\n }\n },\n emit: (event, data) => {\n let listeners = store.get(event);\n if (listeners) {\n listeners = listeners.filter((entry) => {\n entry.listener(data);\n return entry.persist;\n });\n if (!listeners.length) store.delete(event);\n else store.set(event, listeners);\n }\n },\n };\n}\n\nreturn render({ EventEmitter });\n" }, "Calimero.Curb.ChannelsContainer": { "": "const curbApi = props.curbApi;\nconst componentOwnerId = props.componentOwnerId;\nconst onChatSelected = props.onChatSelected;\nconst activeChat = props.activeChat;\nconst isSidebarOpen = props.isSidebarOpen;\nconst setIsSidebarOpen = props.setIsSidebarOpen;\nconst isWsConnectionActive = props.isWsConnectionActive;\nconst wsApi = props.wsApi;\nconst enableCommunities = props.enableCommunities;\nconst handleContractChange = props.handleContractChange;\nconst selectedCommunity = props.selectedCommunity;\n\nconst communitiesList = props.communities;\n\nconst [channels, setChannels] = useState([]);\nconst [users, setUsers] = useState([]);\nconst [communities, setCommunities] = useState([]);\nconst [cachedUsers, setCachedUsers] = useState([]);\n\nconst usersRef = useRef(cachedUsers);\n\nconst generator = () =>\n Promise.all([\n curbApi.getChannels(),\n curbApi.getUnreadMessages(),\n ...(enableCommunities ? [curbApi.getCommunities(communitiesList)] : []),\n ]);\n\nconst [cachedChannels, cachedUnread, cachedCommunities] = useCache(\n generator,\n 'channel_container',\n { subscribe: true },\n);\n\nuseEffect(() => {\n usersRef.current = cachedUsers;\n}, [cachedUsers]);\n\nconst peerStatusChangeCb = (newStatus) => (event) => {\n const updateUserStatus = (userId, isActive) => {\n const user = usersRef.current.find((u) => u.id === userId);\n if (!user || user.active === newStatus) return;\n\n const updatedUsers = usersRef.current.map((u) =>\n u.id === userId ? { ...u, active: isActive } : u,\n );\n setCachedUsers(updatedUsers);\n };\n\n const { account_id: accountId } = event;\n updateUserStatus(accountId, newStatus === 'online');\n};\n\nuseEffect(() => {\n const peerConnectedCb = peerStatusChangeCb('online');\n const peerDisconnectedCb = peerStatusChangeCb('offline');\n const channelMessageCb = (event) => {\n if (event.receiver.type === 'account') {\n const senderId = event.sender;\n const senderExists = usersRef.current.some((u) => u.id === senderId);\n if (!senderExists) {\n const newUser = {\n id: senderId,\n active: true, // we just received a message from this user, so they are online\n type: 'direct_message',\n };\n setCachedUsers((prevUsers) => [...prevUsers, newUser]);\n }\n }\n };\n\n wsApi.notifications.on('PeerConnected', peerConnectedCb);\n wsApi.notifications.on('PeerDisconnected', peerDisconnectedCb);\n wsApi.notifications.on('ChannelMessage', channelMessageCb);\n return () => {\n wsApi.notifications.off('PeerConnected', peerConnectedCb);\n wsApi.notifications.off('PeerDisconnected', peerDisconnectedCb);\n wsApi.notifications.off('ChannelMessage', channelMessageCb);\n };\n}, []);\n\nuseEffect(() => {\n if (isWsConnectionActive) {\n curbApi\n .getDMs()\n .then((accounts) => curbApi.getAccountsStatus(accounts))\n .then((result) =>\n Object.entries(result.status).map(([accountId, status]) => ({\n id: accountId,\n active: status === 'online',\n type: 'direct_message',\n })),\n )\n .then((users) => {\n setCachedUsers(users);\n });\n }\n}, [isWsConnectionActive]);\n\nuseEffect(() => {\n const usersWN = (cachedUsers ?? []).map((u) => ({\n ...u,\n unreadMessages: cachedUnread.chats[u.id] || 0,\n }));\n\n const channelsWN = (cachedChannels ?? []).map((c) => ({\n ...c,\n unreadMessages: cachedUnread.channels[c.name] || 0,\n }));\n\n setUsers(usersWN);\n setChannels(channelsWN);\n enableCommunities && setCommunities(cachedCommunities);\n}, [cachedUsers, cachedChannels, cachedUnread, cachedCommunities]);\n\nconst selectChannel = useCallback(\n (chatSelected) => {\n const chat = { type: chatSelected.type };\n if (chatSelected.type === 'channel') {\n chat.name = chatSelected.name;\n chat.readOnly = chatSelected.readOnly;\n } else {\n // we add a new DM, it will need to fetch the status of the other user\n if (!users.some((user) => user.id === chatSelected.id)) {\n setCachedUsers((prevUsers) => [...prevUsers, chatSelected]);\n }\n chat.id = chatSelected.id;\n }\n onChatSelected(chat);\n },\n [users],\n);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.SideSelector.SideSelector`}\n props={{\n curbApi,\n componentOwnerId,\n onChatSelected: selectChannel,\n activeChat,\n users,\n channels,\n isSidebarOpen,\n setIsSidebarOpen,\n communities,\n enableCommunities,\n handleContractChange,\n selectedCommunity,\n }}\n />\n);\n" }, "Calimero.DocsChain.Sidebar.AllArticlesList": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst Container = styled.div`\n background-color: #0e0e10;\n width: 317px;\n`;\n\nconst Item = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n &:hover {\n color: #ffffff;\n }\n a {\n text-decoration: none;\n color: inherit;\n\n &:hover {\n color: #ffffff;\n }\n }\n :hover {\n background-color: #25252a;\n }\n cursor: pointer;\n ${({ selected }) =>\n selected\n ? 'color: #fff; background-color: #25252a;'\n : 'color: #777583; background-color: #0E0E10;'}\n`;\n\nconst TextMedium = styled.div`\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n padding-top: 1px;\n`;\n\nconst NameContainer = styled.a`\n display: flex;\n justify-content: start;\n align-items: center;\n gap: 12px;\n width: 100%;\n text-decoration: none;\n color: #777583;\n &:hover,\n &:active,\n &:visited {\n text-decoration: none;\n color: #777583;\n }\n`;\n\nconst CircleIcon = styled.div`\n border-radius: 50%;\n width: 16px;\n height: 16px;\n background-color: #4e95ff;\n`;\n\nconst getDateLastEdit = (timestamp) => {\n const date = new Date(Number(timestamp) / 1e6);\n const dateString = `${date.toLocaleDateString()} / ${date.toLocaleTimeString()}`;\n return dateString;\n};\n\nconst allArticles = Near.calimeroView(\n contract,\n 'get_article_ids_paged',\n {},\n).map((articleId) => {\n return {\n id: articleId,\n data: Near.calimeroView(contract, 'get_article', { article_id: articleId }),\n };\n});\n\nreturn (\n <Container>\n {(allArticles ?? []).map((article) => (\n <Item key={article.id} selected={props.selectedArticleId === article.id}>\n <NameContainer\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.ArticleView?articleId=${article.id}&blockHeight=${article.data.blockHeight}&lastEditor=${article.data.author})`,\n )}\n >\n {article.body.icon ? <p>{article.body.icon}</p> : <CircleIcon />}\n <TextMedium>\n {article.data.body.split('\\n')[0].replace('# ', '')}\n </TextMedium>\n </NameContainer>\n </Item>\n ))}\n </Container>\n);\n" }, "Calimero.Curb.SideSelector.ChannelsHeader": { "": "const Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n color: #777583;\n :hover {\n color: #ffffff;\n }\n @media (max-width: 1024px) {\n width: 100%;\n padding-top: 1.25rem;\n }\n`;\n\nconst TextBold = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n`;\nconst IconPlusContainer = styled.div`\n display: flex;\n cursor: pointer;\n justify-content: center;\n align-items: center;\n font-size: 1.25rem;\n`;\n\nconst isValidChannelName = useCallback((value) => {\n const regex = /^[^#!\\s]{3,19}$/;\n let error = null;\n const isValid = regex.test(value);\n\n if (value.match(/[!#.\\s]/)) {\n error = 'Channel name must not contain special charters or links.';\n } else if (value.length < 3) {\n error = 'Channel name is too short. It should be at least 3 characters.';\n } else if (value.length > 19) {\n error = 'Channel name is too long. It should be at most 19 characters.';\n } else {\n error = 'Invalid channel name.';\n }\n\n return { isValid, error };\n}, []);\n\nconst CreateChannelPopup = (props) => (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.Popups.CreateChannelPopup`}\n props={{\n componentOwnerId: props.componentOwnerId,\n title: 'Create new Channel',\n placeholder: '# channel name',\n buttonText: 'Create',\n colors: {\n base: '#5765f2',\n hover: '#717cf0',\n disabled: '#3B487A',\n },\n toggle: (\n <IconPlusContainer>\n <i className=\"bi bi-plus-circle\" />\n </IconPlusContainer>\n ),\n createChannel: (channelName, isPublic, isReadOnly) =>\n props.curbApi.createGroup(channelName, isPublic, isReadOnly, accountId),\n channelNameValidator: isValidChannelName,\n }}\n />\n);\n\nreturn (\n <Container>\n <TextBold>{props.title}</TextBold>\n <CreateChannelPopup\n curbApi={props.curbApi}\n componentOwnerId={props.componentOwnerId}\n />\n </Container>\n);\n" }, "Calimero.Curb.ProfileIcon.NftImage": { "": "const {\n className: propClassName,\n style,\n alt,\n nft: propNft,\n thumbnail,\n fallbackUrl,\n} = props;\n\nconst [state, setState] = useState({\n contractId: propNft?.contractId,\n tokenId: propNft?.tokenId,\n loadingUrl:\n 'https://ipfs.near.social/ipfs/bafkreidoxgv2w7kmzurdnmflegkthgzaclgwpiccgztpkfdkfzb4265zuu',\n imageUrl: null,\n oldUrl: null,\n});\n\nconst nftMetadata =\n propNft.contractMetadata ?? Near.view(state.contractId, 'nft_metadata');\nconst tokenMetadata =\n propNft.tokenMetadata ??\n Near.view(state.contractId, 'nft_token', {\n token_id: state.tokenId,\n }).metadata;\n\nlet imageUrl = null;\n\nif (nftMetadata && tokenMetadata) {\n let tokenMedia = tokenMetadata.media || '';\n\n imageUrl =\n tokenMedia.startsWith('https://') ||\n tokenMedia.startsWith('http://') ||\n tokenMedia.startsWith('data:image')\n ? tokenMedia\n : nftMetadata.base_uri\n ? `${nftMetadata.base_uri}/${tokenMedia}`\n : tokenMedia.startsWith('Qm') || tokenMedia.startsWith('ba')\n ? `https://ipfs.near.social/ipfs/${tokenMedia}`\n : tokenMedia;\n\n if (!tokenMedia && tokenMetadata.reference) {\n if (\n nftMetadata.base_uri === 'https://arweave.net' &&\n !tokenMetadata.reference.startsWith('https://')\n ) {\n const res = fetch(`${nftMetadata.base_uri}/${tokenMetadata.reference}`);\n imageUrl = res.body.media;\n } else if (\n tokenMetadata.reference.startsWith('https://') ||\n tokenMetadata.reference.startsWith('http://')\n ) {\n const res = fetch(tokenMetadata.reference);\n imageUrl = JSON.parse(res.body).media;\n } else if (tokenMetadata.reference.startsWith('ar://')) {\n const res = fetch(\n `${'https://arweave.net'}/${tokenMetadata.reference.split('//')[1]}`,\n );\n imageUrl = JSON.parse(res.body).media;\n }\n }\n\n if (!imageUrl) {\n imageUrl = false;\n }\n setState((prevState) => ({\n ...prevState,\n imageUrl: imageUrl,\n }));\n}\n\nconst replaceIpfs = useCallback(\n (imageUrl) => {\n const rex =\n /^(?:https?:\\/\\/)(?:[^\\/]+\\/ipfs\\/)?(Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,})(?:\\.[^\\/]+)?(\\/.*)?$/g;\n\n if (state.oldUrl !== imageUrl && imageUrl) {\n const match = rex.exec(imageUrl);\n if (match) {\n const newImageUrl = `https://ipfs.near.social/ipfs/${match[1]}${\n match[2] || ''\n }`;\n if (newImageUrl !== imageUrl) {\n setState((prevState) => ({\n ...prevState,\n oldUrl: imageUrl,\n imageUrl: newImageUrl,\n }));\n return;\n }\n }\n }\n if (state.imageUrl !== false) {\n setState((prevState) => ({\n ...prevState,\n imageUrl: false,\n }));\n }\n },\n [state],\n);\n\nconst thumb = (imageUrl) =>\n thumbnail && imageUrl && !imageUrl.startsWith('data:image/')\n ? `https://i.near.social/${thumbnail}/${imageUrl}`\n : imageUrl;\n\nconst img = state.imageUrl !== null ? state.imageUrl : imageUrl;\nconst src = img !== false ? img : fallbackUrl;\n\nreturn (\n <img\n className={propClassName || 'img-fluid'}\n style={style}\n src={src !== null ? thumb(src) : state.loadingUrl}\n alt={alt}\n onError={() => replaceIpfs(img)}\n />\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.TaskCard": { "": "const {\n task,\n status,\n index,\n onViewTaskDetails,\n onTaskDragStop,\n onTaskDragStart,\n createStatus,\n} = props;\n\nconst TaskCardContainer = styled.div`\n display: flex;\n flex-direction: column;\n padding: 16px 16px 2px 16px;\n border-radius: 4px;\n width: 15rem;\n align-items: flex-start;\n justify-content: center;\n background-color: #1e1f28;\n color: white;\n ${({ enabled }) => (enabled ? 'cursor: pointer;' : 'cursor: not-allowed')}\n`;\n\nconst TaskTitle = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n padding-top: 0.25rem;\n width: 100%;\n`;\n\nconst Text = styled.div`\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 100%;\n`;\n\nconst ImageContainer = styled.div`\n align-items: center;\n justify-content: center;\n height: 100px;\n overflow: hidden;\n display: flex;\n`;\n\nconst TaskFooter = styled.div`\n display: flex;\n justify-content: space-between;\n color: #6b7280;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n width: 100%;\n`;\n\nconst Label = styled.div`\n height: 8px;\n border-radius: 200px;\n width: 42px;\n ${({ backgroundColor }) =>\n backgroundColor && `background-color: ${backgroundColor};`}\n`;\n\nconst LabelContainer = styled.div`\n gap: 4px;\n padding-bottom: 4px;\n display: flex;\n`;\n\nconst AssigneeIcon = styled.div`\n display: flex;\n gap: 2px;\n`;\n\nconst TaskImage = ({ imageSrc }) => (\n <Widget src={imageSrc} props={{ style: { width: '32px', height: '32px' } }} />\n);\n\nconst taskImage = task.files?.[0] ?? null;\n\nconst savingStatus = createStatus.find((s) => s.title === task.title);\n\nreturn (\n <>\n <TaskCardContainer\n draggable\n onDragStart={() => {\n if (task.id) {\n onTaskDragStart(task.id, task.title, props.status, index);\n }\n }}\n onDrop={() => {\n if (task.id) {\n onTaskDragStop(props.status, index);\n }\n }}\n onClick={onClick}\n onClick={() => {\n if (task.id) {\n onViewTaskDetails(task);\n }\n }}\n enabled={task.id}\n >\n {savingStatus && <p>{savingStatus.status}</p>}\n {task.labels.length > 0 && (\n <LabelContainer>\n {task.labels.map((label, index) => (\n <Label key={index} backgroundColor={label.color} />\n ))}\n </LabelContainer>\n )}\n {taskImage && (\n <ImageContainer>\n <TaskImage imageSrc={taskImage} />\n </ImageContainer>\n )}\n <TaskTitle>\n <Text>{task.title}</Text>\n </TaskTitle>\n <TaskFooter>\n <p>{task.id}</p>\n {task.assignee && (\n <AssigneeIcon>\n <i className=\"bi bi-person-fill\"></i>\n <p>{task.assignee}</p>\n </AssigneeIcon>\n )}\n </TaskFooter>\n </TaskCardContainer>\n </>\n);\n" }, "Calimero.Curb.SideSelector.ChannelList": { "": "const ChannelListContainer = styled.div`\n background-color: #0e0e10;\n @media (max-width: 1024px) {\n width: 100%;\n }\n`;\n\nconst ChannelListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n cursor: pointer;\n @media (min-width: 1024px) {\n &:hover {\n background-color: #25252a !important;\n color: #fff !important;\n fill: #fff !important;\n }\n }\n @media (max-width: 1024px) {\n &:active {\n background-color: #25252a !important;\n color: #fff !important;\n fill: #fff !important;\n }\n }\n`;\n\nconst TextMedium = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n`;\n\nconst NameContainer = styled.div`\n display: flex;\n gap: 8px;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst IconWrapper = styled.div`\n height: 24px;\n width: 24px;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nreturn (\n <ChannelListContainer>\n {props.channels.map((channel) => (\n <ChannelListItem\n key={channel.name}\n selected={props.selectedChannelId === channel.name}\n onClick={() => props.selectChannel(channel)}\n style={{\n backgroundColor:\n props.selectedChannelId === channel.name ? '#25252a' : '#0E0E10',\n color:\n channel.unreadMessages.count > 0 ||\n props.selectedChannelId === channel.name\n ? '#fff'\n : '#777583',\n fill:\n channel.unreadMessages.count > 0 ||\n props.selectedChannelId === channel.name\n ? '#fff'\n : '#777583',\n }}\n >\n <div>\n <NameContainer>\n <IconWrapper>\n {channel.channelType === 'Private' ? (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M8 1C8.53043 1 9.03914 1.21071 9.41421 1.58579C9.78929 1.96086 10 2.46957 10 3V7H6V3C6 2.46957 6.21071 1.96086 6.58579 1.58579C6.96086 1.21071 7.46957 1 8 1ZM11 7V3C11 2.20435 10.6839 1.44129 10.1213 0.87868C9.55871 0.31607 8.79565 0 8 0C7.20435 0 6.44129 0.31607 5.87868 0.87868C5.31607 1.44129 5 2.20435 5 3V7C4.46957 7 3.96086 7.21071 3.58579 7.58579C3.21071 7.96086 3 8.46957 3 9V14C3 14.5304 3.21071 15.0391 3.58579 15.4142C3.96086 15.7893 4.46957 16 5 16H11C11.5304 16 12.0391 15.7893 12.4142 15.4142C12.7893 15.0391 13 14.5304 13 14V9C13 8.46957 12.7893 7.96086 12.4142 7.58579C12.0391 7.21071 11.5304 7 11 7Z\" />\n </svg>\n ) : (\n <svg\n width=\"13\"\n height=\"17\"\n viewBox=\"0 0 13 17\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M6.585 15.972C6.57134 16.0614 6.56383 16.1516 6.5625 16.242C6.5625 16.6995 6.8775 17.004 7.3125 17.004C7.7115 17.004 8.0505 16.746 8.145 16.2885L8.976 12.234H10.782C11.4135 12.234 11.7075 11.883 11.7075 11.4135C11.7075 10.9455 11.4255 10.6185 10.782 10.6185H9.3045L10.0785 6.83249H11.976C12.621 6.83249 12.903 6.49199 12.903 6.01199C12.903 5.54249 12.621 5.22599 11.976 5.22599H10.407L11.121 1.76999C11.1354 1.68874 11.1434 1.60649 11.145 1.52399C11.1462 1.42202 11.127 1.32083 11.0885 1.22638C11.0501 1.13192 10.9931 1.04612 10.921 0.974005C10.8489 0.90189 10.7631 0.844923 10.6686 0.806453C10.5742 0.767984 10.473 0.748788 10.371 0.749995C10.1822 0.746392 9.99804 0.80888 9.8504 0.926656C9.70277 1.04443 9.60094 1.21009 9.5625 1.39499L8.778 5.22599H5.4255L6.141 1.76999C6.153 1.70999 6.1635 1.59299 6.1635 1.52399C6.16433 1.42123 6.14452 1.31935 6.10526 1.22438C6.06599 1.12941 6.00807 1.04329 5.93491 0.971112C5.86176 0.898937 5.77486 0.842178 5.67937 0.804196C5.58388 0.766214 5.48174 0.747784 5.379 0.749995C5.19215 0.748908 5.0107 0.812572 4.86549 0.930161C4.72028 1.04775 4.62028 1.212 4.5825 1.39499L3.795 5.22599H2.121C1.476 5.22599 1.1955 5.55599 1.1955 6.02399C1.1955 6.49199 1.476 6.83249 2.121 6.83249H3.48L2.7075 10.617H0.9135C0.282 10.617 0 10.9455 0 11.4135C0 11.883 0.282 12.234 0.915 12.234H2.379L1.605 15.972C1.593 16.032 1.5825 16.1595 1.5825 16.242C1.5825 16.6995 1.8975 17.004 2.3325 17.004C2.73 17.004 3.0705 16.746 3.1635 16.2885L3.996 12.234H7.359L6.5865 15.972H6.585ZM5.085 6.80849H8.484L7.7115 10.653H4.2885L5.0865 6.80849H5.085Z\" />\n </svg>\n )}\n </IconWrapper>\n\n <TextMedium>{channel.name}</TextMedium>\n </NameContainer>\n </div>\n {channel.unreadMessages.mentions > 0 && (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.SideSelector.UnreadMessagesBadge`}\n props={{\n messageCount: channel.unreadMessages.mentions,\n backgroundColor: '#FF5E5E',\n }}\n />\n )}\n </ChannelListItem>\n ))}\n </ChannelListContainer>\n);\n" }, "Calimero.Curb.Navbar.UsersButtonGroup": { "": "const AvatarContainer = styled.div`\n padding-left: 1rem;\n display: flex;\n flex-direction: row;\n cursor: pointer;\n`;\n\nconst ProfileIconContainerGroup = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n ${({ id }) => id && `background-color: ${colors[id]};`}\n ${({ counter }) =>\n counter ? 'background-color: #25252A; color: #6E6E78;' : 'color: #FFF;'}\n text-align: center;\n /* Body/Small */\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-left: -8px;\n border: solid 1px #0e0e10;\n :hover {\n border: solid 1px #fff;\n }\n`;\n\nreturn (\n <AvatarContainer onClick={props.openMemberList}>\n {props.channelUserList.slice(0, 3).map((user, id) => {\n return (\n <div key={id}>\n <ProfileIconContainerGroup>\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n showStatus: false,\n componentOwnerId: props.componentOwnerId,\n }}\n />\n </ProfileIconContainerGroup>\n </div>\n );\n })}\n {props.channelUserList.length > 3 && (\n <ProfileIconContainerGroup counter={true}>\n {props.channelUserList.length}\n </ProfileIconContainerGroup>\n )}\n </AvatarContainer>\n);\n" }, "Calimero.Curb.Settings.DetailsContainer": { "": "const channelName = props.channelName;\nconst initialTabIndex = props.selectedTabIndex ? 0 : 1;\nconst onSwitch = props.onSwitch;\nconst userCount = props.userList.length;\nconst userList = props.userList;\nconst componentOwnerId = props.componentOwnerId;\nconst channelMeta = props.channelMeta;\nconst handleLeaveChannel = props.handleLeaveChannel;\nconst addMember = props.addMember;\nconst promoteModerator = props.promoteModerator;\nconst removeUserFromChannel = props.removeUserFromChannel;\nconst curbApi = props.curbApi;\n\nconst [selectedTabIndex, setSelectedTabIndex] = useState(initialTabIndex);\nconst [optionsOpen, setOptionsOpen] = useState(-1);\nconst [nonInvitedUserList, setNonInvitedUserList] = useState([]);\nconst [selectedUser, setSelectedUser] = useState(undefined);\n\nconst getNonInvitedUsers = useCallback(\n (namePrefix, channelName) => {\n if (namePrefix) {\n curbApi.getFilteredMembers(namePrefix, channelName).then((users) => {\n setNonInvitedUserList(users ?? []);\n });\n }\n },\n [curbApi, userList],\n);\n\nconst ChannelTitle = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst ChannelName = () => {\n return (\n <ChannelTitle>\n <i className=\"bi bi-hash\"></i>\n {channelName}\n </ChannelTitle>\n );\n};\n\nreturn (\n <>\n <ChannelName channelName={channelName} />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Settings.TabSwitch`}\n props={{\n selectedTabIndex,\n setSelectedTabIndex,\n userCount,\n }}\n />\n {selectedTabIndex === 0 && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Settings.AboutDetails`}\n props={{\n dateCreated: channelMeta.createdAt,\n manager: channelMeta.createdBy,\n handleLeaveChannel,\n }}\n />\n )}\n {selectedTabIndex === 1 && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Settings.MemberDetails`}\n props={{\n componentOwnerId,\n userList,\n addMember,\n channelName,\n setOptionsOpen,\n optionsOpen,\n promoteModerator,\n removeUserFromChannel,\n channelOwner: channelMeta.createdBy,\n getNonInvitedUsers,\n nonInvitedUserList,\n selectedUser,\n setSelectedUser,\n }}\n />\n )}\n </>\n);\n" }, "Calimero.Curb.SideSelector.DMHeader": { "": "const Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n color: #777583;\n :hover {\n color: #ffffff;\n }\n @media (max-width: 1024px) {\n width: 100%;\n }\n`;\n\nconst TextBold = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n`;\nconst IconPlusContainer = styled.div`\n display: flex;\n cursor: pointer;\n justify-content: center;\n align-items: center;\n font-size: 1.25rem;\n`;\n\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\nconst createDM = props.createDM;\n\nconst isValidNearAccount = useCallback((value) => {\n const regex = /^[a-z\\d]+[-_]*[a-z\\d]+[-_]*[a-z\\d]+\\.(near|testnet)$/;\n let isValid = false;\n let error = '';\n\n if (!regex.test(value)) {\n isValid = false;\n error = \"Invite users whose wallets end with '.near' for access\";\n } else {\n isValid = true;\n error = '';\n }\n return { isValid, error };\n}, []);\n\nconst CreateDMPopup = ({ componentOwnerId, createDM, curbApi }) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Popups.StartDMPopup`}\n props={{\n componentOwnerId,\n title: 'Start new message',\n placeholder: 'Send to wallet address',\n buttonText: 'Next',\n colors: {\n base: '#5765f2',\n hover: '#717cf0',\n disabled: '#3B487A',\n },\n toggle: (\n <IconPlusContainer>\n <i className=\"bi bi-plus-circle\" />\n </IconPlusContainer>\n ),\n onAccountSelected: createDM,\n fetchAccounts: (prefix) => {\n return curbApi.fetchAccounts({ prefix, limit: 20 });\n },\n validator: isValidNearAccount,\n functionLoader: createDM,\n }}\n />\n);\n\nreturn (\n <Container>\n <TextBold>{'Direct Messages'}</TextBold>\n <CreateDMPopup\n componentOwnerId={props.componentOwnerId}\n createDM={props.createDM}\n curbApi={props.curbApi}\n />\n </Container>\n);\n" }, "Calimero.TaskChain.TaskChainLogo": { "": "const LogoContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n ${({ justify }) => justify && 'justify-content: center;'}\n`;\n\nconst TaskChainNameContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-size: 20.923px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-right: 0.875rem;\n`;\n\nconst OrganizationNameContainer = () => {\n return <TaskChainNameContainer>Boards</TaskChainNameContainer>;\n};\n\nreturn (\n <LogoContainer>\n <svg\n width=\"28\"\n height=\"28\"\n viewBox=\"0 0 28 28\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <rect width=\"28\" height=\"28\" fill=\"#1E1E1E\" />\n <g id=\"Frame 20\" clipPath=\"url(#clip0_0_1)\">\n <rect\n width=\"1440\"\n height=\"900\"\n transform=\"translate(-76 -26)\"\n fill=\"#0E0E10\"\n />\n <g id=\"Group 2\">\n <g id=\"Group 1\">\n <rect\n id=\"Rectangle 3\"\n width=\"28\"\n height=\"28\"\n rx=\"6\"\n fill=\"#D0FC42\"\n />\n <path\n id=\"Subtract\"\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M24.1568 13.9999C24.1568 8.69352 19.8554 4.39209 14.549 4.39209C9.2426 4.39209 4.94117 8.69352 4.94117 13.9999C4.93884 15.7503 5.41672 17.4677 6.32278 18.9653L5.46576 21.8784C5.41655 22.0456 5.41331 22.223 5.45639 22.392C5.49946 22.5609 5.58725 22.7151 5.71053 22.8384C5.83381 22.9617 5.98802 23.0495 6.15696 23.0926C6.3259 23.1356 6.50333 23.1324 6.67058 23.0832L9.58368 22.2262C11.0811 23.1326 12.7986 23.6105 14.549 23.6078C15.4677 23.6078 16.3564 23.4788 17.1977 23.238C17.1509 22.8993 17.1267 22.5534 17.1267 22.2017C17.1267 18.2267 20.2197 14.974 24.1303 14.719C24.1479 14.4816 24.1568 14.2418 24.1568 13.9999ZM10.1672 12.9809C10.4375 12.7106 10.804 12.5588 11.1863 12.5588C11.5685 12.5588 11.9351 12.7106 12.2053 12.9809C12.4756 13.2511 12.6274 13.6177 12.6274 13.9999C12.6274 14.3822 12.4756 14.7487 12.2053 15.019C11.9351 15.2893 11.5685 15.4411 11.1863 15.4411C10.804 15.4411 10.4375 15.2893 10.1672 15.019C9.89693 14.7487 9.74509 14.3822 9.74509 13.9999C9.74509 13.6177 9.89693 13.2511 10.1672 12.9809ZM16.8927 12.9809C17.163 12.7106 17.5295 12.5588 17.9118 12.5588C18.294 12.5588 18.6605 12.7106 18.9308 12.9809C19.2011 13.2511 19.3529 13.6177 19.3529 13.9999C19.3529 14.3822 19.2011 14.7487 18.9308 15.019C18.6605 15.2893 18.294 15.4411 17.9118 15.4411C17.5295 15.4411 17.163 15.2893 16.8927 15.019C16.6224 14.7487 16.4706 14.3822 16.4706 13.9999C16.4706 13.6177 16.6224 13.2511 16.8927 12.9809Z\"\n fill=\"#111111\"\n />\n </g>\n </g>\n </g>\n <defs>\n <clipPath id=\"clip0_0_1\">\n <rect\n width=\"1440\"\n height=\"900\"\n fill=\"white\"\n transform=\"translate(-76 -26)\"\n />\n </clipPath>\n </defs>\n </svg>\n <OrganizationNameContainer />\n </LogoContainer>\n);\n" }, "Calimero.Curb.Settings.TabSwitch": { "": "const selectedTabIndex = props.selectedTabIndex ?? 0;\nconst setSelectedTabIndex = props.setSelectedTabIndex;\nconst userCount = props.userCount ?? 0;\n\nconst Popup = styled.div`\n display: flex;\n align-items: center;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n padding-top: 1rem;\n border-bottom: solid 1px #282933;\n margin-bottom: 1rem;\n`;\n\nconst SwitchOption = styled.div`\n display: flex;\n column-gap: 0.5rem;\n padding-right: 1rem;\n ${({ leftPadding }) => leftPadding && 'padding-left: 1rem;'}\n cursor: pointer;\n ${({ selected }) => (selected ? 'color: #5765F2' : 'color: #fff;')}\n`;\n\nconst items = [\n {\n name: 'About',\n icon: 'bi bi-info-circle-fill',\n },\n {\n name: `Members ${userCount}`,\n icon: 'bi bi-people-fill',\n },\n];\n\nreturn (\n <Popup>\n {items.map((item, index) => (\n <SwitchOption\n selected={selectedTabIndex === index}\n onClick={() => setSelectedTabIndex(index)}\n >\n <i className={item.icon}></i>\n <p>{item.name}</p>\n </SwitchOption>\n ))}\n </Popup>\n);\n" }, "Calimero.TaskChain.SideMenu.BoardItem": { "": "const BoardListItem = styled.div`\n display: flex;\n flex-direction: row;\n height: 40px;\n width: 100%;\n justify-content: space-between;\n padding-left: 16px;\n padding-right: 16px;\n align-items: center;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n :hover {\n text-decoration: none;\n background-color: #777583;\n color: #ffffff;\n }\n border-radius: 4px;\n gap: 4px;\n ${({ selected }) => (selected ? 'background-color: #25252A;' : '')};\n ${({ selected }) => (selected ? 'color: #FFFFFF;' : 'color: #777583;')};\n ${({ disabled }) => (disabled ? 'cursor: not-allowed;' : 'cursor: pointer;')};\n`;\n\nconst BoardListItemCircle = styled.div`\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background-color: #d0fc42;\n`;\n\nconst BoardListItemName = styled.div`\n display: flex;\n flex-direction: row;\n gap: 8px;\n align-items: center;\n`;\n\nconst GearIcon = styled.a`\n color: #ffffff;\n :hover {\n color: #0e0e10;\n }\n`;\n\nconst Text = styled.p`\n padding: 0;\n margin: 0;\n max-width: 100px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst SuccessIcon = styled.div`\n color: #00ff66;\n`;\n\nconst ErrorIcon = styled.div`\n color: #dc3545;\n`;\n\nreturn (\n <BoardListItem\n key={props.id + props.name}\n id={props.id}\n selected={props.selectedProjectId === props.id}\n onClick={() => {\n if (props.id) {\n props.onClick();\n }\n }}\n onMouseEnter={props.onMouseEnter}\n onMouseLeave={props.onMouseLeave}\n disabled={!props.id}\n >\n <BoardListItemName>\n <BoardListItemCircle />\n <Text>{props.name}</Text>\n </BoardListItemName>\n {props.boardStatus.status ? (\n props.boardStatus.status === 'Saving' ? (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.TaskChain.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : props.boardStatus.status === 'Saved' ? (\n <SuccessIcon>\n <i className=\"bi bi-check\"></i>\n </SuccessIcon>\n ) : props.boardStatus.status === 'Error' ? (\n <ErrorIcon>\n <i className=\"bi bi-x-circle\"></i>\n </ErrorIcon>\n ) : null\n ) : (\n props.hoverId === props.id && (\n <GearIcon onClick={props.onSettingsClick}>\n <i className=\"bi bi-gear\"></i>\n </GearIcon>\n )\n )}\n </BoardListItem>\n);\n" }, "Calimero.TaskChain.BoardContainer.AddColumnDialog": { "": "const addColumn = props.addColumn;\nconst setAddColumnDialogOpen = props.setAddColumnDialogOpen;\nconst functionLoader = props.functionLoader;\nconst onChangeColumnName = props.onChangeColumnName;\nconst columnName = props.columnName;\nconst componentOwnerId = props.componentOwnerId;\nconst addColumnNameMissing = props.addColumnNameMissing;\nconst setColumnName = props.setColumnName;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Title = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: end;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 14px;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <Header>\n <CloseButton\n onClick={() => {\n setAddColumnDialogOpen(false);\n setColumnName('');\n }}\n >\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <Title\n onChange={onChangeColumnName}\n value={columnName}\n placeholder=\"Add Name\"\n />\n {addColumnNameMissing && <MissingTitle>Missing name</MissingTitle>}\n </FieldContainer>\n <FunctionButton onClick={addColumn}>\n {functionLoader ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n 'Add'\n )}\n </FunctionButton>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.TaskChain.SideMenu.AddMemberDialog": { "": "const createMember = props.createMember;\nconst open = props.open;\nconst onChangeShowAddMemberDialog = props.onChangeShowAddMemberDialog;\nconst functionLoader = props.functionLoader;\nconst componentOwnerId = props.componentOwnerId;\nconst addBoardNameMissing = props.addBoardNameMissing;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Title = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Input = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n flex: 1;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 30%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n border: none;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: end;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingText = styled.div`\n position: absolute;\n right: 36%;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 14px;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <Header>\n <CloseButton onClick={() => onChangeShowAddBoardDialog(false)}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <Title\n onChange={onChangeBoardName}\n value={boardName}\n placeholder=\"Add Name\"\n />\n {addBoardNameMissing && <MissingTitle>Missing name</MissingTitle>}\n </FieldContainer>\n <FunctionButton onClick={createBoard}>\n {functionLoader ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n 'Create'\n )}\n </FunctionButton>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.AssigneeDropdown": { "": "const { componentOwnerId, assignee, setAssignee, projectId, contract } = props;\n\nconst SelectTrigger = styled('Select.Trigger')`\n display: flex;\n flex-direction: row;\n align-items: center;\n height: 31px;\n background-color: #373d43;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-weight: 400;\n line-height: 21px;\n letter-spacing: 0em;\n border: none;\n border-radis: 0.5rem;\n gap: 0.25rem;\n :focus {\n outline-color: transparent;\n outline-style: solid;\n outline-width: 1px;\n }\n`;\n\nconst SelectGroup = styled('Select.Group')`\n display: flex;\n flex-direction: column;\n padding: 16px;\n background-color: #0e0e10;\n color: #777583;\n width: 328px;\n border-radius: 0.5rem;\n`;\n\nconst SelectContent = styled('Select.Content')`\n z-index: 100;\n`;\n\nconst SelectItem = styled('Select.Item')`\n display: flex;\n column-gap: 0.5rem;\n :hover {\n background-color: #d0fc42;\n }\n border: none;\n :focus {\n outline-color: transparent;\n outline-style: solid;\n outline-width: 1px;\n }\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.25rem;\n`;\n\nconst SelectIcon = styled('Select.Icon')`\n color: #fff;\n transition: transform 3s ease-in-out;\n transform: rotate(${({ open }) => (open ? '180deg' : '0deg')});\n`;\n\nconst ButtonsContainer = styled.div`\n display: flex;\n flex-direction: row;\n gap: 8px;\n`;\n\nconst SelectedMember = styled.div`\n display: flex;\n column-gap: 0.5rem;\n padding: 0.5rem;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 1rem;\n`;\n\nconst CloseButton = styled.div`\n background-color: transparent;\n :hover {\n color: #fff;\n }\n`;\n\nconst Text = styled.div`\n padding: 0;\n margin: 0;\n`;\n\nconst ResetButton = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 0.3rem;\n background-color: transparent;\n color: #777583;\n :hover {\n color: #fff;\n }\n`;\n\nconst placeholder = 'Assign a person';\nconst label = 'Assign to task';\nconst NO_ASSIGNEE = 'no_assignee';\n\nconst accountId = context.accountId;\n\nconst [members, setMembers] = useState([]);\nconst [isOpen, setIsOpen] = useState(false);\n\nconst toggleOpen = () => {\n setIsOpen((prevIsOpen) => !prevIsOpen);\n};\n\nconst getMembers = useCallback(async (projectId) => {\n try {\n Near.asyncCalimeroView(contract, 'get_members', {\n project_id: projectId,\n }).then((members) => {\n setMembers(\n members\n .map((member) => ({ id: member }))\n .filter((member) => member.id !== accountId),\n );\n });\n } catch (e) {\n console.log('getMembers', e);\n }\n}, []);\n\nuseEffect(() => {\n if (projectId) {\n getMembers(projectId);\n }\n}, [projectId]);\n\nreturn (\n <Select.Root\n value={assignee}\n onValueChange={setAssignee}\n placeholder={placeholder}\n open={isOpen}\n onOpenChange={toggleOpen}\n >\n <ButtonsContainer>\n <SelectTrigger>\n <Select.Value value={assignee}>\n {assignee !== NO_ASSIGNEE && assignee !== null ? (\n <SelectedMember>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: assignee,\n componentOwnerId,\n }}\n />\n <Text>{assignee}</Text>\n </SelectedMember>\n ) : (\n placeholder\n )}\n </Select.Value>\n <SelectIcon open={isOpen}>\n <i className=\"bi bi-caret-down-fill\"></i>\n </SelectIcon>\n </SelectTrigger>\n {assignee !== NO_ASSIGNEE && assignee !== null && (\n <ResetButton\n type=\"button\"\n onClick={(e) => {\n setAssignee(NO_ASSIGNEE);\n }}\n >\n <i className=\"bi bi-x-circle-fill\"></i>\n Remove\n </ResetButton>\n )}\n </ButtonsContainer>\n <SelectContent position=\"popper\" sideOffset={10}>\n <Select.Viewport>\n <SelectGroup>\n <Header>\n <Select.Label>{label}</Select.Label>\n <CloseButton type=\"button\" onClick={() => setIsOpen(false)}>\n <i className=\"bi bi-x\"></i>\n </CloseButton>\n </Header>\n <SelectItem\n id={accountId}\n key={accountId}\n value={accountId}\n ref=\"forwardedRef\"\n >\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: accountId,\n componentOwnerId,\n }}\n />\n <Select.ItemText>Assign to me</Select.ItemText>\n <Select.ItemIndicator>\n <i className=\"bi bi-check\"></i>\n </Select.ItemIndicator>\n </SelectItem>\n {members?.map((member, id) => (\n <SelectItem id={id} key={id} value={member.id} ref=\"forwardedRef\">\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: member.id,\n componentOwnerId,\n }}\n />\n <Select.ItemText>{member.id}</Select.ItemText>\n </SelectItem>\n ))}\n </SelectGroup>\n </Select.Viewport>\n </SelectContent>\n </Select.Root>\n);\n" }, "Calimero.DocsChain.ArticleView": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst PageContainer = styled.div`\n width: 100%;\n height: 100vh;\n padding-left: 60px;\n padding-right: 60px;\n background-color: #0e0e10;\n color: #ffffff;\n`;\n\nState.init({\n articleId: props.articleId,\n editArticle: false,\n});\n\nState.update({\n ...state,\n article: Near.calimeroView(contract, 'get_article', {\n article_id: props.articleId,\n }),\n});\n\nconsole.log(state.article);\n\nconst saveArticle = () => {\n if (!state.editorInput) {\n return;\n }\n const params = {\n article_id: state.articleId,\n body: state.editorInput,\n };\n Near.fakCalimeroCall(contract, 'post_article', params);\n};\n\nreturn (\n <PageContainer>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Navbar.HorizontalNavbar'}\n config={redirectConfig}\n />\n <div className=\"d-flex\">\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.DocsSidebar'}\n config={redirectConfig}\n props={{\n onOpenCreatePage: state.onOpenCreatePage,\n createPageOpen: state.createPage.open,\n }}\n />\n <div>\n <h1>Article: {state.articleId}</h1>\n {/* === BUTTON - EDIT ARTICLE === */}\n {\n <button\n onClick={() => {\n State.update({\n ...state,\n editArticle: true,\n editorInput: state.article.body,\n });\n }}\n >\n Edit Article\n </button>\n }\n {/* === BUTTON - SAVE ARTICLE === */}\n {state.editArticle && (\n <>\n <button\n type=\"button\"\n className=\"btn btn-success\"\n onClick={() => {\n saveArticle();\n }}\n >\n Save Article{' '}\n </button>\n\n <button\n type=\"button\"\n className=\"btn btn-danger\"\n onClick={() => {\n State.update({\n ...state,\n editArticle: false,\n editorInput: undefined,\n });\n }}\n >\n Cancel{' '}\n </button>\n </>\n )}\n\n {/* === BUTTON - VIEW HISTORY === */}\n <span className=\"ps-4\">\n <button\n onClick={() => {\n State.update({\n ...state,\n viewHistory: !state.viewHistory,\n });\n }}\n >\n View History\n </button>\n </span>\n {/* === EDIT ARTICLE === */}\n {state.editArticle && (\n <>\n <div className=\"d-flex gap-2\" style={{ minHeight: '300px' }}>\n <div className=\"w-50\">\n <Widget\n src=\"calimero.near/widget/Calimero.DocsChain.MarkdownEditorIframe\"\n config={redirectConfig}\n props={{\n initialText: state.article.body,\n onChange: (newBody) =>\n State.update({\n editorInput: newBody,\n }),\n }}\n />\n </div>\n <div className=\"w-50\">\n <Widget\n src=\"calimero.near/widget/Calimero.DocsChain.SocialMarkdown\"\n config={redirectConfig}\n props={{ text: state.editorInput }}\n />\n </div>\n </div>\n </>\n )}\n {/* MARKDOWN and TAGS list when user doesn't edit article */}\n {!state.editArticle && (\n <>\n <Markdown text={state.editorInput || state.article.body} />\n </>\n )}\n {/* === VIEW HISTORY === */}\n {state.viewHistory && (\n <div className=\"mt-3 ps-5\">Not implemented yet</div>\n )}\n {/* === FOOTER === */}\n <Widget\n src={`calimero.near/widget/Calimero.DocsChain.ArticleFooter`}\n config={redirectConfig}\n props={{\n author: state.article.author,\n lastEditor: state.article.author, // expand contract to allow editors\n timeLastEdit: state.article.timestamp,\n version: state.article.version,\n }}\n />\n </div>\n </div>\n </PageContainer>\n);\n" }, "Calimero.Curb.Popups.CreateChannelPopup": { "": "const Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n position: relative;\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n outline: none;\n border: none;\n border-radius: 4px;\n background-color: #0e0e10;\n`;\n\nconst customStyle = {\n border: '1px solid #dc3545',\n outline: 'none',\n};\n\nconst FunctionButton = styled.button`\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.base};`};\n :hover {\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.hover};`};\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n position: absolute;\n right: 1rem;\n cursor: pointer;\n`;\n\nconst {\n title,\n toggle,\n placeholder,\n createChannel,\n buttonText,\n componentOwnerId,\n channelNameValidator,\n colors,\n} = props;\n\nconst BaseModal = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.BaseModal`}\n props={{\n ...props,\n componentOwnerId,\n }}\n />\n);\n\nconst [isOpen, setIsOpen] = useState(false);\nconst [isProcessing, setIsProcessing] = useState(false);\nconst [inputValue, setInputValue] = useState('');\nconst [validInput, setValidInput] = useState(false);\nconst [errorMessage, setErrorMessage] = useState('');\nconst [visibility, setVisibility] = useState('public');\nconst [readOnly, setReadOnly] = useState('no');\n\nconst runProcess = () => {\n setIsProcessing(true);\n createChannel(inputValue, visibility === 'private', readOnly === 'yes').then(\n (receipt) => {\n if (receipt === undefined) {\n setIsProcessing(false);\n setValidInput(false);\n setErrorMessage('Error creating channel.');\n } else {\n setIsProcessing(false);\n setIsOpen(false);\n }\n },\n );\n};\n\nconst onOpenChange = (isOpen) => {\n if (isProcessing && !isOpen) {\n return;\n }\n setIsOpen(isOpen);\n};\n\nconst handleClosePopup = () => {\n if (isProcessing) return;\n setIsOpen(false);\n};\nconst isInvalid =\n inputValue && channelNameValidator && !validInput && errorMessage;\n\nconst ErrorWrapper = styled.div`\n color: #dc3545;\n /* Body/Small */\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-top: 6px;\n`;\n\nconst EmptyMessageContainer = styled.div`\n height: 27px;\n`;\n\nconst IconSvg = styled.svg`\n position: absolute;\n top: 50%;\n right: 13px;\n`;\n\nconst ExclamationIcon = () => (\n <IconSvg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"#dc3545\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M8.99951 2.74918C5.54773 2.74918 2.74951 5.5474 2.74951 8.99918C2.74951 12.451 5.54773 15.2492 8.99951 15.2492C12.4513 15.2492 15.2495 12.451 15.2495 8.99918C15.2495 5.5474 12.4513 2.74918 8.99951 2.74918ZM1.74951 8.99918C1.74951 4.99511 4.99545 1.74918 8.99951 1.74918C13.0036 1.74918 16.2495 4.99511 16.2495 8.99918C16.2495 13.0032 13.0036 16.2492 8.99951 16.2492C4.99545 16.2492 1.74951 13.0032 1.74951 8.99918ZM8.334 5.058C8.42856 4.95669 8.56093 4.89918 8.69951 4.89918H9.29951C9.4381 4.89918 9.57046 4.95669 9.66503 5.058C9.75959 5.15931 9.80786 5.29532 9.79833 5.43358L9.49833 9.78358C9.48025 10.0457 9.2623 10.2492 8.99951 10.2492C8.73672 10.2492 8.51878 10.0457 8.5007 9.78358L8.2007 5.43358C8.19116 5.29532 8.23944 5.15931 8.334 5.058ZM9.89951 12.2992C9.89951 12.7962 9.49657 13.1992 8.99951 13.1992C8.50246 13.1992 8.09951 12.7962 8.09951 12.2992C8.09951 11.8021 8.50246 11.3992 8.99951 11.3992C9.49657 11.3992 9.89951 11.8021 9.89951 12.2992Z\"\n fill=\"#DC3545\"\n />\n </IconSvg>\n);\n\nconst InputWrapper = styled.div`\n position: relative;\n`;\n\nconst popupContent = (\n <>\n <CloseButton onClick={handleClosePopup}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n <Text>{title}</Text>\n <InputWrapper>\n <Input\n onChange={(e) => {\n setInputValue(e.target.value);\n if (channelNameValidator) {\n const { isValid, error } = channelNameValidator(e.target.value);\n setValidInput(isValid);\n setErrorMessage(error ? error : '');\n }\n }}\n value={inputValue}\n placeholder={placeholder}\n disabled={isProcessing}\n style={isInvalid ? customStyle : {}}\n />\n {isInvalid && <ExclamationIcon />}\n </InputWrapper>\n {isInvalid ? (\n <ErrorWrapper>{errorMessage}</ErrorWrapper>\n ) : (\n <EmptyMessageContainer />\n )}\n <div className=\"mb-3\">\n <label className=\"form-label\">Visibility:</label>\n <div className=\"d-flex\">\n <div className=\"form-check form-check-inline\">\n <input\n className=\"form-check-input\"\n type=\"radio\"\n name=\"visibility\"\n id=\"public\"\n value=\"public\"\n checked={visibility === 'public'}\n onChange={() => setVisibility('public')}\n disabled={isSubmitting}\n />\n <label className=\"form-check-label\" htmlFor=\"public\">\n Public\n </label>\n </div>\n <div className=\"form-check form-check-inline\">\n <input\n className=\"form-check-input\"\n type=\"radio\"\n name=\"visibility\"\n id=\"private\"\n value=\"private\"\n checked={visibility === 'private'}\n onChange={() => setVisibility('private')}\n disabled={isSubmitting}\n />\n <label className=\"form-check-label\" htmlFor=\"private\">\n Private\n </label>\n </div>\n </div>\n </div>\n\n <div className=\"mb-3\">\n <label className=\"form-label\">Read-only:</label>\n <div className=\"d-flex\">\n <div className=\"form-check form-check-inline\">\n <input\n className=\"form-check-input\"\n type=\"radio\"\n name=\"readOnly\"\n id=\"yes\"\n value=\"yes\"\n checked={readOnly === 'yes'}\n onChange={() => setReadOnly('yes')}\n disabled={isSubmitting}\n />\n <label className=\"form-check-label\" htmlFor=\"yes\">\n Yes\n </label>\n </div>\n <div className=\"form-check form-check-inline\">\n <input\n className=\"form-check-input\"\n type=\"radio\"\n name=\"readOnly\"\n id=\"no\"\n value=\"no\"\n checked={readOnly === 'no'}\n onChange={() => setReadOnly('no')}\n disabled={isSubmitting}\n />\n <label className=\"form-check-label\" htmlFor=\"no\">\n No\n </label>\n </div>\n </div>\n </div>\n\n <FunctionButton\n onClick={runProcess}\n disabled={inputValue ? isInvalid : true}\n >\n {isProcessing ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n buttonText\n )}\n </FunctionButton>\n </>\n);\n\nreturn (\n <BaseModal\n toggle={toggle}\n content={popupContent}\n open={isOpen}\n onOpenChange={onOpenChange}\n />\n);\n" }, "Calimero.Curb.Navbar.DetailsDropdown": { "": "const activeChat = props.activeChat;\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\n\nconst DropdownSelector = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n cursor: pointer;\n padding-left: 0.875rem;\n padding-top: 0.5rem;\n`;\n\nconst SelectedChannelName = styled.h4`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%;\n @media (max-width: 1024px) {\n font-size: 18px;\n font-weight: 400;\n }\n`;\n\nconst ChevronIcon = styled.i`\n font-size: 1rem;\n @media (max-width: 1024px) {\n display: none;\n }\n cursor: pointer;\n color: #777583;\n`;\n\nconst MobileCogIcon = styled.i`\n display: none;\n @media (max-width: 1024px) {\n display: block;\n }\n cursor: pointer;\n color: #777583;\n font-size: 0.8rem;\n padding-bottom: 4px;\n`;\n\nif (activeChat.type === 'channel') {\n const toggle = (\n <DropdownSelector>\n <SelectedChannelName>{activeChat.name}</SelectedChannelName>\n <>\n <ChevronIcon className=\"bi bi-gear-fill\" />\n <MobileCogIcon className=\"bi bi-info-circle-fill\" />\n </>\n </DropdownSelector>\n );\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Popups.ChannelDetailsPopup`}\n props={{\n toggle,\n chat: activeChat,\n curbApi,\n componentOwnerId,\n }}\n />\n );\n}\nconst title =\n activeChat.type === 'direct_message' ? activeChat.id : activeChat.name;\nreturn <SelectedChannelName>{title}</SelectedChannelName>;\n" }, "Calimero.Curb.Main": { "": "const initialContract = props.contract || 'chat-staging.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId || 'calimero.testnet';\nconst encryptionUrl =\n props.encryptionUrl ||\n 'https://cali-encryption.euw3.staging.gcp.calimero.network/key';\nconst meroshipUrl =\n props.meroshipUrl ||\n 'wss://api.staging.calimero.network/api/v1/shards/ws-protocol-63-calimero-testnet/meroship/ws';\nconst accountId = context.accountId;\nconst enableCommunities = props.enableCommunities ?? false;\nconst onAppJoined = props.onAppJoined || (() => {});\nconst onAppJoin = props.onAppJoin || (() => {});\nconst communities = props.communities || [];\nconst getCalimeroToken = props.getCalimeroToken || (() => {});\nconst refresh = props.refresh || (() => {});\nconst debug = props.debug || false;\n\nconst getSelectedCommunity = useCallback(() => {\n return Storage.innerGet('community').then((value) => {\n if (selectedCommunity === undefined) {\n value = initialContract;\n Storage.set('community', value);\n setContract(value);\n } else {\n setContract(value);\n }\n });\n}, [initialContract]);\n\nconst [contract, setContract] = useState(null);\nconst [loggedIn, setLoggedIn] = useState(false);\nconst [initialChat, setInitialChat] = useState(null);\n\nconst handleContractChange = useCallback((contract) => {\n changeContract(contract, null);\n}, []);\n\nconst updateInitialChat = useCallback((session) => {\n Storage.set('lastSession', JSON.stringify(session));\n}, []);\n\nfunction changeContract(contract, session) {\n Storage.set('community', contract);\n setContract(contract);\n setLoggedIn(false);\n updateInitialChat(session);\n}\n\nconst getUrlSession = () => {\n const urlDerivedChatParams = extractQueryParams();\n let derivedSession = null;\n if (\n urlDerivedChatParams &&\n urlDerivedChatParams.type &&\n urlDerivedChatParams.target &&\n urlDerivedChatParams.contractName\n ) {\n if (!communities.includes(urlDerivedChatParams.contractName)) {\n console.log('Error unknown contract id on push notif.');\n return;\n }\n\n if (urlDerivedChatParams.type === 'channel') {\n derivedSession = {\n type: urlDerivedChatParams.type,\n name: urlDerivedChatParams.target,\n };\n } else if (urlDerivedChatParams.type === 'dm') {\n derivedSession = {\n type: 'direct_message',\n id: urlDerivedChatParams.target,\n };\n }\n changeContract(urlDerivedChatParams.contractName, derivedSession);\n }\n return derivedSession;\n};\n\nconst getInitialChat = useCallback(async () => {\n const derivedSession = getUrlSession();\n if (derivedSession) {\n setInitialChat(derivedSession);\n return;\n }\n Storage.innerGet('lastSession').then((session) => {\n setInitialChat(session ? JSON.parse(session) : null);\n });\n}, []);\n\nuseEffect(() => {\n getSelectedCommunity();\n getInitialChat();\n}, []);\n\nif (!contract) {\n return 'Loading';\n}\n\nconst PageContainer = styled.div`\n height: 100%;\n width: 100%;\n`;\n\nconst loginApi = useMemo(\n () => ({\n join: () => {\n onAppJoin();\n Near.fakCalimeroCall(contract, 'join');\n },\n login: () => {\n onAppJoin();\n Near.requestCalimeroFak(contract);\n },\n validateKey: () => Near.hasValidCalimeroFak(contract),\n getMembers: () => Near.asyncCalimeroView(contract, 'get_members'),\n }),\n [contract],\n);\n\nconst Logo = () => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.CurbLogo`}\n props={{\n justify: true,\n }}\n />\n);\n\n// Ping to prepare the new key, needs to be simplified\nconst ping = () => Near.fakCalimeroCall(contract, 'ping');\n\nconst App = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Logger`}\n props={{\n render: ({ Logger }) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.EventEmitter`}\n props={{\n render: ({ EventEmitter }) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.WebSocketManager`}\n props={{\n deps: { Logger, EventEmitter },\n debug,\n accountId: props.accountId,\n wsAddress: meroshipUrl,\n getAuthToken: getCalimeroToken,\n render: ({ wsApi }) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.AppContainer`}\n props={{\n ...props,\n wsApi,\n deps: { Logger, EventEmitter },\n }}\n />\n ),\n }}\n />\n ),\n }}\n />\n ),\n }}\n />\n);\n\nreturn (\n <PageContainer>\n {!loggedIn || !accountId ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Login.index`}\n props={{\n loginApi,\n accountId: context.accountId,\n componentOwnerId,\n onValidLogin: () => {\n onAppJoined(accountId);\n // todo! migrate to meroship\n ping().then(() => {\n setLoggedIn(true);\n });\n },\n logo: <Logo />,\n onAppJoin,\n refresh,\n }}\n />\n ) : (\n <App\n componentOwnerId={componentOwnerId}\n contract={contract}\n encryptionUrl={encryptionUrl}\n accountId={context.accountId}\n enableCommunities={enableCommunities}\n handleContractChange={handleContractChange}\n initialChat={initialChat}\n updateInitialChat={updateInitialChat}\n communities={communities}\n />\n )}\n </PageContainer>\n);\n" }, "Calimero.DocsChain.Sidebar.SettingsList": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst Container = styled.div`\n background-color: #0e0e10;\n width: 317px;\n`;\n\nconst Item = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n &:hover {\n color: #ffffff;\n }\n a {\n text-decoration: none;\n color: inherit;\n\n &:hover {\n color: #ffffff;\n }\n }\n :hover {\n background-color: #25252a;\n }\n cursor: pointer;\n ${({ selected }) =>\n selected\n ? 'color: #fff; background-color: #25252a;'\n : 'color: #777583; background-color: #0E0E10;'}\n`;\n\nconst TextMedium = styled.div`\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n padding-top: 1px;\n`;\n\nconst NameContainer = styled.a`\n display: flex;\n justify-content: start;\n align-items: center;\n gap: 12px;\n width: 100%;\n height: 24px;\n`;\n\nconst settingsList = [\n {\n id: 'members',\n title: 'Members',\n href: 'Members.Main',\n icon: 'bi bi-people',\n },\n];\n\nreturn (\n <Container>\n {settingsList.map((item) => (\n <Item key={item.id} selected={props.selectedId === item.id}>\n <NameContainer\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.${item.href}`,\n )}\n >\n <i\n className={`${item.icon} w-5 h-5`}\n style={{ fontSize: '1.5rem' }}\n ></i>\n <TextMedium>{item.title}</TextMedium>\n </NameContainer>\n </Item>\n ))}\n </Container>\n);\n" }, "Calimero.Curb.CommunitiesContainer": { "": "const componentOwnerId = props.componentOwnerId;\n\nconst Container = styled.div`\n display: flex;\n flex-direction: column;\n height: 100%;\n align-items: center;\n width: 100%;\n height: 100%;\n background-color: black;\n padding: 31px 60px 31px 31px;\n gap: 43px;\n`;\n\nconst [communities, setCommunities] = useState([]);\nconst [filteredCommunities, setFilteredCommunities] = useState([]);\nconst [isSearched, setIsSearched] = useState(false);\nconst [searchedTerm, setSearchedTerm] = useState('');\n\nconst handleSearch = useCallback(\n (searchTerm) => {\n const filtered = communities.filter((community) =>\n community.name.toLowerCase().startsWith(searchTerm.toLowerCase()),\n );\n setFilteredCommunities(filtered);\n setIsSearched(true);\n setSearchedTerm(searchTerm);\n },\n [setFilteredCommunities, communities],\n);\n\nconst resetSearch = useCallback(() => {\n setIsSearched(false);\n setSearchedTerm('');\n setFilteredCommunities([]);\n}, [setIsSearched]);\n\nreturn (\n <Container>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.SearchContainer`}\n props={{\n componentOwnerId,\n handleSearch,\n isSearched,\n searchedTerm,\n searchResultCount: filteredCommunities.length,\n resetSearch,\n }}\n />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.CommunityList`}\n props={{\n componentOwnerId,\n communities,\n filteredCommunities,\n isSearched,\n }}\n />\n </Container>\n);\n" }, "Calimero.DocsChain.ArticleDialog.Dialog": { "": "const componentOwnerId = props.componentOwnerId || 'fran-cali.testnet';\n\nconst Container = styled.div`\n background-color: #0e0e10;\n color: #fff;\n width: 100%;\n height: 100vh;\n`;\n\nconst Header = styled.div`\n padding-top: 12px;\n`;\n\nconst InputConainer = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: center;\n padding-top: 50px;\n padding-left: 200px;\n padding-right: 200px;\n`;\n\nconst TextInputContainer = styled.div`\n padding-top: 16px;\n display: flex;\n flex-direction: column;\n`;\n\nconst TitleArea = styled.textarea`\n width: 100%;\n border: none;\n background: transparent;\n outline: none;\n width: 100%;\n padding: 0;\n margin: 0;\n font-size: 24px;\n line-height: 120%;\n font-weight: 500;\n color: #fff;\n resize: none;\n overflow-y: hidden;\n height: auto;\n min-height: 50px;\n ::placeholder {\n color: #777583;\n }\n`;\n\nconst TextArea = styled.textarea`\n width: 100%;\n background: #111;\n background: transparent;\n border: none;\n outline: none;\n width: 100%;\n padding: 0;\n margin: 0;\n font-size: 16px;\n line-height: 150%;\n font-weight: 400;\n color: #fff;\n resize: none;\n overflow-y: hidden;\n height: auto;\n min-height: 50px;\n ::placeholder {\n color: #777583;\n }\n`;\n\nconst titleRef = useRef(null);\nconst markdownTextRef = useRef(null);\nconst [title, setTitle] = useState('');\nconst [markdownText, setMarkdownText] = useState('');\n\nconst InputText = ({ textRef, text, setText, isTitle }) => {\n const handleInputChange = (event) => {\n setText(event.target.value);\n if (textRef.current.textLength === 0) {\n textRef.current.style.height = '50px';\n }\n if (textRef.current.scrollHeight > textRef.current.clientHeight) {\n const currentHeight = parseInt(\n textRef.current.style.height.replace('px', ''),\n 10,\n );\n textRef.current.style.height = `${currentHeight + 50}px`;\n }\n };\n return (\n <>\n {isTitle ? (\n <TitleArea\n placeholder=\"Untitled\"\n value={text}\n ref={textRef}\n onChange={handleInputChange}\n style={{ height: '50px' }}\n />\n ) : (\n <TextArea\n placeholder=\"Write your document..\"\n value={text}\n ref={textRef}\n onChange={handleInputChange}\n style={{ height: '50px' }}\n />\n )}\n </>\n );\n};\n\nconst publishDocument = () => {\n console.log(title);\n console.log(markdownText);\n};\n\nreturn (\n <Container>\n <Header>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.ArticleDialog.DocumentHeader`}\n />\n </Header>\n <InputConainer>\n <div className=\"d-flex gap-2\">\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.Common.Button`}\n props={{\n onClick: () => console.log('add'),\n text: 'Add Icon',\n icon: 'bi bi-plus-circle-fill',\n }}\n />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.Common.Button`}\n props={{\n onClick: () => console.log('markdown'),\n text: 'Markdown help',\n icon: 'bi bi-question-circle-fill',\n }}\n />\n </div>\n <TextInputContainer>\n <InputText\n textRef={titleRef}\n text={title}\n setText={setTitle}\n isTitle={true}\n />\n <InputText\n textRef={markdownTextRef}\n text={markdownText}\n setText={setMarkdownText}\n isTitle={false}\n />\n </TextInputContainer>\n </InputConainer>\n </Container>\n);\n" }, "Calimero.Common.Popups.InputPopup": { "": "const Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n`;\n\nconst customStyle = {\n border: '1px solid #dc3545',\n outline: 'none',\n};\n\nconst FunctionButton = styled.button`\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.base};`};\n :hover {\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.hover};`};\n }\n color: #fff;\n border-radius: 4px;\n margin-top: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n position: absolute;\n right: 1rem;\n cursor: pointer;\n`;\n\nconst UserList = styled.div`\n position: absolute;\n left: 0px;\n top: 7rem;\n overflow-y: scroll;\n max-height: 150px;\n width: 100%;\n background-color: #1d1d21;\n border-radius: 4px;\n padding: 8px;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst UserListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n cursor: pointer;\n :hover {\n background-color: #25252a;\n }\n`;\n\nconst UserInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst UserText = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst ErrorWrapper = styled.div`\n color: #dc3545;\n /* Body/Small */\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-top: 6px;\n`;\n\nconst RulesWrapper = styled.div`\n color: #6c757d;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-top: 6px;\n`;\n\nconst EmptyMessageContainer = styled.div`\n height: 27px;\n`;\n\nconst IconSvg = styled.svg`\n position: absolute;\n top: 50%;\n right: 13px;\n`;\n\nconst ExclamationIcon = () => (\n <IconSvg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"#dc3545\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M8.99951 2.74918C5.54773 2.74918 2.74951 5.5474 2.74951 8.99918C2.74951 12.451 5.54773 15.2492 8.99951 15.2492C12.4513 15.2492 15.2495 12.451 15.2495 8.99918C15.2495 5.5474 12.4513 2.74918 8.99951 2.74918ZM1.74951 8.99918C1.74951 4.99511 4.99545 1.74918 8.99951 1.74918C13.0036 1.74918 16.2495 4.99511 16.2495 8.99918C16.2495 13.0032 13.0036 16.2492 8.99951 16.2492C4.99545 16.2492 1.74951 13.0032 1.74951 8.99918ZM8.334 5.058C8.42856 4.95669 8.56093 4.89918 8.69951 4.89918H9.29951C9.4381 4.89918 9.57046 4.95669 9.66503 5.058C9.75959 5.15931 9.80786 5.29532 9.79833 5.43358L9.49833 9.78358C9.48025 10.0457 9.2623 10.2492 8.99951 10.2492C8.73672 10.2492 8.51878 10.0457 8.5007 9.78358L8.2007 5.43358C8.19116 5.29532 8.23944 5.15931 8.334 5.058ZM9.89951 12.2992C9.89951 12.7962 9.49657 13.1992 8.99951 13.1992C8.50246 13.1992 8.09951 12.7962 8.09951 12.2992C8.09951 11.8021 8.50246 11.3992 8.99951 11.3992C9.49657 11.3992 9.89951 11.8021 9.89951 12.2992Z\"\n fill=\"#DC3545\"\n />\n </IconSvg>\n);\n\nconst InputWrapper = styled.div`\n position: relative;\n`;\n\nconst {\n title,\n toggle,\n placeholder,\n functionLoader,\n buttonText,\n componentOwnerId,\n validator,\n colors,\n isChild,\n autocomplete,\n nonInvitedUserList,\n} = props;\n\nconst BaseModal = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.BaseModal`}\n props={{\n ...props,\n componentOwnerId,\n }}\n />\n);\n\nconst [isOpen, setIsOpen] = useState(false);\nconst [isProcessing, setIsProcessing] = useState(false);\nconst [inputValue, setInputValue] = useState('');\nconst [validInput, setValidInput] = useState(false);\nconst [errorMessage, setErrorMessage] = useState('');\nconst [chatUsersNotInMembers, setChatUsersNotInMembers] = useState([]);\nconst [showAutocomplete, setShowAutocomplete] = useState(false);\n\nconst runProcess = () => {\n setIsProcessing(true);\n functionLoader(inputValue).then((receipt) => {\n if (receipt === undefined) {\n setValidInput(false);\n setErrorMessage('This user does not exist.');\n setIsProcessing(false);\n } else {\n setIsProcessing(false);\n setIsOpen(false);\n }\n });\n};\n\nconst onOpenChange = (isOpen) => {\n if (isProcessing && !isOpen) {\n return;\n }\n setIsOpen(isOpen);\n};\n\nconst handleClosePopup = () => {\n if (isProcessing) return;\n setIsOpen(false);\n};\n\nconst isInvalid = inputValue && validator && !validInput && errorMessage;\n\nconst AutocompleteContainer = ({ value, inviteUsers, selectUser }) => {\n return (\n <>\n {inviteUsers.length && (\n <UserList>\n {inviteUsers.map(\n (user, id) =>\n user.id !== value && (\n <UserListItem key={id} onClick={() => selectUser(user.id)}>\n <UserInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n active: user.active,\n componentOwnerId,\n }}\n />\n <UserText>{user.id}</UserText>\n </UserInfo>\n </UserListItem>\n ),\n )}\n </UserList>\n )}\n </>\n );\n};\n\nconst selectUser = (userId) => {\n setInputValue(userId);\n setValidInput(validator(userId));\n setShowAutocomplete(false);\n};\n\nconst popupContent = (\n <>\n <CloseButton onClick={handleClosePopup}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n <Text>{title}</Text>\n <InputWrapper>\n <Input\n onChange={(e) => {\n setInputValue(e.target.value);\n if (validator) {\n const { isValid, error } = validator(e.target.value);\n setValidInput(isValid);\n setErrorMessage(error ? error : '');\n }\n if (e.target.value) {\n setShowAutocomplete(true);\n } else {\n setShowAutocomplete(false);\n }\n }}\n value={inputValue}\n placeholder={placeholder}\n style={isInvalid ? customStyle : {}}\n />\n {isInvalid && <ExclamationIcon />}\n </InputWrapper>\n {isInvalid && errorMessage ? (\n <ErrorWrapper>{errorMessage}</ErrorWrapper>\n ) : (\n <RulesWrapper>\n Invite users whose wallets end with '.near' for access\n </RulesWrapper>\n )}\n {autocomplete &&\n inputValue &&\n nonInvitedUserList.length > 0 &&\n showAutocomplete && (\n <AutocompleteContainer\n value={inputValue}\n inviteUsers={nonInvitedUserList}\n selectUser={selectUser}\n />\n )}\n <FunctionButton\n onClick={runProcess}\n disabled={inputValue ? isInvalid : true}\n >\n {isProcessing ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n buttonText\n )}\n </FunctionButton>\n </>\n);\n\nreturn (\n <BaseModal\n toggle={toggle}\n content={popupContent}\n open={isOpen}\n onOpenChange={onOpenChange}\n isChild={isChild}\n />\n);\n" }, "Calimero.DocsChain.Sidebar.Header": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n color: #777583;\n &:hover {\n color: #ffffff;\n }\n a {\n text-decoration: none;\n color: inherit;\n\n &:hover {\n color: #ffffff;\n }\n }\n width: 317px;\n`;\n\nconst TextBold = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n`;\n\nconst IconPlusContainer = styled.div`\n display: flex;\n cursor: pointer;\n justify-content: center;\n align-items: center;\n font-size: 1.25rem;\n`;\n\nreturn (\n <Container>\n <TextBold>{props.title}</TextBold>\n {props.onChange && (\n <IconPlusContainer onClick={props.onChange}>\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.${props.componentName}`,\n )}\n >\n <i className=\"bi bi-plus-circle\" />\n </a>\n </IconPlusContainer>\n )}\n </Container>\n);\n" }, "Calimero.Curb.Popups.ChannelDetailsPopup": { "": "const componentOwnerId = props.componentOwnerId;\nconst chat = props.chat;\nconst curbApi = props.curbApi;\nconst toggle = props.toggle;\nconst initialTab = props.aboutSelected ?? true;\n\nconst [isOpen, setIsOpen] = useState(false);\nconst [channelMeta, setChannelMeta] = useState({});\nconst [channelUserList, setChannelUserList] = useState([]);\n\nuseEffect(() => {\n curbApi.getChannelMeta(chat.name).then(setChannelMeta);\n}, []);\n\nconst generator = () => curbApi.getChannelMembers(chat.name);\nconst cachedMembers = useCache(generator, 'channel_members', {\n subscribe: true,\n});\n\nuseEffect(() => {\n if (cachedMembers) {\n setChannelUserList(cachedMembers);\n }\n}, [cachedMembers]);\n\nconst popupContent = (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Settings.DetailsContainer`}\n props={{\n componentOwnerId,\n channelName: chat.type === 'channel' ? chat.name : chat.id,\n aboutSelected,\n onSwitch: () => setAboutSelected(!aboutSelected),\n channelMeta,\n handleLeaveChannel: () => curbApi.leaveChannel(chat.name),\n userList: channelUserList,\n addMember: curbApi.inviteUser,\n promoteModerator: (accountId, isMod) =>\n curbApi.promoteModerator(accountId, chat.name, isMod),\n removeUserFromChannel: (accountId) =>\n curbApi.removeUserFromChannel(accountId, chat.name),\n curbApi,\n }}\n />\n);\n\nconst BaseModal = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.BaseModal`}\n props={{\n ...props,\n componentOwnerId,\n }}\n />\n);\n\nreturn (\n <BaseModal\n toggle={toggle}\n content={popupContent}\n open={isOpen}\n onOpenChange={setIsOpen}\n />\n);\n" }, "Calimero.TaskChain.BoardContainer.Column": { "": "const {\n tasks,\n title,\n submitComment,\n onTaskDragStop,\n onTaskDragStart,\n onTaskDragOver,\n functionLoader,\n componentOwnerId,\n createTask,\n projectId,\n contract,\n columns,\n setColumns,\n addActionStatus,\n removeColumn,\n columnIndex,\n removeTaskFromColumn,\n addTaskToColumn,\n updateTaskInColumn,\n} = props;\n\nconst ColumnContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n width: 17.5rem;\n margin-left: 2em;\n margin-right: 2em;\n background-color: transparent;\n cursor: pointer;\n :hover {\n background-color: #11121a;\n }\n padding: 1rem;\n`;\n\nconst ColumnDetailsIcon = styled.div`\n color: transparent;\n`;\n\nconst ColumnLabel = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-top: 4px;\n padding-bottom: 4px;\n gap: 0.5rem;\n cursor: pointer;\n width: 15rem;\n :hover {\n ${ColumnDetailsIcon} {\n color: #ffffff;\n }\n }\n`;\n\nconst TasksContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding-bottom: 16px;\n padding-left: 5px;\n max-height: 83%;\n overflow-y: scroll;\n scroll-behavior: smooth;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst AddTaskContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n`;\n\nconst ColumnLabelText = styled.div`\n display: flex;\n justify-content: start;\n align-items: start;\n`;\n\nconst AddIcon = styled.div`\n color: #777583;\n`;\n\nconst AddTaskButton = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n background-color: #1e1f28;\n color: #ffffff;\n width: 240px;\n padding: 16px;\n gap: 4px;\n border-radius: 4px;\n :hover {\n ${AddIcon} {\n color: #ffffff;\n }\n }\n`;\n\nconst AddTaskText = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n margin-left: 0.25rem;\n`;\n\nconst Title = styled.div`\n padding: 0;\n margin: 0;\n max-width: 100%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst createTaskButton = (\n <AddTaskButton>\n <AddIcon>\n <i className=\"bi bi-plus-circle-fill\"></i>\n </AddIcon>\n <AddTaskText>Add new Card</AddTaskText>\n </AddTaskButton>\n);\n\nconst columnDetailsButton = (\n <ColumnDetailsIcon>\n <i className=\"bi bi-pen-fill\"></i>\n </ColumnDetailsIcon>\n);\n\nconst [createStatus, setCreateStatus] = useState([]);\nconst [columnName, setColumnName] = useState(title);\nconst [selectedTaskDetails, setSelectedTaskDetails] = useState(null);\nconst [isTaskDetailsVisible, setIsTaskDetailsVisible] = useState(false);\n\nconst cleanCreateTaskStatus = (title) => {\n const filteredStatuses = createStatus.filter((s) => s.title !== title);\n setCreateStatus(filteredStatuses);\n\n Near.asyncCalimeroView(contract, 'get_statuses', {\n project_id: projectId,\n }).then((columns) => {\n setColumns(columns);\n });\n};\n\nconst onViewTaskDetails = (task) => {\n setSelectedTaskDetails(task);\n setIsTaskDetailsVisible(true);\n};\n\nconst onCloseTaskDetails = () => {\n setSelectedTaskDetails(null);\n setIsTaskDetailsVisible(false);\n};\n\nconst addCreateTaskStatus = useCallback((newStatus) => {\n let isNew = true;\n let newCreateTaskStatuses = createStatus.map((status) => {\n if (status.title === newStatus.title) {\n isNew = false;\n return newStatus;\n } else {\n return status;\n }\n });\n if (isNew) {\n newCreateTaskStatuses = [...newCreateTaskStatuses, newStatus];\n }\n setCreateStatus(newCreateTaskStatuses);\n}, []);\n\nreturn (\n <>\n {isTaskDetailsVisible && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.TaskDetailsDialog`}\n props={{\n task: selectedTaskDetails,\n componentOwnerId,\n onCloseTaskDetails,\n removeTaskFromColumn,\n projectId,\n contract,\n addActionStatus,\n taskColumn: columnName,\n handleColumns,\n columnIndex,\n updateTaskInColumn,\n }}\n />\n )}\n <ColumnContainer\n droppable\n onDragOver={(e) => {\n onTaskDragOver();\n }}\n onDrop={() => onTaskDragStop(columnName)}\n >\n <ColumnLabel>\n <Title>{columnName}</Title>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.ColumnDetailsPopup`}\n props={{\n componentOwnerId,\n columnIndex,\n columnName,\n setColumnName,\n removeColumn,\n contract,\n projectId,\n addActionStatus,\n containsTasks: tasks.length > 0,\n columnDetailsButton,\n }}\n />\n </ColumnLabel>\n <TasksContainer>\n {tasks.map((task, i) => {\n return (\n <div key={task.id}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.TaskCard`}\n props={{\n task: task,\n status: columnName,\n index: i,\n onTaskDragStart: onTaskDragStart,\n onTaskDragStop: onTaskDragStop,\n createStatus,\n onViewTaskDetails: onViewTaskDetails,\n }}\n />\n </div>\n );\n })}\n </TasksContainer>\n <AddTaskContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.CreateTaskPopup`}\n props={{\n createTaskButton,\n componentOwnerId,\n addCreateTaskStatus,\n cleanCreateTaskStatus,\n contract,\n handleColumns,\n projectId,\n columnTitle: columnName,\n addTaskToColumn,\n columnIndex,\n }}\n />\n </AddTaskContainer>\n </ColumnContainer>\n </>\n);\n" }, "Calimero.TaskChain.SideMenu.BoardList": { "": "const {\n setSelectedProjectId,\n selectedProjectId,\n boards,\n onChangeShowEditBoardDialog,\n createBoardStatus,\n componentOwnerId,\n} = props;\n\nconst BoardList = styled.div`\n justify-content: center;\n width: 100%;\n max-height: 85%;\n padding: 10px;\n overflow-y: scroll;\n scroll-behavior: smooth;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst [hoverId, setHoverId] = useState(-1);\n\nreturn (\n <BoardList>\n {(boards || []).map((board) => {\n const boardStatus = props.createBoardStatus.find(\n (b) => b.name === board[1],\n );\n return (\n <Widget\n key={board[0]}\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.BoardItem`}\n props={{\n id: board[0],\n name: board[1],\n onClick: () => setSelectedProjectId(board[0]),\n onMouseEnter: () => setHoverId(board[0]),\n onMouseLeave: () => setHoverId(-1),\n onSettingsClick: () => onChangeShowEditBoardDialog(true, board),\n hoverId,\n selectedProjectId,\n boardStatus,\n componentOwnerId,\n }}\n />\n );\n })}\n </BoardList>\n);\n" }, "Calimero.TaskChain.SideMenu.EditBoardOptions": { "": "const {\n onChangeShowDeleteBoardDialog,\n editedBoardId,\n addMemberInputOpen,\n componentOwnerId,\n setSelectedUser,\n setMemberInputOpen,\n selectedUser,\n boardMembers,\n selectedOption,\n users,\n addMember,\n addMemberStatus,\n onSelectRemovingMember,\n} = props;\n\nswitch (selectedOption) {\n case 1:\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.BoardOptions.BoardDetails`}\n props={{\n options: [\n {\n buttonText: 'Delete board',\n onClick: () => onChangeShowDeleteBoardDialog(true),\n },\n ],\n }}\n />\n );\n case 2:\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.BoardOptions.BoardMembers`}\n props={{\n addMemberInputOpen,\n componentOwnerId,\n setSelectedUser,\n setMemberInputOpen,\n selectedUser,\n boardMembers,\n users,\n addMember,\n addMemberStatus,\n onSelectRemovingMember,\n }}\n />\n );\n}\n" }, "Calimero.Gateway.Dropdown.Dropdown": { "": "const backgroundColor = props.backgroundColor ?? '#1E1F28';\nconst width = props.width ?? '14.5625rem';\nconst borderColor = props.border ?? '#9c9da326';\nconst expanded = props.expanded ?? false;\nconst apps = props.apps ?? [];\nconst onAppClick = props.onAppClick ?? null;\nconst componentOwnerId = props.componentOwnerId;\n\nconst applicationList = {\n id: 'applicationList',\n name: 'Application List',\n icon: (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"100%\"\n height=\"100%\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M1 2C1 1.73478 1.10536 1.48043 1.29289 1.29289C1.48043 1.10536 1.73478 1 2 1H4C4.26522 1 4.51957 1.10536 4.70711 1.29289C4.89464 1.48043 5 1.73478 5 2V4C5 4.26522 4.89464 4.51957 4.70711 4.70711C4.51957 4.89464 4.26522 5 4 5H2C1.73478 5 1.48043 4.89464 1.29289 4.70711C1.10536 4.51957 1 4.26522 1 4V2ZM6 2C6 1.73478 6.10536 1.48043 6.29289 1.29289C6.48043 1.10536 6.73478 1 7 1H9C9.26522 1 9.51957 1.10536 9.70711 1.29289C9.89464 1.48043 10 1.73478 10 2V4C10 4.26522 9.89464 4.51957 9.70711 4.70711C9.51957 4.89464 9.26522 5 9 5H7C6.73478 5 6.48043 4.89464 6.29289 4.70711C6.10536 4.51957 6 4.26522 6 4V2ZM11 2C11 1.73478 11.1054 1.48043 11.2929 1.29289C11.4804 1.10536 11.7348 1 12 1H14C14.2652 1 14.5196 1.10536 14.7071 1.29289C14.8946 1.48043 15 1.73478 15 2V4C15 4.26522 14.8946 4.51957 14.7071 4.70711C14.5196 4.89464 14.2652 5 14 5H12C11.7348 5 11.4804 4.89464 11.2929 4.70711C11.1054 4.51957 11 4.26522 11 4V2ZM1 7C1 6.73478 1.10536 6.48043 1.29289 6.29289C1.48043 6.10536 1.73478 6 2 6H4C4.26522 6 4.51957 6.10536 4.70711 6.29289C4.89464 6.48043 5 6.73478 5 7V9C5 9.26522 4.89464 9.51957 4.70711 9.70711C4.51957 9.89464 4.26522 10 4 10H2C1.73478 10 1.48043 9.89464 1.29289 9.70711C1.10536 9.51957 1 9.26522 1 9V7ZM6 7C6 6.73478 6.10536 6.48043 6.29289 6.29289C6.48043 6.10536 6.73478 6 7 6H9C9.26522 6 9.51957 6.10536 9.70711 6.29289C9.89464 6.48043 10 6.73478 10 7V9C10 9.26522 9.89464 9.51957 9.70711 9.70711C9.51957 9.89464 9.26522 10 9 10H7C6.73478 10 6.48043 9.89464 6.29289 9.70711C6.10536 9.51957 6 9.26522 6 9V7ZM11 7C11 6.73478 11.1054 6.48043 11.2929 6.29289C11.4804 6.10536 11.7348 6 12 6H14C14.2652 6 14.5196 6.10536 14.7071 6.29289C14.8946 6.48043 15 6.73478 15 7V9C15 9.26522 14.8946 9.51957 14.7071 9.70711C14.5196 9.89464 14.2652 10 14 10H12C11.7348 10 11.4804 9.89464 11.2929 9.70711C11.1054 9.51957 11 9.26522 11 9V7ZM1 12C1 11.7348 1.10536 11.4804 1.29289 11.2929C1.48043 11.1054 1.73478 11 2 11H4C4.26522 11 4.51957 11.1054 4.70711 11.2929C4.89464 11.4804 5 11.7348 5 12V14C5 14.2652 4.89464 14.5196 4.70711 14.7071C4.51957 14.8946 4.26522 15 4 15H2C1.73478 15 1.48043 14.8946 1.29289 14.7071C1.10536 14.5196 1 14.2652 1 14V12ZM6 12C6 11.7348 6.10536 11.4804 6.29289 11.2929C6.48043 11.1054 6.73478 11 7 11H9C9.26522 11 9.51957 11.1054 9.70711 11.2929C9.89464 11.4804 10 11.7348 10 12V14C10 14.2652 9.89464 14.5196 9.70711 14.7071C9.51957 14.8946 9.26522 15 9 15H7C6.73478 15 6.48043 14.8946 6.29289 14.7071C6.10536 14.5196 6 14.2652 6 14V12ZM11 12C11 11.7348 11.1054 11.4804 11.2929 11.2929C11.4804 11.1054 11.7348 11 12 11H14C14.2652 11 14.5196 11.1054 14.7071 11.2929C14.8946 11.4804 15 11.7348 15 12V14C15 14.2652 14.8946 14.5196 14.7071 14.7071C14.5196 14.8946 14.2652 15 14 15H12C11.7348 15 11.4804 14.8946 11.2929 14.7071C11.1054 14.5196 11 14.2652 11 14V12Z\"\n fill=\"white\"\n />\n </svg>\n ),\n};\n\nState.init({\n expanded: expanded,\n selectedApp: props.default ?? applicationList,\n});\n\nconst arrowUp = (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M14.7071 12.7071C14.3166 13.0976 13.6834 13.0976 13.2929 12.7071L10 9.41421L6.70712 12.7071C6.3166 13.0976 5.68343 13.0976 5.29291 12.7071C4.90238 12.3166 4.90238 11.6834 5.29291 11.2929L9.2929 7.29289C9.68342 6.90237 10.3166 6.90237 10.7071 7.29289L14.7071 11.2929C15.0976 11.6834 15.0976 12.3166 14.7071 12.7071Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nconst arrowDown = (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M7.29289 14.7071C6.90237 14.3166 6.90237 13.6834 7.29289 13.2929L10.5858 10L7.29289 6.70712C6.90237 6.3166 6.90237 5.68343 7.29289 5.29291C7.68342 4.90238 8.31658 4.90238 8.70711 5.29291L12.7071 9.2929C13.0976 9.68343 13.0976 10.3166 12.7071 10.7071L8.70711 14.7071C8.31658 15.0976 7.68342 15.0976 7.29289 14.7071Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nconst Dropdown = styled.div`\n display: ${(props) => (props.expanded ? 'block' : 'none')};\n width: 100%;\n border-top: 1px solid ${borderColor};\n opacity: ${(props) => (props.expanded ? 1 : 0)};\n max-height: ${(props) => (props.expanded ? '1000px' : '0')};\n transition:\n max-height 0.3s ease-in-out,\n opacity 0.3s ease-in-out; /* Apply transitions */\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #9c9da326;\n`;\n\nconst DropdownContainer = styled.div`\n width: ${width};\n overflow: hidden;\n border-radius: 0.25rem;\n background-color: ${backgroundColor};\n`;\n\nconst SelectedApp = styled.div`\n background-color: ${backgroundColor};\n width: 100%;\n cursor: pointer;\n align-items: center;\n display: inline-flex;\n flex-direction: row;\n justify-content: space-between;\n`;\n\nconst Arrow = styled.div`\n padding: 1rem;\n`;\n\nconst handleWidgetClick = (app) => {\n State.update({\n expanded: false,\n selectedApp: app,\n });\n onAppClick(app);\n};\n\nconst toggleDropdown = () => {\n State.update({\n expanded: !state.expanded,\n });\n};\n\nreturn (\n <DropdownContainer>\n <SelectedApp onClick={toggleDropdown}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Gateway.Dropdown.Element`}\n props={{\n app: state.selectedApp,\n backgroundColor: backgroundColor,\n componentOwnerId: componentOwnerId,\n }}\n />\n {apps.length > 0 && <Arrow>{state.expanded ? arrowUp : arrowDown}</Arrow>}\n </SelectedApp>\n <Dropdown expanded={state.expanded}>\n {apps.map((app, index) => {\n return (\n <div key={app.id}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Gateway.Dropdown.Element`}\n props={{\n app: app,\n onClick: () => handleWidgetClick(app),\n backgroundColor: backgroundColor,\n componentOwnerId: componentOwnerId,\n }}\n />\n {index !== apps.length - 1 && <Divider />}{' '}\n {/* Render divider for all but the last element */}\n </div>\n );\n })}\n </Dropdown>\n </DropdownContainer>\n);\n" }, "Calimero.TaskChain.ProfileIcon.UserProfileIcon": { "": "const ProfileIconContainer = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n ${({ width }) => width && `width: ${width};`}\n ${({ height }) => height && `height: ${width};`}\n`;\n\nconst ActiveStatusCricle = styled.div`\n position: absolute;\n bottom: -2px;\n right: -2px;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n ${({ active }) =>\n active ? 'background-color: #00FF66;' : 'background-color: #777583;'}\n border: 1px solid #1A1A1D;\n`;\nconst accountId = props.accountId;\nconst width = props.width ?? '24px';\nconst height = props.height ?? '24px';\n\nreturn (\n <ProfileIconContainer width={width} height={height}>\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.Image`}\n props={{\n accountId,\n alt: `profile-icon-${accountId}`,\n className: 'rounded-circle',\n style: { width: width, height: height, objectFit: 'cover' },\n thumbnail: 'thumbnail',\n fallbackUrl: 'https://i.imgur.com/e8buxpa.png',\n }}\n />\n </ProfileIconContainer>\n);\n" }, "Calimero.TaskChain.ProfileIcon.Image": { "": "// Forked from: rubycoptest.testnet/widget/Image\n\nconst accountId = props.accountId;\nconst className = props.className;\nconst style = props.style;\nconst alt = props.alt;\nconst fallbackUrl = props.fallbackUrl;\nconst thumbnail = props.thumbnail;\nconst componentOwnerId = props.componentOwnerId;\n\nconst [imageUrl, setImageUrl] = useState(\n 'https://ipfs.near.social/ipfs/bafkreidoxgv2w7kmzurdnmflegkthgzaclgwpiccgztpkfdkfzb4265zuu',\n);\n\nfunction toUrl(image) {\n return (\n (image.ipfs_cid\n ? `https://ipfs.near.social/ipfs/${image.ipfs_cid}`\n : image.url) || fallbackUrl\n );\n}\n\nconst thumb = (imageUrl) =>\n thumbnail && imageUrl && !imageUrl.startsWith('data:image/')\n ? `https://i.near.social/${thumbnail}/${imageUrl}`\n : imageUrl;\n\nuseEffect(() => {\n const profile = Social.getr(`${accountId}/profile`);\n const image = profile.image;\n if (image) {\n const imageUrl = thumb(toUrl(image));\n setImageUrl(imageUrl);\n } else {\n setImageUrl(fallbackUrl);\n }\n}, [accountId, fallbackUrl, thumbnail]);\n\nreturn <img className={className} style={style} src={imageUrl} alt={alt} />;\n" }, "Calimero.TaskChain.UserDropdown": { "": "const users = props.users;\nconst componentOwnerId = props.componentOwnerId;\nconst onClick = props.onClick;\nconst onClose = props.onClose;\nconst onAdd = props.onAdd;\nconst onSelect = props.onSelect;\nconst selectedUser = props.selectedUser;\nconst addMemberStatus = props.addMemberStatus;\n\nconst UserListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n cursor: pointer;\n :hover {\n background-color: #d0fc42;\n }\n background-color: #1d1d21;\n`;\n\nconst Container = styled.div`\n z-index: 30;\n outline-color: #777583;\n outline-style: solid;\n outline-width: 1px;\n ${({ selected }) =>\n selected\n ? 'border-radius: 4px 4px 4px 4px;'\n : 'border-radius: 4px 4px 0px 0px;'}\n width: inherit;\n font-family: Helvetica Neue;\n`;\n\nconst UserList = styled.div`\n overflow-y: scroll;\n max-height: 200px;\n position: absolute;\n z-index: 30;\n outline-color: #777583;\n outline-style: solid;\n outline-width: 1px;\n border-radius: 0px 0px 4px 4px;\n width: inherit;\n scroll-behavior: smooth;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-track {\n background-color: #1d1d21;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst UserInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst UserId = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst AddUserInput = styled.div`\n color: #fff;\n display: flex;\n border-radius: 4px;\n height: 40px;\n padding-top: 8px;\n padding-bottom: 8px;\n padding-left: 16px;\n padding-right: 16px;\n border: none;\n width: 100%;\n background-color: #1d1d21;\n`;\n\nconst CloseUserDropdownButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n position: absolute;\n right: 10px;\n top: 10px;\n`;\n\nreturn (\n <Container selected={selectedUser || users.length === 0}>\n <AddUserInput>\n {users.length > 0 ? selectedUser?.id : 'No users to add'}\n </AddUserInput>\n {!selectedUser && users.length > 0 && (\n <UserList>\n {users.map((user) => (\n <>\n <UserListItem key={user.id} onClick={() => onClick(user)}>\n <UserInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n componentOwnerId,\n }}\n />\n <UserId>{user.id}</UserId>\n </UserInfo>\n </UserListItem>\n </>\n ))}\n </UserList>\n )}\n <CloseUserDropdownButton onClick={onClose}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseUserDropdownButton>\n </Container>\n);\n" }, "Calimero.Curb.SideSelector.SideSelector": { "": "const users = props.users;\nconst channels = props.channels;\nconst componentOwnerId = props.componentOwnerId;\nconst activeChat = props.activeChat;\nconst onChatSelected = props.onChatSelected;\nconst curbApi = props.curbApi;\nconst isSidebarOpen = props.isSidebarOpen;\nconst communities = props.communities;\nconst enableCommunities = props.enableCommunities;\nconst handleContractChange = props.handleContractChange;\nconst selectedCommunity = props.selectedCommunity;\n\nconst HorizontalSeparatorLine = styled.div`\n background-color: '#BF4F74';\n height: 1px;\n background-color: #282933;\n margin-top: 1rem;\n margin-bottom: 1rem;\n @media (max-width: 1024px) {\n width: 100%;\n }\n`;\n\nconst SideMenu = styled.div`\n background-color: #0e0e10;\n padding-top: 1rem;\n width: 318px;\n overflow-y: scroll;\n height: calc(100vh - 169px);\n @media (max-width: 1024px) {\n display: none;\n }\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst SideMenuMobile = styled.div`\n display: none;\n background-color: #0e0e10;\n padding-top: 1rem;\n overflow-y: scroll;\n height: 100vh;\n @media (max-width: 1024px) {\n display: block;\n position: relative;\n z-index: 10;\n padding-top: 64px;\n width: 100%;\n padding-bottom: 30px;\n }\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst CommunityDropdownContainer = styled.div`\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0.25rem;\n`;\n\nconst SideMenuContent = () => {\n return (\n <>\n {enableCommunities && communities && (\n <>\n <CommunityDropdownContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.CommunityDropdown`}\n props={{\n componentOwnerId,\n communities,\n handleContractChange,\n selectedCommunity,\n }}\n />\n </CommunityDropdownContainer>\n </>\n )}\n {enableCommunities && communities && <HorizontalSeparatorLine />}\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.SideSelector.ChannelsHeader`}\n props={{\n title: 'Channel',\n componentOwnerId,\n curbApi,\n }}\n />\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.SideSelector.ChannelList`}\n props={{\n channels,\n selectChannel: onChatSelected,\n selectedChannelId:\n activeChat.type === 'channel' ? activeChat.name : null,\n componentOwnerId,\n }}\n />\n <HorizontalSeparatorLine />\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.SideSelector.DMSideSelector`}\n props={{\n componentOwnerId,\n curbApi,\n users,\n onDMSelected: onChatSelected,\n selectedDM:\n activeChat.type === 'direct_message' ? activeChat.id : null,\n createDM: (value) =>\n new Promise((resolve) => {\n onChatSelected({\n id: value,\n type: 'direct_message',\n });\n resolve();\n }),\n }}\n />\n </>\n );\n};\nreturn (\n <>\n {isSidebarOpen && (\n <SideMenuMobile>\n <SideMenuContent />\n </SideMenuMobile>\n )}\n <SideMenu>\n <SideMenuContent />\n </SideMenu>\n </>\n);\n" }, "Calimero.Curb.Popups.StartDMPopup": { "": "const SuggestionsDropdown = styled.div`\n max-height: 200px;\n overflow-y: auto;\n border-radius: 4px;\n background-color: #0e0e10;\n position: absolute;\n top: 100%;\n width: 100%;\n z-index: 10;\n`;\n\nconst SuggestionItem = styled.div`\n padding: 8px 16px;\n color: #fff;\n cursor: pointer;\n &:hover {\n background-color: #1f1f21;\n }\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n`;\n\nconst customStyle = {\n border: '1px solid #dc3545',\n outline: 'none',\n};\n\nconst FunctionButton = styled.button`\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.base};`};\n :hover {\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.hover};`};\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n position: absolute;\n right: 1rem;\n cursor: pointer;\n`;\n\nconst ErrorWrapper = styled.div`\n color: #dc3545;\n /* Body/Small */\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-top: 6px;\n`;\n\nconst EmptyMessageContainer = styled.div`\n height: 27px;\n`;\n\nconst IconSvg = styled.svg`\n position: absolute;\n top: 50%;\n right: 13px;\n`;\n\nconst ExclamationIcon = () => (\n <IconSvg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"#dc3545\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M8.99951 2.74918C5.54773 2.74918 2.74951 5.5474 2.74951 8.99918C2.74951 12.451 5.54773 15.2492 8.99951 15.2492C12.4513 15.2492 15.2495 12.451 15.2495 8.99918C15.2495 5.5474 12.4513 2.74918 8.99951 2.74918ZM1.74951 8.99918C1.74951 4.99511 4.99545 1.74918 8.99951 1.74918C13.0036 1.74918 16.2495 4.99511 16.2495 8.99918C16.2495 13.0032 13.0036 16.2492 8.99951 16.2492C4.99545 16.2492 1.74951 13.0032 1.74951 8.99918ZM8.334 5.058C8.42856 4.95669 8.56093 4.89918 8.69951 4.89918H9.29951C9.4381 4.89918 9.57046 4.95669 9.66503 5.058C9.75959 5.15931 9.80786 5.29532 9.79833 5.43358L9.49833 9.78358C9.48025 10.0457 9.2623 10.2492 8.99951 10.2492C8.73672 10.2492 8.51878 10.0457 8.5007 9.78358L8.2007 5.43358C8.19116 5.29532 8.23944 5.15931 8.334 5.058ZM9.89951 12.2992C9.89951 12.7962 9.49657 13.1992 8.99951 13.1992C8.50246 13.1992 8.09951 12.7962 8.09951 12.2992C8.09951 11.8021 8.50246 11.3992 8.99951 11.3992C9.49657 11.3992 9.89951 11.8021 9.89951 12.2992Z\"\n fill=\"#DC3545\"\n />\n </IconSvg>\n);\n\nconst InputWrapper = styled.div`\n position: relative;\n`;\n\nconst {\n title,\n toggle,\n placeholder,\n onAccountSelected,\n buttonText,\n componentOwnerId,\n fetchAccounts,\n validator,\n colors,\n functionLoader,\n} = props;\n\nconst BaseModal = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.BaseModal`}\n props={{\n ...props,\n componentOwnerId,\n }}\n />\n);\n\nconst [isOpen, setIsOpen] = useState(false);\nconst [isProcessing, setIsProcessing] = useState(false);\nconst [inputValue, setInputValue] = useState('');\nconst [validInput, setValidInput] = useState(false);\nconst [errorMessage, setErrorMessage] = useState('');\nconst [suggestions, setSuggestions] = useState([]);\n\nconst inputRef = useRef('');\n\nconst isInvalid = inputValue && validator && !validInput && errorMessage;\n\nuseEffect(() => {\n inputRef.current = inputValue;\n}, [inputValue]);\n\nconst runProcess = () => {\n setIsProcessing(true);\n functionLoader(inputValue).then((_) => {\n setIsProcessing(false);\n setIsOpen(false);\n });\n};\n\nconst onOpenChange = (isOpen) => {\n if (isProcessing && !isOpen) {\n return;\n }\n setIsOpen(isOpen);\n};\n\nconst debouncedFetchUsers = (value) => {\n setTimeout(\n () =>\n fetchAccounts(value).then((users) => {\n const accounts = users.map((user) => user.id);\n setSuggestions(accounts);\n }),\n 200,\n );\n};\n\nconst handleInputChange = (e) => {\n const value = e.target.value;\n setInputValue(value);\n\n if (validator) {\n const { isValid, error } = validator(value);\n setValidInput(isValid);\n setErrorMessage(error ? error : '');\n }\n\n debouncedFetchUsers(value);\n};\n\nconst handleClosePopup = () => {\n if (isProcessing) return;\n setIsOpen(false);\n};\n\nconst handleSuggestionClick = (suggestion) => {\n onAccountSelected(suggestion);\n setIsOpen(false);\n};\n\nconst popupContent = (\n <>\n <CloseButton onClick={handleClosePopup}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n <Text>{title}</Text>\n <InputWrapper>\n <Input\n onChange={handleInputChange}\n value={inputValue}\n placeholder={placeholder}\n style={isInvalid ? customStyle : {}}\n />\n {isInvalid && <ExclamationIcon />}\n </InputWrapper>\n {isInvalid ? (\n <ErrorWrapper>{errorMessage}</ErrorWrapper>\n ) : (\n <EmptyMessageContainer />\n )}\n {suggestions.length > 0 && (\n <SuggestionsDropdown>\n {suggestions.map((suggestion, index) => (\n <SuggestionItem\n key={index}\n onClick={() => handleSuggestionClick(suggestion)}\n >\n {suggestion}\n </SuggestionItem>\n ))}\n </SuggestionsDropdown>\n )}\n <FunctionButton\n onClick={runProcess}\n disabled={inputValue ? isInvalid : true}\n >\n {isProcessing ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n buttonText\n )}\n </FunctionButton>\n </>\n);\n\nreturn (\n <BaseModal\n toggle={toggle}\n content={popupContent}\n open={isOpen}\n onOpenChange={onOpenChange}\n />\n);\n" }, "Calimero.Curb.NavbarContainer": { "": "const activeChat = props.activeChat;\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\nconst isSidebarOpen = props.isSidebarOpen;\nconst setIsSidebarOpen = props.setIsSidebarOpen;\nconst channelSelected = props.channelSelected;\nconst enableCommunities = props.enableCommunities;\n\nconst [appName, setAppName] = useState('');\n\nuseEffect(() => {\n curbApi.getAppName().then(setAppName);\n}, [curbApi]);\n\nconst ChannelNavbarContainer = (props) => {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ChannelNavbarContainer`}\n props={props}\n />\n );\n};\n\nconst DMNavbarContainer = (props) => {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.CurbNavbar`}\n props={props}\n />\n );\n};\n\nconst props = {\n appName,\n activeChat,\n componentOwnerId,\n curbApi,\n isSidebarOpen,\n setIsSidebarOpen,\n channelSelected:\n activeChat.type === 'channel' ? activeChat.name : activeChat.id,\n enableCommunities,\n};\n\nreturn (\n <>\n {activeChat.type === 'channel' ? (\n <ChannelNavbarContainer {...props} />\n ) : (\n <DMNavbarContainer {...props} />\n )}\n </>\n);\n" }, "Calimero.Curb.Chat.MessageInput": { "": "const {\n componentOwnerId,\n threadReply,\n selectedChat,\n sendMessage,\n openThread,\n isThread,\n isReadOnly,\n isOwner,\n isModerator,\n} = props;\n\nconst Container = styled.div`\n position: absolute;\n bottom: 16px;\n padding-left: 16px;\n padding-right: 16px;\n padding-top: 12px;\n padding-bottom: 12px;\n background-color: #1d1d21;\n display: flex;\n align-items: end;\n @media (min-width: 1025px) {\n gap: 8px;\n border-radius: 4px;\n }\n @media (max-width: 1024px) {\n position: fixed;\n margin: 0 !important;\n left: 0;\n right: 0;\n bottom: 0px;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n gap: 4px;\n margin: 0px;\n padding-left: 8px;\n padding-right: 8px;\n padding-bottom: 12px;\n padding-top: 12px;\n width: 100% !important;\n }\n`;\n\nconst EmojiPopupContainer = styled.div`\n position: absolute;\n bottom: 70px;\n right: 2.5rem;\n`;\n\nconst UploadPopupContainer = styled.div`\n position: absolute;\n bottom: 46px;\n left: 16px;\n @media (max-width: 1024px) {\n left: 8px;\n }\n`;\n\nconst UploadContainer = styled.div`\n background-color: #25252a;\n border-radius: 2px;\n width: fit-content;\n`;\n\nconst [showEmojiPopup, setShowEmojiPopup] = useState(false);\nconst [showUpload, setShowUpload] = useState(false);\nconst [message, setMessage] = useState('');\nconst [showMarkdown, setShowMarkdown] = useState(false);\nconst [uploadedFile, setUploadedFile] = useState(null);\nconst [uploadedImage, setUploadedImage] = useState(null);\nconst [emojiSelectorOpen, setEmojiSelectorOpen] = useState(false);\nconst [error, setError] = useState('');\n\nconst updateShowMarkdown = useCallback(() => {\n if (message) {\n setShowMarkdown(!showMarkdown);\n }\n}, [message, showMarkdown]);\n\nconst handleMessageChange = useCallback((mesage) => {\n setMessage(mesage);\n}, []);\n\nconst resetFile = useCallback(() => {\n setUploadedFile(null);\n setShowUpload(false);\n}, []);\n\nconst resetImage = useCallback(() => {\n setUploadedImage(null);\n setShowUpload(false);\n}, []);\n\nconst resetMessage = useCallback(() => setMessage(''), []);\nconst emptyText = /^(\\s*<p><br><\\/p>\\s*)*$/;\nconst markdownParser = (text) => {\n const toHTML = text.replace(\n /(\\b(https?:\\/\\/[^\\s<]+\\/?)\\b)|^(#####|####|###|##|#) (.*)$|(@everyone)|(@here)|(@[a-z\\d]+[-_]*[a-z\\d]+[-_]*[a-z\\d]+\\.(near|testnet))|<p><br><\\/p>(?=\\s*$)/gim,\n (\n match,\n url,\n url2,\n heading,\n text,\n everyoneMention,\n hereMention,\n validMention,\n ) => {\n if (url || url2) {\n return `<a href=\"${url || url2}\" class=\"url-link\" target=\"_blank\">${\n url || url2\n }</a>`;\n } else if (heading) {\n return text;\n } else if (everyoneMention) {\n return `<span class='mention-everyone'>@everyone</span>`;\n } else if (hereMention) {\n return `<span class='mention-here'>@here</span>`;\n } else if (validMention) {\n return `<span class='mention mention-user-${validMention\n .replace('@', '')\n .replace(/\\./g, '\\\\.')\n .replace(/_/g, '\\\\_')}'>${validMention}</span>`;\n } else {\n return '';\n }\n },\n );\n\n return toHTML;\n};\n\nconst isActive =\n (message && !emptyText.test(markdownParser(message))) ||\n uploadedImage ||\n uploadedFile;\n\nconst handleSendMessage = useCallback(() => {\n if (\n (uploadedFile && !uploadedFile.file.cid) ||\n (uploadedImage && !uploadedImage.file.cid)\n ) {\n return;\n } else if (\n emptyText.test(markdownParser(message)) &&\n !uploadedImage &&\n !uploadedFile\n ) {\n handleMessageChange('');\n } else {\n sendMessage(\n markdownParser(message),\n uploadedImage,\n uploadedFile,\n openThread,\n );\n resetImage();\n resetFile();\n setShowUpload(false);\n setEmojiSelectorOpen(false);\n handleMessageChange('');\n }\n}, [message, uploadedImage, uploadedFile, openThread]);\n\nconst [selectedEmoji, setSelectedEmoji] = useState('');\n\nconst Wrapper = styled.div`\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: start;\n background-color: #111111;\n`;\n\nconst FullWidthWrapper = styled.div`\n width: 100%;\n display: flex;\n flex-direction: column;\n`;\n\nconst IconUploadWrapper = styled.div`\n height: 34px;\n width: 34px;\n padding: 8px;\n display: flex;\n justify-content: center;\n border-radius: 2px;\n align-items: center;\n :hover {\n background-color: #686672;\n fill: #fff;\n }\n cursor: pointer;\n fill: #686672;\n`;\n\nconst IconUpload = ({ onClick }) => (\n <IconUploadWrapper>\n <svg\n onClick={onClick}\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n className=\"bi bi-plus-circle\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z\" />\n <path d=\"M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z\" />\n </svg>\n </IconUploadWrapper>\n);\n\nconst EmojiContainer = styled.div`\n border-radius: 2px;\n margin-bottom: 4px;\n height: 26px;\n width: 26px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 2px;\n cursor: pointer;\n\n .hidden-svg {\n visibility: hidden;\n position: absolute;\n z-index: -10;\n }\n\n .visible-svg {\n visibility: visible;\n }\n\n @media (max-width: 1024px) {\n display: none;\n }\n`;\n\nconst IconEmoji = () => {\n const [hovered, setHovered] = useState(false);\n\n return (\n <EmojiContainer\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill=\"#686672\"\n className={`bi bi-emoji-wink ${hovered ? 'hidden-svg' : 'visible-svg'}`}\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z\" />\n <path d=\"M4.285 9.567a.5.5 0 0 1 .683.183A3.498 3.498 0 0 0 8 11.5a3.498 3.498 0 0 0 3.032-1.75.5.5 0 1 1 .866.5A4.498 4.498 0 0 1 8 12.5a4.498 4.498 0 0 1-3.898-2.25.5.5 0 0 1 .183-.683zM7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5zm1.757-.437a.5.5 0 0 1 .68.194.934.934 0 0 0 .813.493c.339 0 .645-.19.813-.493a.5.5 0 1 1 .874.486A1.934 1.934 0 0 1 10.25 7.75c-.73 0-1.356-.412-1.687-1.007a.5.5 0 0 1 .194-.68z\" />\n </svg>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill=\"#FFDD1D\"\n className={`bi bi-emoji-wink-fill ${\n hovered ? 'visible-svg' : 'hidden-svg'\n }`}\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM7 6.5C7 5.672 6.552 5 6 5s-1 .672-1 1.5S5.448 8 6 8s1-.672 1-1.5zM4.285 9.567a.5.5 0 0 0-.183.683A4.498 4.498 0 0 0 8 12.5a4.5 4.5 0 0 0 3.898-2.25.5.5 0 1 0-.866-.5A3.498 3.498 0 0 1 8 11.5a3.498 3.498 0 0 1-3.032-1.75.5.5 0 0 0-.683-.183zm5.152-3.31a.5.5 0 0 0-.874.486c.33.595.958 1.007 1.687 1.007.73 0 1.356-.412 1.687-1.007a.5.5 0 0 0-.874-.486.934.934 0 0 1-.813.493.934.934 0 0 1-.813-.493z\" />\n </svg>\n </EmojiContainer>\n );\n};\n\nconst IconSendSvg = styled.svg`\n margin-bottom: 8px;\n :hover {\n fill: #4e95ff;\n }\n cursor: pointer;\n`;\nconst IconSend = ({ onClick, isActive }) => (\n <IconSendSvg\n onClick={onClick}\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill={`${isActive ? '#4E95FF' : '#686672'}`}\n className=\"bi bi-send-fill\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M15.964.686a.5.5 0 0 0-.65-.65L.767 5.855H.766l-.452.18a.5.5 0 0 0-.082.887l.41.26.001.002 4.995 3.178 3.178 4.995.002.002.26.41a.5.5 0 0 0 .886-.083l6-15Zm-1.833 1.89L6.637 10.07l-.215-.338a.5.5 0 0 0-.154-.154l-.338-.215 7.494-7.494 1.178-.471-.47 1.178Z\" />\n </IconSendSvg>\n);\n\nconst Placeholder = styled.div`\n position: absolute;\n z-index: 10;\n bottom: ${({ placeholderPosition }) =>\n placeholderPosition && placeholderPosition};\n left: 68px;\n color: #686672;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n pointer-events: none;\n @media (max-width: 1024px) {\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n bottom: ${({ placeholderPositionMobile }) =>\n placeholderPositionMobile && placeholderPositionMobile};\n left: 56px;\n }\n`;\n\nconst getCustomStyle = (openThread) => {\n const customStyle = {\n width: 'calc(100% - 440px)',\n marginLeft: '2.5rem',\n marginRight: '2.5rem',\n };\n if (openThread && !isThread) {\n customStyle.width = 'calc(60% - 262px)';\n customStyle.marginRight = '1.25rem';\n } else if (!openThread && !isThread) {\n customStyle.width = 'calc(100% - 440px)';\n } else if (openThread && isThread) {\n customStyle.width = 'calc(40% - 212px)';\n customStyle.marginLeft = '0rem';\n customStyle.marginRight = '1.25rem';\n }\n return customStyle;\n};\n\nconst ReadOnlyField = styled.div`\n background-color: #111111;\n height: 2rem;\n border-radius: 4px;\n padding: 4px 8px 4px 8px;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n color: #797978;\n flex: 1;\n @media (max-width: 1024px) {\n font-size: 14px;\n display: flex;\n align-items: center;\n }\n`;\n\nconst FileUrl = styled.a`\n cursor: pointer;\n text-decoration: none;\n color: #ffdd1d;\n cursor: pointer;\n :hover {\n color: #ffdd1d;\n text-decoration: underline;\n }\n :visited {\n color: #d0fc42;\n }\n`;\n\nlet canWriteMessage = false;\nif (isReadOnly) {\n if (isModerator || isOwner) {\n canWriteMessage = true;\n } else {\n canWriteMessage = false;\n }\n} else {\n canWriteMessage = true;\n}\n\nlet placeholderPosition = '16px';\nlet placeholderPositionMobile = '16px';\nif (uploadedFile.file.cid) {\n placeholderPosition = '61px';\n placeholderPositionMobile = '51px';\n} else if (uploadedImage.file.cid) {\n placeholderPosition = '86px';\n placeholderPositionMobile = '80px';\n}\n\nconst ImageIconSvg = () => (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"#fff\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M15.75 12.75C15.75 13.7446 15.3549 14.6984 14.6517 15.4017C13.9484 16.1049 12.9946 16.5 12 16.5C11.0054 16.5 10.0516 16.1049 9.34835 15.4017C8.64509 14.6984 8.25 13.7446 8.25 12.75C8.25 11.7554 8.64509 10.8016 9.34835 10.0983C10.0516 9.39509 11.0054 9 12 9C12.9946 9 13.9484 9.39509 14.6517 10.0983C15.3549 10.8016 15.75 11.7554 15.75 12.75Z\"\n fill=\"white\"\n />\n <path\n d=\"M3 6C2.20435 6 1.44129 6.31607 0.87868 6.87868C0.316071 7.44129 0 8.20435 0 9V18C0 18.7956 0.316071 19.5587 0.87868 20.1213C1.44129 20.6839 2.20435 21 3 21H21C21.7956 21 22.5587 20.6839 23.1213 20.1213C23.6839 19.5587 24 18.7956 24 18V9C24 8.20435 23.6839 7.44129 23.1213 6.87868C22.5587 6.31607 21.7956 6 21 6H19.242C18.4464 5.99983 17.6835 5.68365 17.121 5.121L15.879 3.879C15.3165 3.31635 14.5536 3.00017 13.758 3H10.242C9.44641 3.00017 8.68348 3.31635 8.121 3.879L6.879 5.121C6.31652 5.68365 5.55358 5.99983 4.758 6H3ZM3.75 9C3.55109 9 3.36032 8.92098 3.21967 8.78033C3.07902 8.63968 3 8.44891 3 8.25C3 8.05109 3.07902 7.86032 3.21967 7.71967C3.36032 7.57902 3.55109 7.5 3.75 7.5C3.94891 7.5 4.13968 7.57902 4.28033 7.71967C4.42098 7.86032 4.5 8.05109 4.5 8.25C4.5 8.44891 4.42098 8.63968 4.28033 8.78033C4.13968 8.92098 3.94891 9 3.75 9ZM17.25 12.75C17.25 14.1424 16.6969 15.4777 15.7123 16.4623C14.7277 17.4469 13.3924 18 12 18C10.6076 18 9.27226 17.4469 8.28769 16.4623C7.30312 15.4777 6.75 14.1424 6.75 12.75C6.75 11.3576 7.30312 10.0223 8.28769 9.03769C9.27226 8.05312 10.6076 7.5 12 7.5C13.3924 7.5 14.7277 8.05312 15.7123 9.03769C16.6969 10.0223 17.25 11.3576 17.25 12.75Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nconst FileIconSvg = () => (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"#fff\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g clipPath=\"url(#clip0_952_64107)\">\n <path\n d=\"M13.9395 0H6C5.20435 0 4.44129 0.316071 3.87868 0.87868C3.31607 1.44129 3 2.20435 3 3V21C3 21.7956 3.31607 22.5587 3.87868 23.1213C4.44129 23.6839 5.20435 24 6 24H18C18.7956 24 19.5587 23.6839 20.1213 23.1213C20.6839 22.5587 21 21.7956 21 21V7.0605C20.9999 6.66271 20.8418 6.28124 20.5605 6L15 0.4395C14.7188 0.158176 14.3373 8.49561e-05 13.9395 0V0ZM14.25 5.25V2.25L18.75 6.75H15.75C15.3522 6.75 14.9706 6.59196 14.6893 6.31066C14.408 6.02936 14.25 5.64782 14.25 5.25ZM9.75 8.25V9.201L10.5735 8.7255C10.6588 8.67548 10.7532 8.64283 10.8512 8.62943C10.9492 8.61603 11.0489 8.62214 11.1445 8.64743C11.2401 8.67271 11.3298 8.71665 11.4084 8.77674C11.487 8.83682 11.5529 8.91185 11.6023 8.9975C11.6518 9.08316 11.6838 9.17776 11.6966 9.27584C11.7093 9.37393 11.7025 9.47357 11.6766 9.56902C11.6507 9.66447 11.6062 9.75386 11.5456 9.83203C11.4849 9.9102 11.4095 9.97561 11.3235 10.0245L10.5 10.5L11.3235 10.9755C11.4095 11.0244 11.4849 11.0898 11.5456 11.168C11.6062 11.2461 11.6507 11.3355 11.6766 11.431C11.7025 11.5264 11.7093 11.6261 11.6966 11.7242C11.6838 11.8222 11.6518 11.9168 11.6023 12.0025C11.5529 12.0882 11.487 12.1632 11.4084 12.2233C11.3298 12.2833 11.2401 12.3273 11.1445 12.3526C11.0489 12.3779 10.9492 12.384 10.8512 12.3706C10.7532 12.3572 10.6588 12.3245 10.5735 12.2745L9.75 11.799V12.75C9.75 12.9489 9.67098 13.1397 9.53033 13.2803C9.38968 13.421 9.19891 13.5 9 13.5C8.80109 13.5 8.61032 13.421 8.46967 13.2803C8.32902 13.1397 8.25 12.9489 8.25 12.75V11.799L7.4265 12.2745C7.34117 12.3245 7.24679 12.3572 7.14879 12.3706C7.0508 12.384 6.95111 12.3779 6.85549 12.3526C6.75987 12.3273 6.67019 12.2833 6.59162 12.2233C6.51304 12.1632 6.44713 12.0882 6.39768 12.0025C6.34822 11.9168 6.3162 11.8222 6.30345 11.7242C6.2907 11.6261 6.29748 11.5264 6.32339 11.431C6.34931 11.3355 6.39385 11.2461 6.45445 11.168C6.51505 11.0898 6.59052 11.0244 6.6765 10.9755L7.5 10.5L6.6765 10.0245C6.50565 9.92434 6.38134 9.76066 6.33072 9.56919C6.2801 9.37772 6.30727 9.174 6.40629 9.00248C6.50532 8.83096 6.66817 8.70558 6.8593 8.65369C7.05043 8.6018 7.25433 8.62761 7.4265 8.7255L8.25 9.201V8.25C8.25 8.05109 8.32902 7.86032 8.46967 7.71967C8.61032 7.57902 8.80109 7.5 9 7.5C9.19891 7.5 9.38968 7.57902 9.53033 7.71967C9.67098 7.86032 9.75 8.05109 9.75 8.25ZM6.75 15H14.25C14.4489 15 14.6397 15.079 14.7803 15.2197C14.921 15.3603 15 15.5511 15 15.75C15 15.9489 14.921 16.1397 14.7803 16.2803C14.6397 16.421 14.4489 16.5 14.25 16.5H6.75C6.55109 16.5 6.36032 16.421 6.21967 16.2803C6.07902 16.1397 6 15.9489 6 15.75C6 15.5511 6.07902 15.3603 6.21967 15.2197C6.36032 15.079 6.55109 15 6.75 15ZM6.75 18H14.25C14.4489 18 14.6397 18.079 14.7803 18.2197C14.921 18.3603 15 18.5511 15 18.75C15 18.9489 14.921 19.1397 14.7803 19.2803C14.6397 19.421 14.4489 19.5 14.25 19.5H6.75C6.55109 19.5 6.36032 19.421 6.21967 19.2803C6.07902 19.1397 6 18.9489 6 18.75C6 18.5511 6.07902 18.3603 6.21967 18.2197C6.36032 18.079 6.55109 18 6.75 18Z\"\n fill=\"white\"\n />\n </g>\n <defs>\n <clipPath id=\"clip0_952_64107\">\n <rect width=\"24\" height=\"24\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n);\nconst ErrorMessage = styled.div`\n color: red;\n`;\n\nconst ErrorContainer = styled.div`\n position: relative;\n top: 0;\n padding-top: 4px;\n padding-bottom: 4px;\n padding-left: 8px;\n display: flex;\n width: 206px;\n color: #dc3545;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n background-color: #25252a;\n border-radius: 2px;\n`;\nreturn (\n <>\n {canWriteMessage && (\n <Container\n style={getCustomStyle(openThread, isThread)}\n key={openThread.id}\n >\n {/* <IconUpload\n onClick={() => {\n setError(\"\");\n if (!uploadedFile.file.cid && !uploadedImage.file.cid) {\n setEmojiSelectorOpen(false);\n setShowUpload(!showUpload);\n }\n }}\n /> */}\n <Wrapper>\n <FullWidthWrapper>\n <MarkdownEditor\n setValue={setMessage}\n value={message}\n onChange={handleMessageChange}\n selectedEmoji={selectedEmoji}\n resetSelectedEmoji={() => setSelectedEmoji('')}\n handleMessageSent={handleSendMessage}\n />\n </FullWidthWrapper>\n {(!message || emptyText.test(markdownParser(message))) && (\n <Placeholder placeholderPosition={placeholderPosition}>\n {openThread && isThread\n ? `Reply in thread`\n : `Type message in ${selectedChat}`}\n </Placeholder>\n )}\n\n {uploadedFile.file.cid && (\n <>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.MessageFileField`}\n props={{\n file: uploadedFile.file,\n resetFile,\n }}\n />\n </>\n )}\n {uploadedImage.file.cid && (\n <>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.MessageImageField`}\n props={{\n file: uploadedImage.file,\n resetImage,\n }}\n />\n </>\n )}\n </Wrapper>\n <div onClick={() => setEmojiSelectorOpen(!emojiSelectorOpen)}>\n <IconEmoji />\n </div>\n <IconSend\n onClick={() => {\n if (isActive) {\n handleSendMessage();\n }\n }}\n isActive={isActive}\n />\n {emojiSelectorOpen && (\n <EmojiPopupContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.EmojiSelector.EmojiSelector`}\n props={{\n OnEmojiSelected: (emoji) => setSelectedEmoji(emoji),\n }}\n key={'message-input-emoji-component'}\n />\n </EmojiPopupContainer>\n )}\n {showUpload && !uploadedFile.file.cid && !uploadedImage.file.cid && (\n <UploadPopupContainer>\n {error && <ErrorContainer>{error}</ErrorContainer>}\n <UploadContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.UploadComponent`}\n props={{\n uploadedFile: uploadedImage,\n setUploadedFile: setUploadedImage,\n type: ['image/jpeg', 'image/png', 'image/gif'],\n icon: <ImageIconSvg />,\n text: 'Upload Image',\n setError: setError,\n }}\n key=\"images-component\"\n />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.UploadComponent`}\n props={{\n uploadedFile: uploadedFile,\n setUploadedFile: setUploadedFile,\n type: ['*/*'],\n icon: <FileIconSvg />,\n text: 'Upload File',\n setError: setError,\n }}\n key=\"files-component\"\n />\n </UploadContainer>\n </UploadPopupContainer>\n )}\n </Container>\n )}\n {!canWriteMessage && (\n <Container\n style={getCustomStyle(openThread, isThread)}\n key={openThread.id}\n >\n <ReadOnlyField>\n You don't have permissions to write in this channel\n </ReadOnlyField>\n </Container>\n )}\n </>\n);\n" }, "Calimero.TaskChain.Main": { "": "const contract = props.contract || 'taskchain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId || 'calimero.testnet';\nconst accountId = context.accountId;\n\nconst PageContainer = styled.div`\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background-color: #0e0e10;\n padding-left: 60px;\n padding-right: 60px;\n padding-top: 30px;\n padding-bottom: 30px;\n gap: 24px;\n`;\n\nconst TaskChainLogo = styled.a`\n display: flex;\n flex-direction: space-between;\n align-items: center\n width: 100%;\n height: 100%;\n background-color: #0E0E10;\n color: #FFFFFF;\n font-family: Inter;\n font-weight: 700;\n font-size: 20.92px;\n line-height: 31.39px;\n :hover {\n text-decoration: none;\n }\n`;\n\nconst HeaderContainer = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n position: relative;\n`;\n\nconst BoardContainer = styled.div`\n padding-top: 8px;\n display: flex;\n flex-direction: row;\n justify-content: start;\n width: 100%;\n height: 100%;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n`;\n\nconst [selectedProjectId, setSelectedProjectId] = useState(undefined);\nconst [bootStrapping, setBootStrapping] = useState(true);\nconst [loggedIn, setLoggedIn] = useState(false);\nconst [functionLoader, setFunctionLoader] = useState(false);\nconst [actionStatuses, setActionStatuses] = useState([]);\n\nconst addActionStatus = useCallback(\n (newActionStatus) => {\n setActionStatuses([...actionStatuses, newActionStatus]);\n },\n [actionStatuses],\n);\n\nconst setActionStatusNotVisible = (actionStatus) => {\n setActionStatuses(\n actionStatuses.map((status) => {\n if (status.id === actionStatus.id) {\n return { ...actionStatus, seen: true };\n } else {\n return actionStatus;\n }\n }),\n );\n};\n\nconst bootStrapApp = () =>\n Near.hasValidCalimeroFak(contract).then((result) => {\n if (result) {\n setLoggedIn(true);\n setBootStrapping(false);\n } else {\n setLoggedIn(false);\n setBootStrapping(false);\n }\n });\n\nconst Logo = () => (\n <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.TaskChainLogo`} />\n);\n\nif (!accountId) {\n return <>Please login to continue.</>;\n}\n\nif (bootStrapping) {\n bootStrapApp();\n return (\n <PageContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.Loading`}\n props={{\n logo: <Logo />,\n }}\n />\n </PageContainer>\n );\n}\n\nif (!loggedIn) {\n return (\n <PageContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.JoinApp`}\n props={{\n contractName: contract,\n componentOwnerId,\n }}\n />\n </PageContainer>\n );\n}\n\nreturn (\n <PageContainer>\n <HeaderContainer>\n <Logo />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ActionStatus.StatusContainer`}\n props={{\n componentOwnerId,\n actionStatuses,\n setActionStatusNotVisible,\n }}\n />\n </HeaderContainer>\n <Divider>.</Divider>\n <BoardContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.SideMenu`}\n props={{\n contract,\n componentOwnerId,\n selectedProjectId,\n setSelectedProjectId,\n functionLoader,\n setFunctionLoader,\n accountId,\n addActionStatus,\n }}\n />\n {selectedProjectId && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Board`}\n props={{\n componentOwnerId,\n contract,\n projectId: selectedProjectId,\n addActionStatus,\n addNewStatus,\n }}\n />\n )}\n </BoardContainer>\n </PageContainer>\n);\n" } } } } }

Transaction Execution Plan

Convert Transaction To Receipt
Gas Burned:
3 Tgas
Tokens Burned:
0.00036 
Receipt:
Predecessor ID:
Receiver ID:
Gas Burned:
186 Tgas
Tokens Burned:
0.01861 
Called method: 'set' in contract: social.near
Arguments:
{ "data": { "calimero.near": { "widget": { "Calimero.Curb.Chat.UploadComponent": { "": "const uploadedFile = props.uploadedFile;\nconst setUploadedFile = props.setUploadedFile;\nconst type = props.type;\nconst icon = props.icon;\nconst text = props.text;\nconst setError = props.setError;\n\nconst uploadFileUpdateState = (body) => {\n setError('');\n asyncFetch('https://ipfs.near.social/add', {\n method: 'POST',\n headers: { Accept: 'application/json' },\n body,\n }).then((res) => {\n if (res.status === 500) {\n setError('Maximum file size is 10MB!');\n setUploadedFile(undefined);\n } else {\n setError('');\n const cid = res.body.cid;\n setUploadedFile({ file: { cid, name: body.name } });\n }\n });\n};\n\nconst filesOnChange = (files) => {\n if (files) {\n setUploadedFile({ file: { uploading: true, cid: null } });\n uploadFileUpdateState(files[0]);\n }\n};\n\nconst ButtonUpload = styled.div`\n .custom-files {\n width: 206px;\n padding: 8px 16px; /* 8px top and bottom, 16px left and right */\n color: #fff; /* Text color */\n font-family: 'Helvetica Neue'; /* Font family */\n font-size: 16px; /* Font size */\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 24px */\n cursor: pointer;\n border-radius: 2px;\n :hover {\n background-color: #686672;\n }\n }\n`;\n\nreturn (\n <div className=\"d-flex justify-center align-items-center mt-2\">\n <ButtonUpload>\n <Files\n multiple={false}\n accepts={type}\n minFileSize={1}\n maxFileSize={10000000}\n clickable\n className=\"custom-files\"\n onChange={filesOnChange}\n >\n {icon}\n {uploadedFile.file?.uploading ? 'Uploading' : text}\n </Files>\n </ButtonUpload>\n </div>\n);\n" }, "Calimero.Gateway.Dropdown.Element": { "": "const app = props.app ?? { name: 'App', icon: null };\nconst icon = props.icon ?? null;\nconst backgroundColor = props.backgroundColor ?? '#090723';\nconst onClick = props.onClick ?? null;\nconst componentOwnerId = props.componentOwnerId;\n\nconst Card = styled.div`\n display: inline-flex;\n flex-direction: row;\n align-items: center;\n background-color: ${backgroundColor};\n cursor: pointer;\n padding: 1rem;\n overflow: hidden;\n cursor: ${onClick ? 'pointer' : 'default'};\n width: 100%;\n`;\n\nconst Title = styled.div`\n color: #fff;\n /* text-sm/leading-5/font-medium */\n font-family: Inter;\n font-size: 0.875rem;\n font-style: normal;\n font-weight: 500;\n line-height: 1.25rem;\n margin-left: 1rem;\n`;\n\nreturn (\n <Card onClick={onClick}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Gateway.Dropdown.Icon`}\n props={{\n icon: app.icon,\n }}\n />\n <Title>{app.name}</Title>\n </Card>\n);\n" }, "Calimero.DocsChain.Members.Main": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst PageContainer = styled.div`\n width: 100%;\n height: 100vh;\n padding-left: 60px;\n padding-right: 60px;\n background-color: #0e0e10;\n color: #ffffff;\n`;\n\nreturn (\n <PageContainer>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Navbar.HorizontalNavbar'}\n config={redirectConfig}\n />\n <div className=\"d-flex\">\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.DocsSidebar'}\n config={redirectConfig}\n props={{\n onOpenCreatePage: state.onOpenCreatePage,\n createPageOpen: state.createPage.open,\n }}\n />\n </div>\n </PageContainer>\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.Comment": { "": "const { comment, componentOwnerId } = props;\n\nconst CommentContainer = styled.div`\n width: 100%;\n display: flex;\n flex-direction: column;\n`;\n\nconst CommenterInfoContainer = styled.div`\n width: 100%;\n display: flex;\n align-items: center;\n column-gap: 0.5rem;\n display: flex;\n justify-content: flex-start;\n`;\n\nconst ProfileIconContainerMsg = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n text-align: center;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n`;\n\nconst NameContainerCommenter = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n color: #6c757d;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 100%;\n padding-top: 2px;\n`;\n\nconst TimeText = styled.p`\n color: #adb5bd;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 100%;\n padding-top: 1rem;\n`;\n\nconst MessageText = styled.div`\n max-width: 100%;\n position: relative;\n word-wrap: break-word;\n display: flex;\n flex-direction: column;\n padding: 1rem;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n border-radius: 0px 8px 8px 8px;\n background-color: #1e1f28;\n`;\n\nconst formatTimeAgo = (seconds) => {\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const weeks = Math.floor(days / 7);\n const months = Math.floor(weeks / 4);\n\n if (months > 0) {\n return `${months} month${months > 1 ? 's' : ''} ago`;\n } else if (weeks > 0) {\n return `${weeks} week${weeks > 1 ? 's' : ''} ago`;\n } else if (days > 0) {\n return `${days} day${days > 1 ? 's' : ''} ago`;\n } else if (hours > 0) {\n return `${hours} hour${hours > 1 ? 's' : ''} ago`;\n } else if (minutes > 0) {\n return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;\n } else {\n return `just now`;\n }\n};\n\nreturn (\n <CommentContainer>\n <CommenterInfoContainer\n ownMessage={props.message.sender === context.accountId}\n >\n <ProfileIconContainerMsg>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: comment.member_id,\n showStatus: false,\n componentOwnerId: componentOwnerId,\n }}\n />\n </ProfileIconContainerMsg>\n <NameContainerCommenter>{comment.member_id}</NameContainerCommenter>\n <TimeText>{formatTimeAgo((Date.now() - comment.date) / 1000)}</TimeText>\n </CommenterInfoContainer>\n <MessageText>{comment.message}</MessageText>\n </CommentContainer>\n);\n" }, "Calimero.Curb.Communities.SearchContainer": { "": "const componentOwnerId = props.componentOwnerId;\nconst handleSearch = props.handleSearch;\nconst isSearched = props.isSearched;\nconst searchedTerm = props.searchedTerm;\nconst searchResultCount = props.searchResultCount;\nconst resetSearch = props.resetSearch;\n\nconst SearchContainer = styled.div`\n display: flex;\n flex-direction: column;\n ${({ isSearched }) =>\n isSearched\n ? 'align-items: start; padding-bottom: 0px; background-color: transparent;'\n : 'align-items: center; justify-content: center; height: 277px; background-color: #1d1d21;'}\n width: 100%;\n top: 127px;\n left: 60px;\n border-radius: 4px;\n padding: 24px;\n color: #fff;\n font-family: Helvetica Neue;\n`;\n\nconst Title = styled.div`\n font-size: 24px;\n font-weight: 500;\n line-height: 29px;\n letter-spacing: 0em;\n height: 29px;\n margin-bottom: 11px;\n`;\n\nconst Subtitle = styled.div`\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n margin-bottom: 26px;\n`;\n\nconst SearchResultContainer = styled.div`\n display: flex;\n column-gap: 16px;\n justify-content: center;\n`;\n\nconst SearchResult = styled.h5`\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n`;\n\nconst BackIcon = styled.i`\n cursor: pointer;\n color: #6c757d;\n :hover {\n color: #fff;\n }\n`;\n\nconst searchText =\n searchResultCount === 1\n ? `${searchResultCount} community for “${searchedTerm}”`\n : `${searchResultCount} communities for “${searchedTerm}”`;\n\nreturn (\n <SearchContainer isSearched={isSearched}>\n {isSearched ? (\n <SearchResultContainer>\n <BackIcon className=\"bi bi-arrow-left\" onClick={resetSearch}></BackIcon>\n <SearchResult>{searchText}</SearchResult>\n </SearchResultContainer>\n ) : (\n <>\n <Title>Discover communities on Calimero</Title>\n <Subtitle>\n From your favourite projects to Near protocol updates...\n </Subtitle>\n </>\n )}\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.Search`}\n props={{\n componentOwnerId,\n handleSearch,\n searchedTerm,\n }}\n />\n </SearchContainer>\n);\n" }, "Calimero.Curb.Chat.MessageImageField": { "": "const file = props.file;\nconst resetImage = props.resetImage;\n\nconst RemoveButton = styled.div`\n position: relative;\n top: 0;\n right: 16px;\n z-index: 20;\n cursor: pointer;\n`;\n\nconst ResetFileIcon = ({ resetFile }) => {\n return (\n <RemoveButton onClick={resetFile}>\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 12 12\"\n fill=\"#fff\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g clipPath=\"url(#clip0_972_45209)\">\n <path\n d=\"M12 6C12 7.5913 11.3679 9.11742 10.2426 10.2426C9.11742 11.3679 7.5913 12 6 12C4.4087 12 2.88258 11.3679 1.75736 10.2426C0.632141 9.11742 0 7.5913 0 6C0 4.4087 0.632141 2.88258 1.75736 1.75736C2.88258 0.632141 4.4087 0 6 0C7.5913 0 9.11742 0.632141 10.2426 1.75736C11.3679 2.88258 12 4.4087 12 6ZM4.0155 3.4845C3.94509 3.41408 3.84958 3.37453 3.75 3.37453C3.65042 3.37453 3.55491 3.41408 3.4845 3.4845C3.41408 3.55491 3.37453 3.65042 3.37453 3.75C3.37453 3.84958 3.41408 3.94509 3.4845 4.0155L5.46975 6L3.4845 7.9845C3.44963 8.01937 3.42198 8.06076 3.40311 8.10631C3.38424 8.15187 3.37453 8.20069 3.37453 8.25C3.37453 8.29931 3.38424 8.34813 3.40311 8.39369C3.42198 8.43924 3.44963 8.48063 3.4845 8.5155C3.55491 8.58591 3.65042 8.62547 3.75 8.62547C3.79931 8.62547 3.84813 8.61576 3.89369 8.59689C3.93924 8.57802 3.98063 8.55037 4.0155 8.5155L6 6.53025L7.9845 8.5155C8.01937 8.55037 8.06076 8.57802 8.10631 8.59689C8.15187 8.61576 8.20069 8.62547 8.25 8.62547C8.29931 8.62547 8.34813 8.61576 8.39369 8.59689C8.43924 8.57802 8.48063 8.55037 8.5155 8.5155C8.55037 8.48063 8.57802 8.43924 8.59689 8.39369C8.61576 8.34813 8.62547 8.29931 8.62547 8.25C8.62547 8.20069 8.61576 8.15187 8.59689 8.10631C8.57802 8.06076 8.55037 8.01937 8.5155 7.9845L6.53025 6L8.5155 4.0155C8.55037 3.98063 8.57802 3.93924 8.59689 3.89369C8.61576 3.84813 8.62547 3.79931 8.62547 3.75C8.62547 3.70069 8.61576 3.65187 8.59689 3.60631C8.57802 3.56076 8.55037 3.51937 8.5155 3.4845C8.48063 3.44963 8.43924 3.42198 8.39369 3.40311C8.34813 3.38424 8.29931 3.37453 8.25 3.37453C8.20069 3.37453 8.15187 3.38424 8.10631 3.40311C8.06076 3.42198 8.01937 3.44963 7.9845 3.4845L6 5.46975L4.0155 3.4845Z\"\n fill=\"white\"\n />\n </g>\n <defs>\n <clipPath id=\"clip0_972_45209\">\n <rect width=\"12\" height=\"12\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n </RemoveButton>\n );\n};\n\nconst ImageWrapper = styled.div`\n display: flex;\n height: 64px;\n width: 64px;\n margin-left: 8px;\n margin-bottom: 8px;\n`;\n\nif (!file) return null;\nconst cid = file?.cid;\nreturn (\n <ImageWrapper>\n <img\n src={`https://ipfs.near.social/ipfs/${cid}`}\n alt=\"uploaded\"\n height=\"64px\"\n width=\"64px\"\n style={{\n maxHeight: '64px',\n maxWidth: '64px',\n }}\n />\n <ResetFileIcon resetFile={resetImage} />\n </ImageWrapper>\n);\n" }, "Calimero.TaskChain.Popups.ColumnDetailsPopup": { "": "const {\n componentOwnerId,\n columnIndex,\n columnName,\n setColumnName,\n contract,\n projectId,\n addActionStatus,\n removeColumn,\n containsTasks,\n columnDetailsButton,\n} = props;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst ActionsContainer = styled.div`\n display: flex;\n flex-direction: row;\n gap: 3px;\n align-items: center;\n items-center: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n flex: 1;\n height: 40px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-width: 0;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n width: 30%;\n height: 40px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n background-color: transparent;\n border: none;\n display: flex;\n justify-content: start;\n align-items: center;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst DeleteButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n display: flex;\n flex-direction: row;\n cursor: pointer;\n text-align: center;\n color: #ffffff;\n justify-content: center;\n align-items: center;\n gap: 3px;\n`;\n\nconst DeleteButtonText = styled.p`\n padding: 0;\n margin: 0;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst MissingName = styled.div`\n position: absolute;\n left: 8.6rem;\n top: 2.8rem;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst TextContainer = styled.div`\n display: flex;\n justify-content: center;\n flex-direction: column;\n width: 100%;\n text-align: center;\n margin-bottom: 1rem;\n margin-top: 1rem;\n`;\n\nconst SaveButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DeleteColumnContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst CloseDeleteColumnButton = styled.div`\n background-color: transparent;\n display: flex;\n justify-content: center;\n color: #6b7280;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst Overlay = styled.div`\n background-color: var(--blackA9);\n position: fixed;\n z-index: 1000;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);\n`;\n\nconst ModalContent = styled.div`\n position: absolute;\n width: 500px;\n padding: 30px;\n border-radius: 20px;\n color: white;\n`;\n\nconst FunctionButton = styled.button`\n ${({ backgroundColor }) =>\n backgroundColor && `background-color: ${backgroundColor};`}\n ${({ color }) => (color ? `color: ${color};` : 'color: #fff;')}\n :hover {\n ${({ hoverColor }) => hoverColor && `background-color: ${hoverColor};`}\n }\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DialogTitle = styled.p`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 15px;\n font-style: normal;\n font-weight: 700;\n line-height: 100%;\n padding: 0;\n margin: 0;\n`;\n\nconst ButtonContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 8px;\n`;\n\nconst [columnDetailsOpen, setColumnDetailsOpen] = useState(false);\nconst [editColumnName, setEditColumnName] = useState(columnName);\nconst [editColumnNameMissing, setEditColumnNameMissing] = useState(false);\nconst [deleteContent, setDeleteContent] = useState(false);\nconst [editColumnNameStatus, setEditColumnNameStatus] = useState(undefined);\n\nconst onChangeEditColumnName = (event) => {\n setEditColumnNameMissing(false);\n setEditColumnName(event.target.value);\n};\n\nconst onClose = () => {\n setColumnDetailsOpen(false);\n setEditColumnNameMissing(false);\n setEditColumnName(columnName);\n};\n\nconst handleUpdateColumnName = (columnName, newColumnName, columnIndex) => {\n if (!newColumnName || newColumnName.trim() === '') {\n setEditColumnNameMissing(true);\n return;\n }\n\n if (columnName === newColumnName) {\n return;\n }\n\n const actionStatusPrefix = 'Rename column';\n let newActionStatus = {\n id: actionStatusPrefix + projectId + columnIndex,\n status: `Saving column ${columnName} as ${newColumnName}`,\n };\n try {\n addActionStatus(newActionStatus);\n\n setColumnName(newColumnName);\n\n Near.fakCalimeroCall(contract, 'rename_status', {\n project_id: projectId,\n status_index: columnIndex,\n new_name: newColumnName,\n }).then(() => {\n newActionStatus.status = `Saved column ${columnName} as ${newColumnName}`;\n addActionStatus(newActionStatus);\n });\n } catch {\n newActionStatus.status = `Error saving column ${columnName} as ${newColumnName}`;\n addActionStatus(newActionStatus);\n setColumnName(columnName);\n setColumnDetailsOpen(false);\n }\n};\n\nconst deleteColumn = useCallback(() => {\n const actionStatusPrefix = 'Delete column';\n let newActionStatus = {\n id: actionStatusPrefix + projectId + columnIndex,\n status: `Deleting column ${columnName}`,\n seen: false,\n };\n try {\n addActionStatus(newActionStatus);\n removeColumn(columnIndex);\n\n Near.fakCalimeroCall(contract, 'remove_status', {\n project_id: projectId,\n status_index: columnIndex,\n }).then(() => {\n newActionStatus.status = `Deleted column ${columnName}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n console.log('deleteColumn', e);\n newActionStatus.status = `Error deleting column ${columnName}`;\n addActionStatus(newActionStatus);\n }\n setColumnDetailsOpen(false);\n}, [contract, projectId, columnIndex, columnName]);\n\nconst editColumnContent = (\n <Overlay>\n <ModalContent>\n <PopupContainer>\n <Header>\n <DialogTitle>Edit column name</DialogTitle>\n <CloseButton onClick={onClose}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <Label>Name</Label>\n <Input\n onChange={onChangeEditColumnName}\n value={editColumnName}\n placeholder=\"Add Name\"\n />\n {editColumnNameMissing && <MissingName>Missing name</MissingName>}\n </FieldContainer>\n <ButtonContainer>\n <FunctionButton\n backgroundColor=\"#D0FC42\"\n color=\"black\"\n hoverColor=\"#BBE33B\"\n onClick={() =>\n handleUpdateColumnName(columnName, editColumnName, columnIndex)\n }\n >\n Save\n </FunctionButton>\n <FunctionButton\n backgroundColor=\"transparent\"\n hoverColor=\"#F25757\"\n onClick={() => setDeleteContent(true)}\n >\n Delete\n </FunctionButton>\n </ButtonContainer>\n </PopupContainer>\n </ModalContent>\n </Overlay>\n);\n\nconst deleteColumnContent = (\n <Overlay>\n <ModalContent>\n <DeleteColumnContainer>\n <TextContainer>\n <Text>Are you sure you want to delete this column?</Text>\n </TextContainer>\n <FunctionButton\n backgroundColor=\"#F25757\"\n hoverColor=\"#DA4E4E\"\n onClick={deleteColumn}\n >\n Delete\n </FunctionButton>\n <CloseDeleteColumnButton onClick={() => setDeleteContent(false)}>\n Close\n </CloseDeleteColumnButton>\n </DeleteColumnContainer>\n </ModalContent>\n </Overlay>\n);\n\nconst cannotDeleteColumnContent = (\n <Overlay>\n <ModalContent>\n <DeleteColumnContainer>\n <TextContainer>\n <Text>You cannot delete column which contains tasks?</Text>\n </TextContainer>\n <CloseDeleteColumnButton onClick={() => setDeleteContent(false)}>\n Close\n </CloseDeleteColumnButton>\n </DeleteColumnContainer>\n </ModalContent>\n </Overlay>\n);\n\nconst content = deleteContent\n ? containsTasks\n ? cannotDeleteColumnContent\n : deleteColumnContent\n : editColumnContent;\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.BaseModal`}\n props={{\n toggle: columnDetailsButton,\n content,\n open: columnDetailsOpen,\n onOpenChange: (open) => setColumnDetailsOpen(open),\n }}\n />\n);\n" }, "Calimero.Curb.Settings.AboutDetails": { "": "const SettingsItem = styled.div`\n background-color: #0e0e10;\n padding-left: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n ${({ borderbottom }) => borderbottom && 'border-bottom: 1px solid #282933;'}\n ${({ roundedTop }) =>\n roundedTop &&\n 'border-top-left-radius: 0.375rem; border-top-right-radius: 0.375rem;'}\n ${({ roundedBottom }) =>\n roundedBottom &&\n 'border-bottom-left-radius: 0.375rem; border-bottom-right-radius: 0.375rem;'}\n`;\n\nconst Text = styled.h6`\n ${({ red }) =>\n red ? 'color: #DC3545; :hover { color: #f76560 }' : 'color: #FFF;'}\n /* Body/Regular */\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 24px */\n`;\n\nconst ButtonLeave = styled.button`\n background-color: transparent;\n border: none;\n padding: 0rem;\n margin: 0rem;\n`;\n\nconst timestampToDate = (timestampMs) => {\n const date = new Date(timestampMs);\n const day = date.getDate();\n const month = date.getMonth() + 1;\n const year = date.getFullYear();\n const formattedDay = day < 10 ? `0${day}` : `${day}`;\n const formattedMonth = month < 10 ? `0${month}` : `${month}`;\n return `${formattedDay}/${formattedMonth}/${year}`;\n};\n\nreturn (\n <>\n <SettingsItem borderbottom roundedTop>\n <Text>Created</Text>\n <Text>\n {props.dateCreated ? timestampToDate(props.dateCreated) : 'N/A'}\n </Text>\n </SettingsItem>\n <SettingsItem borderbottom>\n <Text>Managed by</Text>\n <Text>{props.manager}</Text>\n </SettingsItem>\n <SettingsItem roundedBottom>\n <ButtonLeave>\n <Text red onClick={props.handleLeaveChannel}>\n Leave Channel\n </Text>\n </ButtonLeave>\n </SettingsItem>\n </>\n);\n" }, "Calimero.DocsChain.Popups.JoinPopup": { "": "const PageContainer = styled.div`\n height: 100vh;\n width: 100%;\n background-color: #0e0e10;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n border-radius: 8px;\n width: 489px;\n`;\n\nconst JoinContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n text: center;\n`;\n\nconst JoinHeader = styled.div`\n display: flex;\n align-items: center;\n flex-direction: column;\n justify-content: center;\n gap: 1.5rem;\n padding-bottom: 1rem;\n padding-top: 1rem;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Button = styled.button`\n background-color: #4e95ff;\n :hover {\n background-color: #5bb0ff;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <PageContainer>\n <PopupContainer>\n <JoinContainer>\n <JoinHeader>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Logo.DocsChainLogo'}\n props={{\n justify: true,\n }}\n />\n <Text>Join DocsChain to view documents!</Text>\n <Button onClick={props.join}>Join</Button>\n </JoinHeader>\n </JoinContainer>\n </PopupContainer>\n </PageContainer>\n);\n" }, "Calimero.TaskChain.Popups.AddColumnPopup": { "": "const {\n componentOwnerId,\n addColumnButton,\n addNewColumn,\n addActionStatus,\n contract,\n projectId,\n} = props;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Title = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 14px;\n top: 55px;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DialogTitle = styled.p`\n font-family: Helvetica Neue;\n font-size: 24px;\n font-weight: 500;\n line-height: 29px;\n letter-spacing: 0em;\n text-align: left;\n padding: 0;\n margin: 0;\n color: #fff;\n`;\n\nconst Overlay = styled.div`\n background-color: var(--blackA9);\n position: fixed;\n z-index: 1000;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);\n`;\n\nconst ModalContent = styled.div`\n position: absolute;\n width: 500px;\n padding: 30px;\n border-radius: 20px;\n color: white;\n`;\n\nconst [columnName, setColumnName] = useState('');\nconst [addColumnNameMissing, setAddColumnNameMissing] = useState(false);\nconst [addColumnPopupOpen, setAddColumnPopupOpen] = useState(false);\n\nconst onChangeColumnName = ({ target }) => {\n setAddColumnNameMissing(false);\n setColumnName(target.value);\n};\n\nconst handleAddColumn = useCallback(() => {\n if (!columnName || columnName.trim() === '') {\n setAddColumnNameMissing(true);\n return;\n }\n const actionStatusPrefix = 'Add column';\n let newActionStatus = {\n id: actionStatusPrefix + columnName,\n status: `Saving column ${columnName}`,\n seen: false,\n };\n\n try {\n addActionStatus(newActionStatus);\n\n const newColumnProps = { name: columnName, tasks: [] };\n addNewColumn(newColumnProps);\n\n Near.fakCalimeroCall(contract, 'add_status', {\n project_id: projectId,\n status_name: columnName,\n }).then(() => {\n newActionStatus.status = `Saved column ${columnName}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n newActionStatus.status = `Error saving column ${columnName}`;\n addActionStatus(newActionStatus);\n }\n setAddColumnPopupOpen(false);\n setColumnName('');\n}, [columnName]);\n\nconst content = (\n <Overlay>\n <ModalContent>\n <PopupContainer>\n <Header>\n <DialogTitle>Add new Column</DialogTitle>\n <CloseButton\n onClick={() => {\n setAddColumnPopupOpen(false);\n setColumnName('');\n }}\n >\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n </Header>\n <FieldContainer>\n <Title\n onChange={onChangeColumnName}\n value={columnName}\n placeholder=\"Add Name\"\n />\n {addColumnNameMissing && <MissingTitle>Missing name</MissingTitle>}\n </FieldContainer>\n <FunctionButton onClick={handleAddColumn}>Add</FunctionButton>\n </PopupContainer>\n </ModalContent>\n </Overlay>\n);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.BaseModal`}\n props={{\n toggle: addColumnButton,\n content,\n open: addColumnPopupOpen,\n onOpenChange: (open) => setAddColumnPopupOpen(open),\n }}\n />\n);\n" }, "Calimero.Curb.WebSocketManager": { "": "const render = props.render;\n\n// dependencies\nconst Logger = props.deps.Logger;\nconst EventEmitter = props.deps.EventEmitter;\n\nlet debug = 'debug' in props ? JSON.parse(props.debug) : false;\nif (typeof debug !== 'boolean') debug = false;\n\nconst userId = props.accountId;\nconst wsAddress = props.wsAddress ?? 'ws://0.0.0.0:6376/ws';\nconst maxRetries = props.maxRetries ?? 5;\nconst reconnectDelay = props.reconnectDelay ?? 1000;\nconst maxDelay = props.maxDelay ?? 30000;\nconst getAuthToken = props.getAuthToken;\n\nconst ConnectionStatus = {\n CONNECTED: 'Connected',\n CONNECTING: 'Connecting...',\n RECONNECTING: 'Reconnecting...',\n DISCONNECTED: 'Disconnected',\n};\n\nconst retryCountRef = useRef(0);\nconst shouldReconnectRef = useRef(true);\nconst pingIntervalRef = useRef(null);\nconst wsTransport = useRef(null);\nconst connectionStatus = useRef(ConnectionStatus.DISCONNECTED);\n\nlet meroshipLogger = Logger('meroship', null, debug);\nlet logger$ = meroshipLogger.fallback();\n\nconst wsState$ = (ws) => {\n switch (ws.readyState) {\n case 0:\n return 'CONNECTING';\n case 1:\n return 'OPEN';\n case 2:\n return 'CLOSING';\n case 3:\n return 'CLOSED';\n default:\n return 'UNKNOWN';\n }\n};\n\nconst wsApiEvents = useRef(EventEmitter());\n\nconst wsApiNotifications = useRef(EventEmitter());\n\nconst setupWebSocket = () => {\n const commandEvents = EventEmitter();\n\n const ws = new WebSocket(wsAddress);\n connectionStatus.current = ConnectionStatus.CONNECTING;\n\n let retryTimeout = null;\n\n wsTransport.current = {\n send: (command, args, callback, reschedule, logger) => {\n let { log: log$, error: error$ } = logger$(logger).span(\n 'wsTransport.send',\n {\n command,\n args,\n },\n );\n\n if (ws.readyState === 1 /* OPEN */) {\n let id;\n for (;;) {\n id = Math.trunc(Math.random() * (Math.pow(2, 53) - 1));\n try {\n commandEvents.once(\n id,\n ([err, result]) => {\n if (callback)\n if (err) return callback(err);\n else callback(null, result);\n },\n true,\n );\n break;\n } catch {\n error$('request ID collision, retrying', { id });\n }\n }\n ws.send(JSON.stringify({ id, command, args }));\n if (pingIntervalRef.current) clearInterval(pingIntervalRef.current);\n pingIntervalRef.current = setInterval(ping, 15_000);\n log$('sent', { id, command, args });\n } else {\n if (typeof reschedule === 'undefined') reschedule = (f) => f();\n if (typeof reschedule === 'function') {\n wsApiEvents.current.once('connected', () =>\n setTimeout(reschedule, null, () =>\n wsTransport.current.send(\n command,\n args,\n callback,\n reschedule,\n logger,\n ),\n ),\n );\n\n log$('scheduled', {\n command,\n args,\n wsState: wsState$(ws),\n connectionStatus: connectionStatus.current,\n });\n }\n\n if (ws.readyState !== 0 /* CONNECTING */) {\n if (\n connectionStatus.current === ConnectionStatus.RECONNECTING &&\n retryTimeout\n ) {\n log$('expediting reconnection to send', {\n command,\n args,\n wsState: wsState$(ws),\n connectionStatus: connectionStatus.current,\n });\n clearTimeout(retryTimeout);\n retryTimeout = null;\n } else {\n log$('forcefully reviving WS connection to send', {\n command,\n args,\n wsState: wsState$(ws),\n connectionStatus: connectionStatus.current,\n });\n wsApiEvents.current.emit('disconnected', {\n delay: 0,\n maxRetries,\n trial: 0,\n });\n }\n setupWebSocket();\n }\n }\n },\n close: (code, reason, logger) => {\n logger$(logger).span('wsTransport.close', {\n code,\n reason,\n });\n // shouldReconnectRef.current = false;\n ws.close(code, reason);\n },\n };\n\n function ping(logger) {\n const pingLogger = logger$(logger).span('ping');\n const { log: log$ } = pingLogger;\n wsTransport.current.send(\n 'Ping',\n [],\n (err) => {\n if (err) return log$('ping failed', { err });\n if (connectionStatus.current !== ConnectionStatus.CONNECTED) {\n connectionStatus.current = ConnectionStatus.CONNECTED;\n wsTransport.current.send(\n 'Init',\n { account_id: userId },\n (err) => {\n if (err) return log$('connection initialization failed:', err);\n wsApiEvents.current.emit('connected');\n },\n (_f) => {\n /* do not reschedule if the connection is not open */\n },\n pingLogger,\n );\n }\n },\n (_f) => {\n /* do not reschedule if the connection is not open */\n },\n pingLogger,\n );\n }\n\n ws.onopen = (event) => {\n const onOpenLogger = meroshipLogger.span('WebSocket.onopen', { event });\n const { error: error$ } = onOpenLogger;\n wsTransport.current.send(\n 'Gateway::Headers',\n {\n 'x-api-key': { getAuthToken }.getAuthToken?.(),\n },\n (err) => {\n if (\n err &&\n !(\n err.type === 'ParseError' &&\n typeof err.data === 'string' &&\n // if there is no gateway infront of meroship, this error is expected, ignore it\n err.data.startsWith('unknown variant `Gateway::Headers`')\n )\n )\n return error$('gateway auth header application failed:', err);\n\n retryCountRef.current = 0;\n ping(onOpenLogger);\n },\n );\n };\n\n ws.onmessage = (event) => {\n const { log: log$ } = meroshipLogger.span('WebSocket.onmessage', { event });\n\n let message = JSON.parse(event.data);\n\n if (message) {\n if ('id' in message) {\n if ('result' in message) {\n log$('received', { id: message.id, result: message.result });\n commandEvents.emit(message.id, [null, message.result]);\n return;\n } else if ('error' in message) {\n log$('received', { id: message.id, error: message.error });\n commandEvents.emit(message.id, [message.error]);\n return;\n }\n } else if ('event' in message) {\n log$('received', { event: message.event });\n wsApiNotifications.current.emit(message.event.type, message.event.data);\n return;\n }\n }\n\n log$('unknown message', event.data);\n };\n\n let _transport = wsTransport.current;\n ws.onclose = (event) => {\n const { log: log$ } = meroshipLogger.span('WebSocket.onclose', { event });\n\n // this event was delayed, and a new connection has been established, safe to ignore\n let expiredTransport = _transport !== wsTransport.current;\n\n log$('WebSocket closed', {\n wsState: wsState$(ws),\n expiredTransport,\n connectionStatus: connectionStatus.current,\n });\n\n if (expiredTransport) return;\n if (shouldReconnectRef.current && retryCountRef.current < maxRetries) {\n let delay =\n Math.min(\n reconnectDelay * Math.pow(2, retryCountRef.current),\n maxDelay,\n ) +\n Math.random() * 1500;\n\n log$('reconnecting', {\n delay,\n trial: retryCountRef.current,\n maxRetries,\n });\n\n wsApiEvents.current.emit('disconnected', {\n delay,\n maxRetries,\n trial: retryCountRef.current,\n });\n\n retryCountRef.current++;\n retryTimeout = setTimeout(() => {\n retryTimeout = null;\n setupWebSocket();\n }, delay);\n connectionStatus.current = ConnectionStatus.RECONNECTING;\n } else {\n clearInterval(pingIntervalRef.current);\n log$('disconnected', {\n delay: 0,\n trial: retryCountRef.current,\n maxRetries,\n });\n\n wsApiEvents.current.emit('disconnected', {\n delay: 0,\n maxRetries,\n trial: retryCountRef.current,\n });\n\n connectionStatus.current = ConnectionStatus.DISCONNECTED;\n }\n };\n};\n\nconst wsApi = {\n send: (command, args, callback, reschedule) => {\n if (wsTransport.current) {\n wsTransport.current.send(command, args, callback, reschedule);\n return wsApi;\n }\n throw 'WebSocket has not been initialized';\n },\n connect: () => {\n if (connectionStatus.current === ConnectionStatus.DISCONNECTED) {\n retryCountRef.current = 0;\n setupWebSocket();\n } else if (connectionStatus.current === ConnectionStatus.CONNECTED) {\n // throw \"Already connected\";\n }\n return wsApi;\n },\n disconnect: () => {\n if (wsTransport.current) {\n if (connectionStatus.current === ConnectionStatus.DISCONNECTED) {\n // throw \"Already disconnected..\";\n } else {\n shouldReconnectRef.current = false;\n wsTransport.current.close();\n }\n } else {\n throw 'WebSocket has not been initialized';\n }\n return wsApi;\n },\n on: (event, listener) => {\n wsApiEvents.current.on(event, listener);\n return wsApi;\n },\n once: (event, listener) => {\n wsApiEvents.current.once(event, listener);\n return wsApi;\n },\n prependListener: (event, listener) => {\n wsApiEvents.current.prependListener(event, listener);\n return wsApi;\n },\n prependOnceListener: (event, listener) => {\n wsApiEvents.current.prependOnceListener(event, listener);\n return wsApi;\n },\n off: (event, listener) => {\n wsApiEvents.current.off(event, listener);\n return wsApi;\n },\n notifications: {\n on: (event, listener) => {\n wsApiNotifications.current.on(event, listener);\n return wsApi.notifications;\n },\n once: (event, listener) => {\n wsApiNotifications.current.once(event, listener);\n return wsApi.notifications;\n },\n prependListener: (event, listener) => {\n wsApiNotifications.current.prependListener(event, listener);\n return wsApi.notifications;\n },\n prependOnceListener: (event, listener) => {\n wsApiNotifications.current.prependOnceListener(event, listener);\n return wsApi.notifications;\n },\n off: (event, listener) => {\n wsApiNotifications.current.off(event, listener);\n return wsApi.notifications;\n },\n },\n methods: {\n subscribe: (args, callback, reschedule) => {\n return wsApi.send('Subscribe', args, callback, reschedule);\n },\n unsubscribe: (args, callback, reschedule) => {\n return wsApi.send('Unsubscribe', args, callback, reschedule);\n },\n submitTx: (signedTx, callback, reschedule) => {\n return wsApi.send('SubmitTransaction', [signedTx], callback, reschedule);\n },\n getAccountsStatus: (accounts, callback, reschedule) => {\n return wsApi.send('Status', { accounts }, callback, reschedule);\n },\n },\n};\n\nif (render) {\n return render({ wsApi, EventEmitter });\n}\n" }, "Calimero.TaskChain.BoardContainer.TaskColumn": { "": "const {\n tasks,\n title,\n submitComment,\n onTaskDragStop,\n onTaskDragStart,\n onTaskDragOver,\n functionLoader,\n componentOwnerId,\n handleColumns,\n createTask,\n projectId,\n contract,\n columns,\n setColumns,\n addActionStatus,\n removeColumn,\n columnIndex,\n} = props;\n\nconst ColumnContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n width: 17.5rem;\n margin-left: 2em;\n margin-right: 2em;\n background-color: transparent;\n cursor: pointer;\n :hover {\n background-color: #11121a;\n }\n padding: 1rem;\n`;\n\nconst ColumnDetailsIcon = styled.div`\n color: transparent;\n`;\n\nconst ColumnLabel = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-top: 4px;\n padding-bottom: 4px;\n gap: 0.5rem;\n cursor: pointer;\n width: 15rem;\n :hover {\n ${ColumnDetailsIcon} {\n color: #ffffff;\n }\n }\n`;\n\nconst TasksContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding-bottom: 16px;\n`;\n\nconst AddTaskContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n`;\n\nconst ColumnLabelText = styled.div`\n display: flex;\n justify-content: start;\n align-items: start;\n`;\n\nconst AddIcon = styled.div`\n color: #777583;\n`;\n\nconst AddTaskButton = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n background-color: #1e1f28;\n color: #ffffff;\n width: 240px;\n padding: 16px;\n gap: 4px;\n border-radius: 4px;\n :hover {\n ${AddIcon} {\n color: #ffffff;\n }\n }\n`;\n\nconst AddTaskText = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n margin-left: 0.25rem;\n`;\n\nconst Title = styled.div`\n padding: 0;\n margin: 0;\n max-width: 100%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst createTaskButton = (\n <AddTaskButton>\n <AddIcon>\n <i className=\"bi bi-plus-circle-fill\"></i>\n </AddIcon>\n <AddTaskText>Add new Card</AddTaskText>\n </AddTaskButton>\n);\n\nconst columnDetailsButton = (\n <ColumnDetailsIcon>\n <i className=\"bi bi-pen-fill\"></i>\n </ColumnDetailsIcon>\n);\n\nconst [showAddTaskDialog, setShowAddTaskDialog] = useState(false);\nconst [createStatus, setCreateStatus] = useState([]);\nconst [editTaskId, setEditTaskId] = useState(undefined);\nconst [editedTask, setEditedTask] = useState(undefined);\nconst [editTaskTitle, setEditTaskTitle] = useState('');\nconst [editTaskDescription, setEditTaskDescription] = useState('');\nconst [editTaskTitleMissing, setEditTaskTitleMissing] = useState(false);\nconst [editTaskDescriptionMissing, setEditTaskDescriptionMissing] =\n useState(false);\nconst [editTaskColumn, setEditTaskColumn] = useState('');\nconst [editTaskDialogTaskId, setEditTaskDialogTaskId] = useState(undefined);\nconst [showDeleteTaskDialog, setShowDeleteTaskDialog] = useState(false);\nconst [deleteTaskId, setDeleteTaskId] = useState(undefined);\nconst [showEditTaskDialog, setShowEditTaskDialog] = useState(false);\nconst [editTaskTitleStatus, setEditTaskTitleStatus] = useState([]);\nconst [editTaskDescriptionStatus, setEditTaskDescriptionStatus] = useState([]);\n\nconst addCreateTaskStatus = useCallback((newStatus) => {\n let isNew = true;\n let newCreateTaskStatuses = createStatus.map((status) => {\n if (status.title === newStatus.title) {\n isNew = false;\n return newStatus;\n } else {\n return status;\n }\n });\n if (isNew) {\n newCreateTaskStatuses = [...newCreateTaskStatuses, newStatus];\n }\n setCreateStatus(newCreateTaskStatuses);\n}, []);\n\nconst onChangeShowDeleteTaskDialog = (open, id) => {\n setShowDeleteTaskDialog(open);\n setDeleteTaskId(id);\n};\n\nconst onEditTaskTitle = ({ target }) => {\n setEditTaskTitleMissing(false);\n setEditTaskTitle(target.value);\n};\n\nconst onChangeShowEditTaskDialog = (open, id) => {\n setEditTaskTitleMissing(false);\n setEditTaskDescriptionMissing(false);\n setShowEditTaskDialog(open);\n setEditTaskDialogTaskId(id);\n};\n\nconst onEditTaskDescription = ({ target }) => {\n setEditTaskDescriptionMissing(false);\n setEditTaskDescription(target.value);\n};\n\nconst setEditTask = (task, id, title, description, column) => {\n setEditedTask(task);\n setEditTaskId(id);\n setEditTaskTitle(title);\n setEditTaskDescription(description);\n setEditTaskColumn(column);\n};\n\nconst onChangeDeleteTaskStatus = (status) => {\n setDeleteTaskStatus(status);\n};\n\nconst deleteTask = useCallback(() => {\n const oldStatuses = columns;\n const taskId = deleteTaskId;\n\n const actionStatusPrefix = 'Delete task';\n const newActionStatus = {\n id: actionStatusPrefix + taskId,\n status: `Deleting task: ${taskId}`,\n seen: false,\n };\n try {\n addActionStatus(newActionStatus);\n const newStatuses = columns;\n\n for (let status of newStatuses) {\n if (status.tasks.find((task) => task.id === taskId)) {\n deleteTask = status.tasks.find((task) => task.id === taskId);\n status.tasks = status.tasks.filter((task) => task.id !== taskId);\n }\n }\n\n setColumns(newStatuses);\n\n Near.fakCalimeroCall(contract, 'delete_task_by_id', {\n project_id: projectId,\n task_id: taskId,\n }).then(() => {\n newActionStatus.status = `Deleted task: ${taskId}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n newActionStatus.status = `Error deleting task: ${taskId}`;\n addActionStatus(newActionStatus);\n setColumns(oldStatuses);\n }\n onChangeShowDeleteTaskDialog(false, undefined);\n onChangeShowEditTaskDialog(false, undefined);\n}, []);\n\nconst updateTaskTitle = useCallback(() => {\n if (!editTaskTitle || editTaskTitle.trim() === '') {\n setEditTaskTitleMissing(true);\n return;\n }\n const oldTask = tasks.find((task) => task.id === editTaskId);\n\n if (oldTask.title === editTaskTitle) {\n return;\n }\n\n const newStatus = { id: editTaskId, status: 'Saving...' };\n const newStatuses = [...editTaskTitleStatus, newStatus];\n try {\n setEditTaskTitleStatus(newStatuses);\n\n const editedTask = {\n project_id: projectId,\n id: editTaskId,\n title: editTaskTitle,\n description: editTaskDescription,\n status: editTaskColumn,\n };\n\n let newColumnsArray = [];\n const storageColumns = Storage.privateGet(\n 'tempColumns' + contract + projectId,\n );\n\n if (storageColumns && JSON.parse(storageColumns).length > 0) {\n const columnsArray = JSON.parse(storageColumns);\n\n let filteredColumnsArray = [];\n if (columnsArray.find((task) => task.id === editTaskId)) {\n filteredColumnsArray = columnsArray.filter(\n (task) => task.id !== editTaskId,\n );\n }\n if (columnsArray.find((task) => task.title === oldTask.title)) {\n filteredColumnsArray = columnsArray.filter(\n (task) => task.title !== oldTask.title,\n );\n }\n\n newColumnsArray = filteredColumnsArray;\n newColumnsArray.push(editedTask);\n } else {\n newColumnsArray.push(editedTask);\n }\n\n const jsonStringArray = JSON.stringify(newColumnsArray);\n\n Storage.privateSet('tempColumns' + contract + projectId, jsonStringArray);\n\n handleColumns(projectId);\n\n Near.fakCalimeroCall(contract, 'change_task_name', {\n project_id: projectId,\n task_id: editTaskId,\n new_title: editTaskTitle,\n }).then(() => {\n let foundStatus = newStatuses.find((t) => t.id === editTaskId);\n foundStatus.status = 'Saved';\n const filteredStatuses = newStatuses.filter((t) => t.id !== editTaskId);\n setEditTaskTitleStatus([...filteredStatuses, foundStatus]);\n setTimeout(() => {\n const filteredStatuses = newStatuses.filter((s) => s.id !== editTaskId);\n setEditTaskTitleStatus(filteredStatuses);\n }, 3000);\n });\n } catch (e) {\n let foundStatus = newStatuses.find((t) => t.id === editTaskId);\n foundStatus.status = 'Error saving';\n const filteredStatuses = newStatuses.filter((t) => t.id !== editTaskId);\n setEditTaskTitleStatus([...filteredStatuses, foundStatus]);\n setTimeout(() => {\n const filteredStatuses = newStatuses.filter((s) => s.id !== editTaskId);\n setEditTaskTitleStatus(filteredStatuses);\n }, 3000);\n }\n}, []);\n\nconst updateTaskDescription = useCallback(() => {\n const oldTask = tasks.find((task) => task.id === editTaskId);\n\n if (oldTask.description === editTaskDescription) {\n return;\n }\n\n const newStatus = { id: editTaskId, status: 'Saving...' };\n const newStatuses = [...editTaskDescriptionStatus, newStatus];\n\n try {\n setEditTaskDescriptionStatus(newStatuses);\n\n const editedTask = {\n project_id: projectId,\n id: editTaskId,\n title: editTaskTitle,\n description: editTaskDescription,\n status: editTaskColumn,\n };\n\n let newColumnsArray = [];\n const storageColumns = Storage.privateGet(\n 'tempColumns' + contract + projectId,\n );\n\n if (storageColumns && JSON.parse(storageColumns).length > 0) {\n const columnsArray = JSON.parse(storageColumns);\n\n let filteredColumnsArray = [];\n if (columnsArray.find((task) => task.id === editTaskId)) {\n filteredColumnsArray = columnsArray.filter(\n (task) => task.id !== editTaskId,\n );\n }\n\n if (\n columnsArray.find((task) => task.description === oldTask.description)\n ) {\n filteredColumnsArray = columnsArray.filter(\n (task) => task.description !== oldTask.description,\n );\n }\n\n newColumnsArray = filteredColumnsArray;\n newColumnsArray.push(editedTask);\n } else {\n newColumnsArray.push(editedTask);\n }\n\n const jsonStringArray = JSON.stringify(newColumnsArray);\n\n Storage.privateSet('tempColumns' + contract + projectId, jsonStringArray);\n\n handleColumns(projectId);\n\n Near.fakCalimeroCall(contract, 'edit_task_description', {\n project_id: projectId,\n task_id: editTaskId,\n new_description: editTaskDescription,\n }).then(() => {\n let foundStatus = newStatuses.find((t) => t.id === editTaskId);\n foundStatus.status = 'Saved';\n const filteredStatuses = newStatuses.filter((t) => t.id !== editTaskId);\n setEditTaskDescriptionStatus([...filteredStatuses, foundStatus]);\n setTimeout(() => {\n const filteredStatuses = editTaskDescriptionStatus.filter(\n (s) => s.id !== editTaskId,\n );\n setEditTaskDescriptionStatus(filteredStatuses);\n }, 3000);\n });\n } catch (e) {\n let foundStatus = newStatuses.find((t) => t.id === editTaskId);\n foundStatus.status = 'Error saving';\n const filteredStatuses = newStatuses.filter((t) => t.id !== editTaskId);\n setEditTaskDescriptionStatus([...filteredStatuses, foundStatus]);\n setTimeout(() => {\n const filteredStatuses = editTaskDescriptionStatus.filter(\n (s) => s.id !== editTaskId,\n );\n setEditTaskDescriptionStatus(filteredStatuses);\n }, 3000);\n }\n}, []);\n\nconst cleanTaskStatusById = (id) => {\n const filteredStatuses = createStatus.filter((s) => s.id !== id);\n setCreateStatus(filteredStatuses);\n};\n\nconst cleanEditTaskDescriptionStatus = (id) => {\n const filteredStatuses = editTaskDescriptionStatus.filter((s) => s.id !== id);\n setEditTaskDescriptionStatus(filteredStatuses);\n};\n\nconst cleanCreateTaskStatus = (title) => {\n const filteredStatuses = createStatus.filter((s) => s.title !== title);\n setCreateStatus(filteredStatuses);\n};\n\nreturn (\n <>\n {showDeleteTaskDialog && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ConfirmationPopup`}\n props={{\n onClick: () => deleteTask(),\n onClose: () => onChangeShowDeleteTaskDialog(false, undefined, title),\n functionLoader,\n title: 'Are you sure you want to delete the task?',\n }}\n />\n )}\n {showEditTaskDialog && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.TaskDetailsDialog`}\n props={{\n onChangeShowEditTaskDialog,\n editTaskTitle,\n editTaskDescription,\n onEditTaskTitle,\n onEditTaskDescription,\n updateTaskTitle,\n updateTaskDescription,\n editTaskTitleStatus,\n editTaskId,\n editedTask,\n task,\n editTaskDialogTaskId,\n editTaskDescriptionStatus,\n onChangeShowDeleteTaskDialog,\n editTaskTitleMissing,\n editTaskDescriptionMissing,\n componentOwnerId,\n contract,\n projectId,\n }}\n />\n )}\n <ColumnContainer\n droppable\n onDragOver={(e) => {\n onTaskDragOver();\n }}\n onDrop={() => onTaskDragStop(title)}\n >\n <ColumnLabel>\n <Title>{title}</Title>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.ColumnDetailsPopup`}\n props={{\n componentOwnerId,\n columnIndex,\n columnName: title,\n removeColumn,\n contract,\n projectId,\n addActionStatus,\n containsTasks: tasks.length > 0,\n columnDetailsButton,\n columns,\n setColumns,\n addActionStatus,\n }}\n />\n </ColumnLabel>\n <TasksContainer>\n {tasks.map((task, i) => {\n return (\n <div key={task.id}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.TaskCard`}\n props={{\n task: task,\n status: title,\n index: i,\n onTaskDragStart: onTaskDragStart,\n onTaskDragStop: onTaskDragStop,\n onClick: () => {\n setEditTask(\n task,\n task.id,\n task.title,\n task.description,\n title,\n );\n onChangeShowEditTaskDialog(true, task.id);\n },\n createStatus,\n }}\n />\n </div>\n );\n })}\n </TasksContainer>\n <AddTaskContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.CreateTaskPopup`}\n props={{\n createTaskButton,\n componentOwnerId,\n addCreateTaskStatus,\n cleanCreateTaskStatus,\n contract,\n handleColumns,\n projectId,\n columnTitle: title,\n }}\n />\n </AddTaskContainer>\n </ColumnContainer>\n </>\n);\n" }, "Calimero.Curb.Loader.Loader": { "": "const Loader = styled.div`\n display: inline-block;\n ${({ size }) =>\n size\n ? `width: ${size}px; height: ${size}px;`\n : 'width: 20px; height: 20px;'}\n border: 3px solid rgba(255, 255, 255, 0.3);\n border-radius: 50%;\n ${({ color }) =>\n color ? `border-top-color: ${color};` : 'border-top-color: #fff;'}\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n`;\n\nreturn <Loader size={props.size} color={props.color} />;\n" }, "Calimero.Curb.Navbar.CurbLogo": { "": "const LogoContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n ${({ justify }) => justify && 'justify-content: center;'}\n`;\n\nconst CurbNameContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-size: 20.923px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-right: 0.875rem;\n`;\n\nconst OrganizationNameContainer = () => {\n return <CurbNameContainer>Chat</CurbNameContainer>;\n};\n\nreturn (\n <LogoContainer>\n <svg\n width=\"28\"\n height=\"28\"\n viewBox=\"0 0 28 28\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <rect width=\"28\" height=\"28\" rx=\"6\" fill=\"#5770F2\" />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M24.1568 14C24.1568 8.69358 19.8554 4.39215 14.549 4.39215C9.2426 4.39215 4.94117 8.69358 4.94117 14C4.93884 15.7503 5.41672 17.4677 6.32278 18.9653L5.46576 21.8784C5.41655 22.0457 5.41331 22.2231 5.45639 22.392C5.49946 22.561 5.58725 22.7152 5.71053 22.8385C5.83381 22.9618 5.98802 23.0495 6.15696 23.0926C6.3259 23.1357 6.50333 23.1324 6.67058 23.0832L9.58368 22.2262C11.0811 23.1326 12.7986 23.6105 14.549 23.6078C15.4677 23.6078 16.3564 23.4789 17.1977 23.2381C17.1509 22.8994 17.1267 22.5534 17.1267 22.2018C17.1267 18.2267 20.2197 14.974 24.1303 14.7191C24.1479 14.4817 24.1568 14.2419 24.1568 14ZM10.1672 12.9809C10.4375 12.7107 10.804 12.5588 11.1863 12.5588C11.5685 12.5588 11.9351 12.7107 12.2053 12.9809C12.4756 13.2512 12.6274 13.6178 12.6274 14C12.6274 14.3822 12.4756 14.7488 12.2053 15.0191C11.9351 15.2893 11.5685 15.4412 11.1863 15.4412C10.804 15.4412 10.4375 15.2893 10.1672 15.0191C9.89693 14.7488 9.74509 14.3822 9.74509 14C9.74509 13.6178 9.89693 13.2512 10.1672 12.9809ZM16.8927 12.9809C17.163 12.7107 17.5295 12.5588 17.9118 12.5588C18.294 12.5588 18.6605 12.7107 18.9308 12.9809C19.2011 13.2512 19.3529 13.6178 19.3529 14C19.3529 14.3822 19.2011 14.7488 18.9308 15.0191C18.6605 15.2893 18.294 15.4412 17.9118 15.4412C17.5295 15.4412 17.163 15.2893 16.8927 15.0191C16.6224 14.7488 16.4706 14.3822 16.4706 14C16.4706 13.6178 16.6224 13.2512 16.8927 12.9809Z\"\n fill=\"white\"\n />\n </svg>\n <OrganizationNameContainer />\n </LogoContainer>\n);\n" }, "Calimero.DocsChain.Main": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId;\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\n\nconst accountId = props.accountId ?? context.accountId;\n\nif (!accountId) {\n return (\n <Widget\n src={`calimero.near/widget/Calimero.DocsChain.Popups.TextPopup`}\n config={redirectConfig}\n props={{\n componentOwnerId,\n popupText: 'Please login to NEAR Socials to continue!',\n }}\n />\n );\n}\n\nconst PageContainer = styled.div`\n width: 100%;\n height: 100vh;\n padding-left: 60px;\n padding-right: 60px;\n background-color: #0e0e10;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #5765f2;\n :hover {\n background-color: #717cf0;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nState.init({\n bootstraping: true,\n createPage: { open: false, private: false },\n onOpenCreatePage: (isPrivate) =>\n State.update({ openCreatePage: { open: true, private: isPrivate } }),\n});\n\nconst joinDocs = () => {\n Near.requestCalimeroFak(contract);\n};\n\nconst isMember = () => {\n return Near.calimeroView(contract, 'get_account', {\n account_id: context.accountId,\n });\n};\n\nconst verifyKey = () => {\n Near.hasValidCalimeroFak(contract).then((result) => {\n State.update({ bootstraping: false, loggedIn: result });\n if (result) {\n if (!isMember()) {\n Near.fakCalimeroCall(contract, 'join');\n }\n }\n });\n};\n\nif (state.bootstraping) {\n verifyKey();\n}\n\nreturn (\n <PageContainer>\n <>\n {state.bootstraping ? (\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Popups.TextPopup'}\n config={redirectConfig}\n props={{\n popupText: 'Loading...',\n }}\n />\n ) : (\n <>\n {isMember(context.accountId) ? (\n <>\n <Widget\n src={\n 'calimero.near/widget/Calimero.DocsChain.Navbar.HorizontalNavbar'\n }\n config={redirectConfig}\n />\n <div className=\"d-flex\">\n <Widget\n src={\n 'calimero.near/widget/Calimero.DocsChain.Sidebar.DocsSidebar'\n }\n config={redirectConfig}\n props={{\n onOpenCreatePage: state.onOpenCreatePage,\n createPageOpen: state.createPage.open,\n }}\n />\n </div>\n </>\n ) : (\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Popups.JoinPopup'}\n config={redirectConfig}\n props={{\n join: () => joinDocs(),\n }}\n />\n )}\n </>\n )}\n </>\n </PageContainer>\n);\n" }, "Calimero.DocsChain.MainNavigation": { "": "const currentPill = props.currentNavPill || '';\nconst contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst pills = [\n {\n id: 'articles',\n title: 'Articles',\n widgetName: 'Main',\n },\n {\n id: 'authors',\n title: 'Authors',\n widgetName: 'Authors',\n },\n {\n id: 'create',\n title: 'Create Article',\n widgetName: 'CreateArticle',\n },\n];\n\nreturn (\n <div>\n <ul className=\"nav nav-pills nav-fill mb-4\">\n {pills.map(({ id, title, widgetName }, i) => (\n <li className=\"nav-item\">\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.${widgetName}`,\n )}\n className={`nav-link ${\n id === currentPill ? 'active' : ''\n } text-decoration-none`}\n >\n {title}\n </a>\n </li>\n ))}\n </ul>\n </div>\n);\n" }, "Calimero.TaskChain.SideMenu.BoardOptions.BoardMembers": { "": "const {\n addMemberInputOpen,\n componentOwnerId,\n setSelectedUser,\n setMemberInputOpen,\n selectedUser,\n boardMembers,\n users,\n addMember,\n addMemberStatus,\n onSelectRemovingMember,\n} = props;\n\nconst OptionContainer = styled.div`\n display: flex;\n flex-direction: column;\n width: 100%;\n position: relative;\n`;\n\nconst AddMemberButton = styled.div`\n color: #0e0e10;\n background-color: #d0fc42;\n display: flex;\n flex-direction: row;\n ${({ center }) => center && 'justify-content: center;'}\n border-radius: 4px;\n height: 40px;\n gap: 4px;\n padding-top: 8px;\n padding-bottom: 8px;\n padding-left: 16px;\n padding-right: 16px;\n cursor: pointer;\n :hover {\n opacity: 0.8;\n }\n`;\n\nconst MemberListItem = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n cursor: pointer;\n`;\n\nconst MemberList = styled.div`\n margin-top: 16px;\n margin-bottom: 16px;\n overflow-y: scroll;\n max-height: 24rem;\n scroll-behavior: smooth;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-track {\n background-color: #1d1d21;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst MemberInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst MemberId = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst RemoveMemberButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst AddUserButton = styled.button`\n background-color: #D0FC42;\n :hover {\n back\n }\n padding: 2px 8px 2px 8px;\n width: 100%\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: center;\n`;\n\nreturn (\n <OptionContainer>\n {addMemberInputOpen ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.UserDropdown`}\n props={{\n users,\n componentOwnerId,\n onClick: setSelectedUser,\n onClose: () => {\n setMemberInputOpen(false);\n setSelectedUser(undefined);\n },\n selectedUser,\n addMemberStatus,\n }}\n />\n ) : (\n <AddMemberButton onClick={() => setMemberInputOpen(true)}>\n <i className=\"bi bi-plus-circle-fill\"></i>\n <p>Add new Member</p>\n </AddMemberButton>\n )}\n <MemberList>\n {boardMembers.length &&\n boardMembers.map((member, id) => (\n <MemberListItem key={id}>\n <MemberInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: member.id,\n componentOwnerId,\n }}\n />\n <MemberId>{member.id}</MemberId>\n </MemberInfo>\n <RemoveMemberButton onClick={() => onSelectRemovingMember(member)}>\n <i className=\"bi bi-x-circle\"></i>\n </RemoveMemberButton>\n </MemberListItem>\n ))}\n </MemberList>\n {selectedUser && (\n <AddMemberButton center={true} onClick={() => addMember(selectedUser.id)}>\n Add member\n </AddMemberButton>\n )}\n </OptionContainer>\n);\n" }, "Calimero.DocsChain.Popups.TextPopup": { "": "const PageContainer = styled.div`\n height: 100vh;\n width: 100%;\n background-color: #0e0e10;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n border-radius: 8px;\n width: fit-content;\n`;\n\nconst JoinContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n text: center;\n`;\n\nconst JoinHeader = styled.div`\n display: flex;\n align-items: center;\n flex-direction: column;\n justify-content: center;\n gap: 1.5rem;\n padding-bottom: 1rem;\n padding-top: 1rem;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Button = styled.button`\n background-color: #4e95ff;\n :hover {\n background-color: #5bb0ff;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <PageContainer>\n <PopupContainer>\n <JoinContainer>\n <JoinHeader>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Logo.DocsChainLogo'}\n props={{\n justify: true,\n }}\n />\n <Text>{props.popupText}</Text>\n </JoinHeader>\n </JoinContainer>\n </PopupContainer>\n </PageContainer>\n);\n" }, "Calimero.Curb.AppContainer": { "": "// dependencies\nconst EventEmitter = props.deps.EventEmitter;\n\nconst componentOwnerId = props.componentOwnerId;\nconst contract = props.contract;\nconst encryptionUrl = props.encryptionUrl;\nconst accountId = props.accountId;\nconst enableCommunities = props.enableCommunities;\nconst wsApi = props.wsApi;\nconst handleContractChange = props.handleContractChange;\nconst initialChat = props.initialChat;\nconst updateInitialChat = props.updateInitialChat;\nconst communities = props.communities;\n\n/**\n * Types of chats.\n * @typedef {object} ChatTypes\n * @property {string} CHANNEL - Designates a multi-user chat.\n * @property {string} DIRECT_MESSAGE - Designates a one-to-one chat.\n */\nconst ChatTypes = {\n CHANNEL: 'channel',\n DIRECT_MESSAGE: 'direct_message',\n};\n\n/**\n * `curbApi` is a utility object containing several methods for interacting\n * with a chat API and managing encryption in a chat application.\n *\n * @type {Object}\n * @property {function} createGroup - Sends an API request to create a new chat group.\n * @property {function} inviteUser - Invites a user to a channel.\n * @property {function} getChannels - Retrieves all channels that a user is part of.\n * @property {function} getDMs - Retrieves all direct messages for the user.\n * @property {function} getUnreadMessages - Retrieves all unread messages for the user.\n * @property {function} getChannelMeta - Fetches metadata about a particular channel.\n * @property {function} leaveChannel - Leaves a specified channel.\n * @property {function} createChannel - Creates a new channel.\n * @property {function} toggleReaction - Toggles a reaction to a message.\n * @property {function} fetchMessages - Fetches messages from a specified chat.\n * @property {function} fetchKey - Obtains the encryption key for a particular chat.\n * @property {function} sendMessage - Sends a message to a user or channel.\n *\n * @property {function} createGroup\n * @param {string} name - The name of the new group.\n * @returns {Promise} - A Promise that resolves with the result of the API call.\n *\n * @property {function} inviteUser\n * @param {Object} param\n * @param {string} param.account - The account of the user to invite.\n * @param {string} param.channel - The name of the channel to which the user is being invited.\n * @returns {Promise} - A Promise that resolves with the result of the API call.\n *\n * @property {function} getChannels\n * @returns {Promise<Array>} - A Promise that resolves with an array of channels.\n *\n * @property {function} fetchKey\n * @param {Object} param\n * @param {Object} param.chat - An object containing the chat details.\n * @returns {Promise<string>} - A Promise that resolves with the fetched key.\n *\n * @property {function} sendMessage\n * @param {Object} param\n * @param {string} param.message - The message to be sent.\n * @param {string} [param.img] - The image to be sent (if any).\n * @param {string} [param.toAccount] - The account to which the message is to be sent.\n * @param {string} [param.toChannel] - The channel to which the message is to be sent.\n * @param {string} param.key - The encryption key for the message.\n * @param {string} [param.threadId] - The ID of the thread to which the message belongs.\n * @throws {string} - Throws an error message if the required parameters are not valid.\n * @returns {Promise} - A Promise that resolves with the result of the API call.\n */\nconst curbApi = useMemo(() => {\n // Send transactions in order and wait for the previous transaction to be confirmed before sending the next one\n const meroshipSend = (method, params, callback, reschedule) => {\n transactionQueue.push({ method, params, callback, reschedule });\n processQueue();\n };\n\n let transactionQueue = [];\n let isProcessing = false;\n\n const processQueue = () => {\n if (transactionQueue.length === 0 || isProcessing) {\n return;\n }\n\n isProcessing = true;\n const { method, params, callback, reschedule } = transactionQueue.shift();\n\n Calimero.fakSignTx(contract, method, params).then(([_, signedTx]) => {\n wsApi.methods.submitTx(\n signedTx,\n (err, result) => {\n if (err) {\n console.log(\n 'Error: Calimero.Curb.AppContainer.meroshipSend error',\n contract,\n method,\n params,\n err,\n );\n // add retry logic\n callback?.(err);\n } else {\n console.log(\n 'Calimero.Curb.AppContainer.meroshipSend result',\n result,\n );\n callback?.(null, result);\n }\n isProcessing = false;\n processQueue();\n },\n reschedule,\n );\n });\n };\n\n const getStorageKey = (chatType, id) => `${contract}:${id}:${chatType}`;\n const fetchKey = ({ chat }) => {\n return new Promise((resolve, reject) => {\n if (!chat || !chat.type) {\n return reject(\n 'Error: Invalid chat object, you need to provide a valid chat object to fetch the key',\n );\n }\n const storageKey = getStorageKey(\n chat.type,\n chat.type === ChatTypes.CHANNEL ? chat.name : chat.id,\n );\n const storedEncriptionKey = Storage.privateGet(storageKey);\n if (storedEncriptionKey) {\n return resolve(storedEncriptionKey);\n }\n const nonce = toHexString(Crypto.randomBytes(16));\n Calimero.sign(contract, Buffer.from(accountId + '|' + nonce))\n .then((signature) => {\n const keyBody = {\n from: accountId,\n contract,\n };\n if (chat.type === ChatTypes.DIRECT_MESSAGE) {\n keyBody.dms = [chat.id];\n } else {\n keyBody.channels = [{ name: chat.name }];\n }\n keyBody.nonce = nonce;\n keyBody.signature = toHexString(signature.signature);\n\n return asyncFetch(encryptionUrl, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(keyBody),\n }).catch((e) => {\n return Promise.reject(e);\n });\n })\n .then((keyData) => {\n for (const [id, key] of Object.entries(keyData.body.dms)) {\n const storageKey = getStorageKey(ChatTypes.DIRECT_MESSAGE, id);\n Storage.privateSet(storageKey, key);\n }\n for (const [id, key] of Object.entries(keyData.body.channels)) {\n const storageKey = getStorageKey(ChatTypes.CHANNEL, id);\n Storage.privateSet(storageKey, key);\n }\n resolve(Storage.privateGet(storageKey));\n })\n .catch((e) => {\n console.log('Error: Calimero.Curb.AppContainer.fetchKey error', e);\n reject(e);\n });\n });\n };\n\n function extractAndAddMentions(inputText) {\n const regexPattern =\n /(@everyone)|(@here)|(@[a-zA-Z0-9_.-]+(?<![-.0-9])(?:\\.testnet|\\.near))/g;\n const uniqueMentions = new Set();\n let match;\n while ((match = regexPattern.exec(inputText)) !== null) {\n uniqueMentions.add(match[0].slice(1, match[0].length));\n }\n return Array.from(uniqueMentions);\n }\n\n const sendMessage = (\n { message, images, chat, files, threadId },\n callback,\n ) => {\n if (!message && images?.length === 0 && files?.length === 0) {\n // Nothing to send\n return;\n }\n if (!chat) {\n throw 'You need to provide a chat object to send the message';\n }\n return fetchKey({ chat }).then((key) => {\n const params = {};\n const encrypted = encrypt(message, images ?? [], files ?? [], key);\n const mentionsArray = extractAndAddMentions(message);\n if (chat.type === ChatTypes.DIRECT_MESSAGE) {\n params.account = chat.id;\n } else {\n params.group = { name: chat.name };\n }\n if (mentionsArray.length) {\n params.mentions = mentionsArray;\n }\n if (encrypted.images.length > 0) {\n params.images = encrypted.images;\n } else if (encrypted.files.length > 0) {\n params.files = encrypted.files;\n }\n\n params.message = encrypted.text;\n params.nonce = encrypted.nonce;\n params.timestamp = Date.now();\n params.parent_message = threadId ?? undefined;\n try {\n meroshipSend('send_message', params, callback, (retry) =>\n chat.type === ChatTypes.CHANNEL\n ? curbApi.once(\n 'subscribed',\n (name) => name === chat.name && retry(),\n )\n : retry(),\n );\n } catch (e) {\n return Promise.reject(e);\n }\n });\n };\n const parseHexString = (hexString) => {\n const result = [];\n while (hexString.length >= 2) {\n result.push(parseInt(hexString.substring(0, 2), 16));\n hexString = hexString.substring(2, hexString.length);\n }\n return result;\n };\n\n const deleteMessage = ({ message, chat, callback }) => {\n const params = {\n message_id: message.id,\n };\n if (chat.type === ChatTypes.DIRECT_MESSAGE) {\n params.account = chat.id;\n } else {\n params.group = { name: chat.name };\n }\n if (message.parent_message) {\n params.parent_message = message.parent_message;\n }\n try {\n meroshipSend('delete_message', params, callback, (retry) =>\n chat.type === ChatTypes.CHANNEL\n ? curbApi.once('subscribed', (name) => name === chat.name && retry())\n : retry(),\n );\n } catch (e) {\n return Promise.reject(e);\n }\n };\n\n const toHexString = (byteArray) => {\n let result = '';\n for (let byte of byteArray) {\n result += ('0' + (byte & 0xff).toString(16)).slice(-2);\n }\n return result;\n };\n\n const encryptWithCipher = (data, key, nonce) => {\n const cipher = Crypto.createCipheriv(\n 'aes-256-cbc',\n parseHexString(key),\n nonce,\n );\n let encryptedData = cipher.update(data, 'utf8', 'base64');\n encryptedData += cipher.final('base64');\n return encryptedData;\n };\n\n const encrypt = (text, images, files, key, prevNonce) => {\n let nonce;\n if (prevNonce) {\n nonce = [];\n let hexString = prevNonce;\n while (hexString.length >= 2) {\n nonce.push(parseInt(hexString.substring(0, 2), 16));\n hexString = hexString.substring(2, hexString.length);\n }\n } else {\n nonce = Crypto.randomBytes(16);\n }\n console.log('prev', text, key, nonce, encrypted);\n let encrypted = encryptWithCipher(text, key, nonce);\n console.log('encrypt', text, key, nonce, encrypted);\n let encryptedImages = [];\n let encryptedFiles = [];\n if (images.length > 0) {\n encryptedImages = images.map((img) => ({\n name: encryptWithCipher('image.png', key, nonce),\n ipfs_cid: encryptWithCipher(img.ipfs_cid, key, nonce),\n }));\n } else if (files.length > 0) {\n // TODO: handle multiple files and images\n encryptedFiles = files.map((file) => ({\n name: encryptWithCipher(file.name, key, nonce),\n ipfs_cid: encryptWithCipher(file.ipfs_cid, key, nonce),\n }));\n }\n return {\n text: encrypted,\n images: encryptedImages,\n files: encryptedFiles,\n nonce: toHexString(nonce),\n };\n };\n\n function decryptMessages(messages, key) {\n if (!key || key.length === 0) {\n return [];\n }\n const decryptedMessages = messages.map((msg) => {\n return {\n ...msg,\n text: decrypt(msg.text, key, msg.nonce),\n files: msg.files?.length\n ? [\n {\n name: decrypt(msg.files[0].name, key, msg.nonce),\n ipfs_cid: decrypt(msg.files[0].ipfs_cid, key, msg.nonce),\n },\n ]\n : [],\n images: msg.images?.length\n ? [\n {\n name: decrypt(msg.images[0].name, key, msg.nonce),\n ipfs_cid: decrypt(msg.images[0].ipfs_cid, key, msg.nonce),\n },\n ]\n : [],\n reactions: msg.reactions ?? [],\n };\n });\n return decryptedMessages;\n }\n function decrypt(text, key, nonce) {\n if (!key) {\n return null;\n }\n const byteNonce = [];\n let hexString = nonce;\n while (hexString.length >= 2) {\n byteNonce.push(parseInt(hexString.substring(0, 2), 16));\n hexString = hexString.substring(2, hexString.length);\n }\n\n try {\n const decipher = Crypto.createDecipheriv(\n 'aes-256-cbc',\n parseHexString(key),\n byteNonce,\n );\n let decrypted = decipher.update(text, 'base64', 'utf8');\n decrypted += decipher.final('utf8');\n return decrypted;\n } catch (e) {\n console.log('Calimero.decrypt error: ', e.toString());\n return null;\n }\n }\n\n let appEvents = new EventEmitter();\n\n return {\n utils: { toHexString, parseHexString },\n decryptMessages,\n fetchKey,\n on: (event, listener) => {\n appEvents.on(event, listener);\n return curbApi;\n },\n once: (event, listener) => {\n appEvents.once(event, listener);\n return curbApi;\n },\n off: (event, listener) => {\n appEvents.off(event, listener);\n return curbApi;\n },\n prependListener: (event, listener) => {\n appEvents.prependListener(event, listener);\n return curbApi;\n },\n prependOnceListener: (event, listener) => {\n appEvents.prependOnceListener(event, listener);\n return curbApi;\n },\n emit: (event, data) => {\n appEvents.emit(event, data);\n return curbApi;\n },\n createGroup: (name, isPrivate, isReadOnly) =>\n Near.fakCalimeroCall(contract, 'create_group', {\n group: { name },\n channel_type: isPrivate ? 'Private' : 'Public',\n read_only: isReadOnly,\n creator: accountId,\n }),\n inviteUser: ({ account, channel }) =>\n Near.fakCalimeroCall(contract, 'group_invite', {\n group: { name: channel },\n account,\n }),\n getChannels: () =>\n Near.asyncCalimeroView(contract, 'get_groups', {\n account: accountId,\n }).then((groups) =>\n groups.map((group) => ({ ...group, type: ChatTypes.CHANNEL })),\n ),\n getFilteredMembers: (namePrefix, channelName) =>\n Near.asyncCalimeroView(contract, 'get_members', {\n name_prefix: namePrefix,\n group: { name: channelName },\n exclude: true,\n }).then((members) =>\n members.map((member) => ({\n ...member,\n type: ChatTypes.DIRECT_MESSAGE,\n })),\n ),\n getDMs: () =>\n Near.asyncCalimeroView(contract, 'get_direct_messages', {\n account: accountId,\n }),\n fetchAccounts: ({ groupName, prefix, limit, exclude }) =>\n Near.asyncCalimeroView(contract, 'get_members', {\n group: groupName ? { groupName } : undefined,\n name_prefix: prefix,\n limit,\n exclude,\n }),\n getAccountsStatus: (accounts) =>\n new Promise((resolve, reject) => {\n wsApi.methods.getAccountsStatus(accounts, (err, result) => {\n if (err) {\n reject(err);\n return;\n }\n resolve(result);\n });\n }),\n getAppName: () =>\n Near.asyncCalimeroView(contract, 'public_info').then(({ name }) => name),\n getChannelMembers: (channelName) =>\n Near.asyncCalimeroView(contract, 'get_members', {\n group: { name: channelName },\n }),\n getUnreadMessages: () =>\n Near.asyncCalimeroView(contract, 'unread_messages', {\n account: accountId,\n }),\n getChannelMeta: (channelName) =>\n Near.asyncCalimeroView(contract, 'channel_info', {\n group: { name: channelName },\n }),\n promoteModerator: (accountId, channelName, isAdd) =>\n Near.fakCalimeroCall(contract, 'set_group_moderator', {\n group: { name: channelName },\n moderator: accountId,\n is_add: isAdd,\n }),\n removeUserFromChannel: (accountId, channelName) =>\n Near.fakCalimeroCall(contract, 'leave_group', {\n group: { name: channelName },\n account: accountId,\n }),\n leaveChannel: (channelName) =>\n Near.fakCalimeroCall(contract, 'leave_group', {\n group: { name: channelName },\n account: accountId,\n }),\n createChannel: (channelName) =>\n Near.fakCalimeroCall(contract, 'create_group', {\n group: { channelName },\n }),\n toggleReaction: ({ messageId, reaction }, callback) => {\n meroshipSend(\n 'toggle_reaction',\n {\n message_id: messageId,\n reaction,\n },\n callback,\n );\n },\n fetchMessages: ({ chat, beforeId, afterId, limit, parentMessageId }) =>\n new Promise((resolve, reject) => {\n if (!chat || !chat.type) {\n reject(`Error: Invalid chat object ${JSON.stringify(chat)}`);\n return;\n }\n let args = { limit: 1000000 };\n if (chat.type === ChatTypes.CHANNEL && chat.name) {\n args.group = { name: chat.name };\n } else if (chat.type === ChatTypes.DIRECT_MESSAGE && chat.id) {\n args.accounts = [accountId, chat.id];\n } else {\n reject('Error: Invalid chat object');\n return;\n }\n if (beforeId) {\n args.before_id = beforeId;\n } else if (afterId) {\n args.after_id = afterId;\n }\n if (limit) {\n args.limit = limit;\n }\n if (parentMessageId) {\n args.parent_message = parentMessageId;\n }\n Promise.all([\n fetchKey({ chat }),\n Near.asyncCalimeroView(contract, 'get_messages', args),\n ])\n .then(([key, messagesResponse]) => {\n const { messages, totalCount, startPosition } = messagesResponse;\n const result = {\n messages: decryptMessages(messages, key),\n totalCount,\n hasOlder: startPosition !== 0,\n };\n resolve(result);\n })\n .catch((e) => {\n console.log('Error: Calimero.Curb.Chat.fetchMessages error', e);\n reject(e);\n });\n }),\n sendMessage,\n deleteMessage,\n editMessage: ({ message, chat }, callback) => {\n if (!chat || !chat.type) {\n return;\n }\n return fetchKey({ chat }).then((key) => {\n const params = {};\n console.log('editMessage', message, key, message.nonce);\n const encrypted = encrypt(\n message.text,\n message.images,\n message.files,\n key,\n message.nonce,\n );\n if (chat.type === ChatTypes.DIRECT_MESSAGE) {\n params.account = chat.id;\n } else {\n params.group = { name: chat.name };\n }\n params.new_message = encrypted.text;\n params.message_id = message.id;\n params.images = [];\n params.files = [];\n if (encrypted.images && encrypted.images.length > 0) {\n params.images = encrypted.images;\n } else if (encrypted.files && encrypted.files.length > 0) {\n params.files = encrypted.files;\n }\n try {\n meroshipSend('edit_message', params, callback, (retry) =>\n chat.type === ChatTypes.CHANNEL\n ? curbApi.once(\n 'subscribed',\n (name) => name === chat.name && retry(),\n )\n : retry(),\n );\n } catch (e) {\n return Promise.reject(e);\n }\n });\n },\n readMessage: ({ chat, messageId }, callback) => {\n if (!chat || !chat.type) {\n return;\n }\n const readMessageParams =\n chat.type === ChatTypes.CHANNEL\n ? {\n group: { name: chat.name },\n }\n : {\n account: chat.id,\n };\n meroshipSend(\n 'read_message',\n {\n message_id: messageId,\n ...readMessageParams,\n },\n callback,\n );\n },\n getCommunities: (contracts) => {\n return Promise.all(\n contracts.map((contract) =>\n Near.asyncCalimeroView(contract, 'public_info').then(\n (publicInfo) => ({\n ...publicInfo,\n assets: JSON.parse(publicInfo.assets),\n contract,\n }),\n ),\n ),\n );\n },\n };\n}, [contract, accountId]);\n\n/**\n * Initial version of the chat object, currently only supports channels and p2p DMs.\n * @typedef {object} Chat\n * @property {string} type - Can be 'channel' or 'direct_message'.\n * @property {string} [name] - Name of the channel.\n * @property {string} [account] - Account for direct message.\n */\nconst [activeChat, setActiveChat] = useState({\n type: ChatTypes.CHANNEL,\n name: 'general',\n readOnly: false,\n});\n\nuseEffect(() => {\n if (initialChat) {\n setActiveChat(initialChat);\n updateInitialChat(initialChat);\n }\n}, [initialChat]);\n\nconst [isSidebarOpen, setIsSidebarOpen] = useState(false);\n\nconst ContentDivContainer = styled.div`\n width: 100%;\n @media (min-width: 1025px) {\n height: calc(100vh - 169px);\n display: flex;\n }\n`;\n\nconst Wrapper = styled.div`\n @media (min-width: 1025px) {\n flex: 1;\n }\n`;\nconst updateSelectedActiveChat = useCallback((selectedChat) => {\n setActiveChat(selectedChat);\n setIsSidebarOpen(false);\n updateInitialChat(selectedChat);\n}, []);\n\nconst [isWsConnectionActive, setIsWsConnectionActive] = useState(false);\n\nuseEffect(() => {\n let connected = () => setIsWsConnectionActive(true);\n let disconnected = () => setIsWsConnectionActive(false);\n\n wsApi.on('connected', connected).on('disconnected', disconnected).connect();\n\n return () =>\n wsApi\n .off('connected', connected)\n .off('disconnected', disconnected)\n .disconnect();\n}, []);\n\nreturn (\n <>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.NavbarContainer`}\n props={{\n componentOwnerId,\n activeChat,\n isSidebarOpen,\n setIsSidebarOpen,\n curbApi,\n enableCommunities,\n }}\n />\n <ContentDivContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ChannelsContainer`}\n props={{\n componentOwnerId,\n curbApi,\n onChatSelected: updateSelectedActiveChat,\n activeChat,\n isSidebarOpen,\n enableCommunities,\n isWsConnectionActive,\n wsApi,\n handleContractChange,\n communities,\n selectedCommunity: contract,\n }}\n />\n {!isSidebarOpen && (\n <Wrapper>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.ChatContainer`}\n props={{\n componentOwnerId,\n contract,\n curbApi,\n activeChat,\n accountId,\n wsApi,\n isWsConnectionActive,\n }}\n />\n </Wrapper>\n )}\n </ContentDivContainer>\n </>\n);\n" }, "Calimero.Curb.ChannelNavbarContainer": { "": "const activeChat = props.activeChat;\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\n\nconst generator = () => curbApi.getChannelMembers(activeChat.name);\nconst cachedMembers = useCache(generator, 'channel_members', {\n subscribe: true,\n});\n\nconst [members, setMembers] = useState([]);\n\nuseEffect(() => {\n if (cachedMembers) {\n setMembers(cachedMembers);\n }\n}, [cachedMembers]);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.CurbNavbar`}\n props={{\n ...props,\n channelUserList: members,\n }}\n />\n);\n" }, "Calimero.DocsChain.ArticleFooter": { "": "const { author, timeCreated, lastEditor, timeLastEdit, version } = props;\nconst getDate = (timestamp) => {\n const date = new Date(Number(timestamp) / 1e6);\n return date.toDateString();\n};\n\nreturn (\n <div className=\"alert alert-secondary bg-white\">\n <div>\n Created by{' '}\n <a\n href={`https://near.social/#/mob.near/widget/ProfilePage?accountId=${author}`}\n target=\"_blank\"\n style={{ textDecoration: 'underline' }}\n rel=\"noreferrer\"\n >\n {author}\n </a>\n <br />\n {timeCreated && (\n <>\n Posted on {getDate(timeCreated)}\n <br />\n </>\n )}\n <br />\n Last edit by{' '}\n <a\n href={`https://near.social/#/mob.near/widget/ProfilePage?accountId=${lastEditor}`}\n style={{ textDecoration: 'underline' }}\n >\n {lastEditor}\n </a>\n <br />\n Edited on {getDate(timeLastEdit)}\n <br />\n <br />\n Edit versions: {version}\n </div>\n </div>\n);\n" }, "Calimero.DocsChain.MarkdownEditorIframe": { "": "const initialText = props.initialText ?? '# Hello World\\n\\n';\n\nconst code = `\n<script src=\"https://unpkg.com/react@18/umd/react.development.js\" crossorigin></script>\n<script src=\"https://unpkg.com/react-dom@18/umd/react-dom.development.js\" crossorigin></script>\n<script src=\"https://unpkg.com/react-markdown-editor-lite@1.3.4/lib/index.js\" crossorigin></script>\n<link rel=\"stylesheet\" href=\"https://unpkg.com/react-markdown-editor-lite@1.3.4/lib/index.css\" />\n\n\n<div id=\"react-root\"></div>\n\n<script>\nfunction TestReact(props) {\n const [value, setValue] = React.useState(props.initialText || \"\");\n return React.createElement(ReactMarkdownEditorLite.default, {\n value,\n view: { menu: true, md: true, html: false },\n canView: { menu: true, md: false, html: false, fullScreen: false, hideMenu: true },\n onChange: ({ text }) => {\n setValue(text);\n window.top.postMessage(text, \"*\");\n },\n renderHTML: () => {},\n className: \"full\",\n }); \n}\n\nconst domContainer = document.querySelector('#react-root');\nconst root = ReactDOM.createRoot(domContainer);\n\nwindow.addEventListener(\"message\", (event) => {\n root.render(React.createElement(TestReact, {\n initialText: event.data,\n }));\n});\n\n</script>\n`;\n\nreturn (\n <iframe\n className=\"w-100 h-100\"\n srcDoc={code}\n message={initialText}\n onMessage={props.onChange}\n />\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.Comments": { "": "const { componentOwnerId, contract, projectId, taskId } = props;\n\nconst Comments = styled.div`\n width: 100%;\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: 84px;\n margin-top: 1rem;\n`;\n\nconst CommentsContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 10px;\n width: 520px;\n`;\n\nconst OldComments = styled.div`\n max-height: 300px;\n overflow-y: scroll;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 104px;\n height: 40px;\n padding: 8px 60px 8px 16px;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n border: none;\n`;\n\nconst [comments, setComments] = useState(props.comments);\n\nconst addComment = useCallback(\n (comment) => {\n const newComment = {\n id: comments.length + 1,\n member_id: context.accountId,\n message: comment,\n date: new Date(),\n };\n\n setComments(comments.concat(newComment));\n\n return Near.fakCalimeroCall(contract, 'comment_on_task', {\n project_id: projectId,\n task_id: taskId,\n message: comment,\n });\n },\n [comments, contract, projectId, taskId],\n);\n\nconst getComments = useCallback(\n (taskId) => {\n try {\n Near.fakCalimeroCall(contract, 'get_task', {\n project_id: projectId,\n task_id: taskId,\n }).then((task) => {\n setComments(task.comments);\n });\n } catch (e) {\n console.log('getComments', e);\n }\n },\n [setComments],\n);\n\nuseEffect(() => {\n if (taskId) {\n getComments(taskId);\n }\n}, [taskId]);\n\nreturn (\n <Comments>\n <Label>Comments</Label>\n <CommentsContainer>\n {comments && (\n <OldComments>\n {comments.map((comment, id) => (\n <Widget\n key={comment.id + id}\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.Comment`}\n props={{\n comment,\n componentOwnerId,\n }}\n />\n ))}\n </OldComments>\n )}\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.CommentInput`}\n props={{\n componentOwnerId,\n contract,\n projectId,\n taskId,\n addComment,\n }}\n />\n </CommentsContainer>\n </Comments>\n);\n" }, "Calimero.Curb.Communities.CommunityDropdown": { "": "const {\n componentOwnerId,\n communities,\n handleContractChange,\n selectedCommunity,\n} = props;\n\nconst SelectTrigger = styled('Select.Trigger')`\n display: flex;\n align-items: center;\n width: 293px;\n height: 63px;\n padding: 16px;\n border-radius: 4px;\n gap: 16px;\n background-color: #1e1f28;\n font-family: Helvetica Neue;\n font-weight: 400;\n border: none;\n :focus {\n outline-width: 0px;\n }\n :hover {\n background-color: #2e2e2e;\n }\n ${({ isOpen }) =>\n isOpen ? 'border-radius: 4px 4px 0px 0px;' : 'border-radius: 4px;'}\n @media (max-width: 1024px) {\n width: 350px;\n }\n`;\n\nconst SelectContent = styled('Select.Content')`\n display: flex;\n flex-direction: column;\n background-color: #1e1f28;\n gap: 16px;\n z-index: 20;\n border-radius: 0px 0px 4px 4px;\n cursor: pointer;\n width: 293px;\n @media (max-width: 1024px) {\n width: 350px;\n }\n`;\n\nconst SelectItem = styled('Select.Item')`\n display: flex;\n align-items: center;\n width: 293px;\n height: 63px;\n padding: 16px;\n border-radius: 4px;\n gap: 16px;\n background-color: #1e1f28;\n font-family: Helvetica Neue;\n font-weight: 400;\n border: none;\n :focus {\n outline-width: 0px;\n }\n :hover {\n background-color: #2e2e2e;\n }\n @media (max-width: 1024px) {\n width: 350px;\n }\n`;\n\nconst SelectIcon = styled('Select.Icon')`\n color: #fff;\n transition: transform 3s ease-in-out;\n transform: rotate(${({ isOpen }) => (isOpen ? '180deg' : '0deg')});\n`;\n\nconst CommunityInfo = styled.div`\n display: flex;\n column-gap: 8px;\n width: 220px;\n @media (max-width: 1024px) {\n width: 270px;\n }\n`;\n\nconst CommunityDetails = styled.div`\n display: flex;\n flex-direction: column;\n`;\n\nconst CommunityName = styled.p`\n padding: 0;\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n color: #fff;\n`;\n\nconst CommunityMembersCount = styled.p`\n padding: 0;\n margin: 0;\n font-size: 14px;\n line-height: 17px;\n letter-spacing: 0em;\n text-align: left;\n color: #777583;\n`;\n\nconst Text = styled.p`\n padding: 0;\n margin: 0;\n`;\n\nconst CommunityLogoContainer = styled.div`\n position: relative;\n width: 43px;\n height: 43px;\n`;\n\nconst NotificationsStatusCricle = styled.div`\n position: absolute;\n bottom: 1px;\n right: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background-color: #ff3a3a;\n`;\n\nconst CommunityLogo = styled.div`\n width: 42px;\n height: 42px;\n`;\n\nconst [community, setCommunity] = useState(\n communities.find((x) => x.contract === selectedCommunity),\n);\nconst [isOpen, setIsOpen] = useState(false);\n\nconst toggleOpen = () => {\n setIsOpen((prevIsOpen) => !prevIsOpen);\n};\n\nconst handleCommunityChange = (community) => {\n handleContractChange(community.contract);\n setCommunity(community);\n};\n\nreturn (\n <Select.Root\n value={community}\n onValueChange={handleCommunityChange}\n open={isOpen}\n onOpenChange={toggleOpen}\n >\n <SelectTrigger isOpen={isOpen}>\n <Select.Value value={community}>\n <CommunityInfo>\n {community.assets?.logo && (\n <CommunityLogoContainer>\n <CommunityLogo>\n <img\n src={`https://ipfs.near.social/ipfs/${community.assets.logo}`}\n alt=\"uploaded\"\n />\n </CommunityLogo>\n {community.notifications?.length > 0 && (\n <NotificationsStatusCricle />\n )}\n </CommunityLogoContainer>\n )}\n <CommunityDetails>\n <CommunityName>{community.name}</CommunityName>\n <CommunityMembersCount>{`${community.members} members`}</CommunityMembersCount>\n </CommunityDetails>\n </CommunityInfo>\n </Select.Value>\n <SelectIcon isOpen={isOpen}>\n <i className=\"bi bi-chevron-down\"></i>\n </SelectIcon>\n </SelectTrigger>\n <SelectContent position=\"popper\">\n <Select.Viewport>\n <Select.Group>\n {communities?.map((community, id) => (\n <SelectItem id={id} key={id} value={community} ref=\"forwardedRef\">\n <CommunityInfo>\n {community.assets?.logo && (\n <CommunityLogoContainer>\n <CommunityLogo>\n <img\n src={`https://ipfs.near.social/ipfs/${community.assets.logo}`}\n alt=\"uploaded\"\n />\n </CommunityLogo>\n {community.notifications?.length > 0 && (\n <NotificationsStatusCricle />\n )}\n </CommunityLogoContainer>\n )}\n <CommunityDetails>\n <CommunityName>{community.name}</CommunityName>\n <CommunityMembersCount>{`${community.members} members`}</CommunityMembersCount>\n </CommunityDetails>\n </CommunityInfo>\n </SelectItem>\n ))}\n </Select.Group>\n </Select.Viewport>\n </SelectContent>\n </Select.Root>\n);\n" }, "Calimero.Curb.Navbar.CurbNavbar": { "": "const appName = props.appName;\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\nconst mobileSideMenuOpen = props.mobileSideMenuOpen;\nconst channelSelected = props.channelSelected;\nconst channelUserList = props.channelUserList;\nconst activeChat = props.activeChat;\nconst isSidebarOpen = props.isSidebarOpen;\nconst setIsSidebarOpen = props.setIsSidebarOpen;\nconst enableCommunities = props.enableCommunities;\n\nconst NavigationBar = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-left: 1rem;\n padding-right: 1rem;\n border-bottom: 1px solid #282933;\n @media (max-width: 1024px) {\n ${({ isSidebarOpen }) =>\n isSidebarOpen ? 'display: none;' : 'display: flex;'}\n position: fixed;\n top: 64px;\n left: 0;\n right: 0;\n z-index: 1000;\n }\n`;\n\nconst CurbNameContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-size: 20.923px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-right: 0.875rem;\n`;\nconst VerticalSeparatorFull = styled.div`\n width: 1px;\n height: 80px;\n ${({ enableCommunities }) => enableCommunities && 'margin-left: 5.5rem;'}\n background-color: #282933;\n @media (max-width: 1024px) {\n display: none;\n }\n}\n`;\n\nconst OrgNameContainer = styled.div`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%;\n padding-left: 1rem;\n padding-right: 6rem;\n padding-top: 1rem;\n padding-bottom: 1rem;\n display: flex;\n justify-content: center;\n align-items: center;\n border-left: 1px solid #282933;\n @media (max-width: 1024px) {\n display: none;\n }\n}\n`;\n\nconst IconPlus = styled.div`\n :hover {\n border: solid 1px #fff;\n }\n cursor: pointer;\n width: 24px;\n height: 24px;\n background-color: #5765f2;\n color: #fff;\n margin-left: 0.5rem;\n display: flex;\n justify-content: center;\n align-items: center;\n border-radius: 50%;\n`;\n\nconst ItemsContainer = styled.div`\n display: flex;\n ${({ align }) => align && 'align-items: center;'}\n`;\n\nconst AddUserButton = () => {\n return (\n <IconPlus>\n <i className=\"bi bi-plus-lg\" />\n </IconPlus>\n );\n};\n\nconst LogoContainer = styled.div`\n display: flex;\n @media (max-width: 1024px) {\n display: none;\n }\n }\n column-gap: 0.5rem;\n align-items: center;\n ${({ justify }) => justify && 'justify-content: center;'}\n`;\n\nconst BackIcon = styled.svg`\n fill: #5765f2;\n display: none;\n @media (max-width: 1024px) {\n display: flex;\n }\n maring-right: 14px;\n`;\n\nconst CurbLogo = () => {\n return (\n <LogoContainer>\n <svg\n width=\"28\"\n height=\"28\"\n viewBox=\"0 0 28 28\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <rect width=\"28\" height=\"28\" rx=\"6\" fill=\"#5770F2\" />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M24.1568 14C24.1568 8.69358 19.8554 4.39215 14.549 4.39215C9.2426 4.39215 4.94117 8.69358 4.94117 14C4.93884 15.7503 5.41672 17.4677 6.32278 18.9653L5.46576 21.8784C5.41655 22.0457 5.41331 22.2231 5.45639 22.392C5.49946 22.561 5.58725 22.7152 5.71053 22.8385C5.83381 22.9618 5.98802 23.0495 6.15696 23.0926C6.3259 23.1357 6.50333 23.1324 6.67058 23.0832L9.58368 22.2262C11.0811 23.1326 12.7986 23.6105 14.549 23.6078C15.4677 23.6078 16.3564 23.4789 17.1977 23.2381C17.1509 22.8994 17.1267 22.5534 17.1267 22.2018C17.1267 18.2267 20.2197 14.974 24.1303 14.7191C24.1479 14.4817 24.1568 14.2419 24.1568 14ZM10.1672 12.9809C10.4375 12.7107 10.804 12.5588 11.1863 12.5588C11.5685 12.5588 11.9351 12.7107 12.2053 12.9809C12.4756 13.2512 12.6274 13.6178 12.6274 14C12.6274 14.3822 12.4756 14.7488 12.2053 15.0191C11.9351 15.2893 11.5685 15.4412 11.1863 15.4412C10.804 15.4412 10.4375 15.2893 10.1672 15.0191C9.89693 14.7488 9.74509 14.3822 9.74509 14C9.74509 13.6178 9.89693 13.2512 10.1672 12.9809ZM16.8927 12.9809C17.163 12.7107 17.5295 12.5588 17.9118 12.5588C18.294 12.5588 18.6605 12.7107 18.9308 12.9809C19.2011 13.2512 19.3529 13.6178 19.3529 14C19.3529 14.3822 19.2011 14.7488 18.9308 15.0191C18.6605 15.2893 18.294 15.4412 17.9118 15.4412C17.5295 15.4412 17.163 15.2893 16.8927 15.0191C16.6224 14.7488 16.4706 14.3822 16.4706 14C16.4706 13.6178 16.6224 13.2512 16.8927 12.9809Z\"\n fill=\"white\"\n />\n </svg>\n <CurbNameContainer>Chat</CurbNameContainer>\n </LogoContainer>\n );\n};\n\nconst AddUserDialog = ({ curbApi, channelName, componentOwnerId }) => {\n const addUser = useCallback(\n (account) => curbApi.inviteUser({ account, channel: channelName }),\n [curbApi, channelName],\n [channelName],\n );\n\n const isValidAccountName = useCallback((value) => {\n const regex = /^[a-z\\d]+[-_]*[a-z\\d]+[-_]*[a-z\\d]+\\.(near|testnet)$/;\n if (!regex.test(value)) {\n return false;\n }\n return true;\n }, []);\n\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.InputPopup`}\n props={{\n componentOwnerId,\n title: `Invite user to #${channelName}`,\n placeholder: 'ex: username.near',\n buttonText: 'Invite',\n functionLoader: addUser,\n colors: {\n base: '#5765f2',\n hover: '#717cf0',\n disabled: '#3B487A',\n },\n toggle: <AddUserButton />,\n validator: isValidAccountName,\n autocomplete: true,\n members: channelUserList,\n }}\n />\n );\n};\n\nconst IconWrapper = styled.div`\n padding: 8px;\n display: flex;\n justify-content: center;\n align-items: center;\n margin-right: 8px;\n`;\n\nconst BackIconContainer = ({ onClick }) => {\n return (\n <IconWrapper onClick={onClick}>\n <BackIcon\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n fill=\"currentColor\"\n className=\"bi bi-chevron-left\"\n viewBox=\"0 0 16 16\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z\"\n />\n </BackIcon>\n </IconWrapper>\n );\n};\n\nreturn (\n <NavigationBar isSidebarOpen={isSidebarOpen}>\n <ItemsContainer align={true}>\n {enableCommunities ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.NearSvgLogo`}\n />\n ) : (\n <>\n <CurbLogo />\n <OrgNameContainer>{appName}</OrgNameContainer>\n </>\n )}\n <VerticalSeparatorFull enableCommunities={enableCommunities} />\n {\n <BackIconContainer\n onClick={() => {\n setIsSidebarOpen(!isSidebarOpen);\n }}\n />\n }\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.DetailsDropdown`}\n props={{\n activeChat,\n componentOwnerId,\n curbApi,\n }}\n />\n </ItemsContainer>\n {channelUserList.length > 0 && (\n <ItemsContainer align={false}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Popups.ChannelDetailsPopup`}\n props={{\n toggle: (\n <div>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.UsersButtonGroup`}\n props={{\n componentOwnerId,\n channelUserList,\n aboutSelected: false,\n }}\n />\n </div>\n ),\n chat: activeChat,\n curbApi,\n componentOwnerId,\n }}\n />\n {/* <AddUserDialog\n curbApi={curbApi}\n channelName={channelSelected}\n componentOwnerId={componentOwnerId}\n /> */}\n </ItemsContainer>\n )}\n </NavigationBar>\n);\n" }, "Calimero.DocsChain.Common.Button": { "": "const Button = styled.div`\n color: #ffffff;\n\n padding: 8px;\n gap: 4px;\n ${({ color }) =>\n color ? `background-color: ${color};` : 'background-color: #1e1f28;'}\n :hover {\n ${({ hoverColor }) =>\n hoverColor\n ? `background-color: ${hoverColor};`\n : 'background-color: #25252A;'}\n }\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: normal;\n line-height: 150%;\n border-radius: 4px;\n ${({ width }) => (width ? `width: ${width};` : 'width: fit-content;')}\n cursor: pointer;\n justify-content: center;\n align-items: center;\n display: flex;\n font-size: 0.875rem;\n line-height: 1.25rem;\n`;\n\nconst Icon = styled.i`\n color: #777583;\n width: 16px;\n height: 16px;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nreturn (\n <Button\n onClick={props.onClick}\n color={props.color}\n hoverColor={props.hoverColor}\n width={props.width}\n >\n {props.icon && <Icon className={props.icon}></Icon>}\n {props.text}\n </Button>\n);\n" }, "Calimero.Common.TimeFormatting": { "": "const formatTimeAgo = (seconds) => {\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const weeks = Math.floor(days / 7);\n const months = Math.floor(weeks / 4);\n\n if (months > 0) {\n return `${months} month${months > 1 ? 's' : ''} ago`;\n } else if (weeks > 0) {\n return `${weeks} week${weeks > 1 ? 's' : ''} ago`;\n } else if (days > 0) {\n return `${days} day${days > 1 ? 's' : ''} ago`;\n } else if (hours > 0) {\n return `${hours} hour${hours > 1 ? 's' : ''} ago`;\n } else if (minutes > 0) {\n return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;\n } else {\n return `just now`;\n }\n};\n\nreturn formatTimeAgo(props.time);\n" }, "WebSocketDemo": { "": "const WS_ADDRESS = 'ws://localhost:8080';\nconst MAX_RETRIES = 5;\nconst RECONNECT_DELAY = 1000;\nconst PONG_WAIT_TIME = 6000;\nconst MAX_DELAY = 30000;\n\nconst userId = context.accountId;\n\nconst ConnectionStatus = {\n CONNECTED: 'Connected',\n RECONNECTING: 'Reconnecting...',\n DISCONNECTED: 'Disconnected',\n};\n\nconst WebSocketState = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n};\n\nconst [messages, setMessages] = useState([]);\nconst [inputValue, setInputValue] = useState('');\nconst [connectionStatus, setConnectionStatus] = useState(\n ConnectionStatus.DISCONNECTED,\n);\nconst [connectedUsers, setConnectedUsers] = useState([]);\nconst [selectedUser, setSelectedUser] = useState(null);\n\nconst socketRef = useRef(null);\nconst retryCountRef = useRef(0);\nconst shouldReconnectRef = useRef(true);\nconst pingIntervalRef = useRef(null);\n\nconst setupWebSocket = () => {\n const ws = new WebSocket(WS_ADDRESS);\n\n ws.onopen = () => {\n console.log('Connected to the WebSocket');\n setConnectionStatus(ConnectionStatus.CONNECTED);\n retryCountRef.current = 0;\n\n if (pingIntervalRef.current) {\n clearInterval(pingIntervalRef.current);\n }\n\n pingIntervalRef.current = setInterval(() => {\n if (ws.readyState === WebSocketState.OPEN) {\n ws.send('ping');\n }\n }, PONG_WAIT_TIME);\n };\n\n ws.onmessage = (event) => {\n console.log('Received message:', event.data);\n\n if (event.data === 'pong') {\n console.log('Received pong');\n setConnectionStatus(ConnectionStatus.CONNECTED);\n return;\n }\n\n const parsedEvent = JSON.parse(event.data);\n if (parsedEvent.type === 'usersList') {\n setConnectedUsers(parsedEvent.users);\n return;\n } else if (parsedEvent.type === 'message') {\n setMessages((prev) => [\n ...prev,\n `Received ${parsedEvent.content} from ${parsedEvent.from}`,\n ]);\n return;\n }\n };\n\n ws.onclose = () => {\n setConnectionStatus(\n ConnectionStatus.RECONNECTING +\n '...' +\n retryCountRef.current +\n 'attempts',\n );\n if (shouldReconnectRef.current && retryCountRef.current < MAX_RETRIES) {\n setTimeout(\n setupWebSocket,\n Math.min(\n RECONNECT_DELAY * Math.pow(2, retryCountRef.current),\n MAX_DELAY,\n ),\n );\n retryCountRef.current++;\n } else {\n setConnectionStatus(ConnectionStatus.DISCONNECTED);\n }\n };\n\n ws.onerror = () => {\n setConnectionStatus(ConnectionStatus.DISCONNECTED);\n };\n\n socketRef.current = ws;\n};\nuseEffect(() => {\n setupWebSocket();\n\n return () => {\n shouldReconnectRef.current = false;\n clearInterval(pingIntervalRef.current);\n if (socketRef.current) {\n socketRef.current.close();\n }\n };\n}, []);\n\nconst handleRegister = () => {\n if (\n socketRef.current &&\n socketRef.current.readyState === WebSocketState.OPEN\n ) {\n socketRef.current.send(JSON.stringify({ register: true, userId }));\n }\n};\n\nconst handleInputChange = (event) => {\n setInputValue(event.target.value);\n};\n\nconst handleSendMessage = () => {\n if (socketRef.current && inputValue !== '' && selectedUser) {\n const messageData = {\n content: inputValue,\n to: selectedUser,\n from: userId,\n };\n socketRef.current.send(JSON.stringify(messageData));\n setInputValue('');\n }\n};\n\nconst handleManualReconnect = () => {\n if (\n !socketRef.current ||\n socketRef.current.readyState === WebSocketState.CLOSED\n ) {\n retryCountRef.current = 0;\n setupWebSocket();\n }\n};\n\nreturn (\n <>\n <div className=\"App\">\n <h2>WebSocket Test Client</h2>\n <h3>Status: {connectionStatus}</h3>\n <button onClick={handleRegister}>Register</button>\n <div>\n <input\n value={inputValue}\n onChange={handleInputChange}\n placeholder=\"Type a message...\"\n />\n <button onClick={handleSendMessage}>Send</button>\n <button onClick={handleManualReconnect}>Manual Reconnect</button>\n </div>\n <h4>Connected Users:</h4>\n {connectedUsers.length === 0 && <p>No connected users</p>}\n {connectedUsers.length > 0 &&\n connectedUsers.map((user, index) => (\n <div style={{ display: 'flex', flexDirection: 'row' }}>\n <input\n style={{ width: '24px', height: '24px', marginRight: '10px' }}\n type=\"radio\"\n name=\"connectedUsers\"\n value={user}\n checked={selectedUser === user}\n onChange={() => setSelectedUser(user)}\n />\n {user}\n </div>\n ))}\n <h4>Messages:</h4>\n <ul>\n {messages.map((message, index) => (\n <li key={index}>{message}</li>\n ))}\n </ul>\n </div>\n </>\n);\n" }, "Calimero.TaskChain.Selector": { "": "const selectorOptions = props.selectorOptions;\nconst selected = props.selected;\nconst color = props.color ?? '#D0FC42';\nconst onClick = props.onClick;\n\nconst SelectorContainer = styled.div`\n display: flex;\n flex-direction: row;\n background-color: transparent;\n gap: 16px;\n`;\n\nconst OptionButton = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 8px;\n ${({ selected }) => (selected ? `color: ${color};` : 'color: #fff;')}\n :hover {\n color: ${color};\n }\n cursor: pointer;\n`;\n\nconst P = styled.p`\n margin: 0;\n`;\n\nreturn (\n <SelectorContainer>\n {selectorOptions.map((option) => (\n <OptionButton\n onClick={() => onClick(option.id)}\n selected={selected === option.id}\n >\n <i className={option.icon}></i>\n <P>{option.title}</P>\n </OptionButton>\n ))}\n </SelectorContainer>\n);\n" }, "Calimero.Curb.Communities.CommunityList": { "": "const communities = props.communities;\nconst filteredCommunities = props.filteredCommunities;\nconst isSearched = props.isSearched;\nconst componentOwnerId = props.componentOwnerId;\n\nconst Container = styled.div`\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: 16px;\n`;\n\nconst CommunitiesContainer = styled.div`\n width: 100%;\n display: grid;\n grid-template-columns: auto auto auto auto auto;\n gap: 16px;\n ${({ isSearched }) => isSearched && 'padding: 0px 24px 0px 24px;'}\n`;\n\nconst SearchBox = styled.input`\n width: 347px;\n padding: 6px 12px 6px 12px;\n border-radius: 4px 0px 0px 4px;\n background-color: #37373f;\n color: #6c757d;\n border: none;\n :focus {\n border-color: #d0fc42;\n border-style: solid;\n border-width: 0.1px;\n outline-width: 0px;\n }\n`;\n\nconst SearchButton = styled.button`\n display: flex;\n column-gap: 8px;\n justify-content: center;\n align-items: center;\n background-color: #d0fc42;\n color: #212529;\n width: 99px;\n padding: 6px 12px 6px 12px;\n border-radius: 0px 4px 4px 0px;\n border: none;\n :hover {\n background-color: #bbe33b;\n }\n`;\n\nconst Title = styled.h4`\n color: #fff;\n text-align: left;\n`;\n\nreturn (\n <Container>\n {!isSearched && <Title>Community list</Title>}\n <CommunitiesContainer isSearched={isSearched}>\n {isSearched\n ? filteredCommunities.map((community) => (\n <Widget\n key={community.id}\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.CommunityCard`}\n props={{\n community,\n }}\n />\n ))\n : communities.map((community) => (\n <Widget\n key={community.id}\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.CommunityCard`}\n props={{\n community,\n }}\n />\n ))}\n </CommunitiesContainer>\n </Container>\n);\n" }, "Calimero.Curb.Chat.MessageFileField": { "": "const file = props.file;\nconst resetFile = props.resetFile;\n\nconst FileContainer = styled.div`\n min-width: 197px;\n max-width: 197px;\n position: relative;\n width: fit-content;\n background-color: #1D1D21;\n display: flex;\n gap: 4px;,\n border-radius: 2px;\n padding: 4px;\n cursor: poiner;\n margin-left: 4px;\n margin-bottom: 4px;\n`;\n\nconst IconContainer = styled.div`\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: #4E95FF;\n border-radius: 2px;\n width: 2rem;\n height: 2rem;\n padding: 0.5rem:\n`;\n\nconst FileInfo = styled.div`\n display: flex;\n flex-direction: column;\n`;\n\nconst FileTitle = styled.div`\n color: #fff;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 130px;\n -webkit-font-smoothing: antialiased applied;\n`;\n\nconst FileExtension = styled.div`\n font-size: 12px;\n font-style: normal;\n font-weight: 400;\n line-height: 100%;\n color: #5c5c71;\n`;\n\nconst FileIcon = ({ className }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill=\"white\"\n className={className ?? 'bi bi-file-earmark-fill'}\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M4 0h5.293A1 1 0 0 1 10 .293L13.707 4a1 1 0 0 1 .293.707V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm5.5 1.5v2a1 1 0 0 0 1 1h2l-3-3z\" />\n </svg>\n);\n\nconst RemoveButton = styled.div`\n position: relative;\n top: -4px;\n right: -4px;\n z-index: 20;\n cursor: pointer;\n`;\n\nconst ResetFileIcon = ({ resetFile }) => {\n return (\n <RemoveButton onClick={resetFile}>\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g clipPath=\"url(#clip0_955_44892)\">\n <path\n d=\"M12 6C12 7.5913 11.3679 9.11742 10.2426 10.2426C9.11742 11.3679 7.5913 12 6 12C4.4087 12 2.88258 11.3679 1.75736 10.2426C0.632141 9.11742 0 7.5913 0 6C0 4.4087 0.632141 2.88258 1.75736 1.75736C2.88258 0.632141 4.4087 0 6 0C7.5913 0 9.11742 0.632141 10.2426 1.75736C11.3679 2.88258 12 4.4087 12 6ZM4.0155 3.4845C3.94509 3.41408 3.84958 3.37453 3.75 3.37453C3.65042 3.37453 3.55491 3.41408 3.4845 3.4845C3.41408 3.55491 3.37453 3.65042 3.37453 3.75C3.37453 3.84958 3.41408 3.94509 3.4845 4.0155L5.46975 6L3.4845 7.9845C3.44963 8.01937 3.42198 8.06076 3.40311 8.10631C3.38424 8.15187 3.37453 8.20069 3.37453 8.25C3.37453 8.29931 3.38424 8.34813 3.40311 8.39369C3.42198 8.43924 3.44963 8.48063 3.4845 8.5155C3.55491 8.58591 3.65042 8.62547 3.75 8.62547C3.79931 8.62547 3.84813 8.61576 3.89369 8.59689C3.93924 8.57802 3.98063 8.55037 4.0155 8.5155L6 6.53025L7.9845 8.5155C8.01937 8.55037 8.06076 8.57802 8.10631 8.59689C8.15187 8.61576 8.20069 8.62547 8.25 8.62547C8.29931 8.62547 8.34813 8.61576 8.39369 8.59689C8.43924 8.57802 8.48063 8.55037 8.5155 8.5155C8.55037 8.48063 8.57802 8.43924 8.59689 8.39369C8.61576 8.34813 8.62547 8.29931 8.62547 8.25C8.62547 8.20069 8.61576 8.15187 8.59689 8.10631C8.57802 8.06076 8.55037 8.01937 8.5155 7.9845L6.53025 6L8.5155 4.0155C8.55037 3.98063 8.57802 3.93924 8.59689 3.89369C8.61576 3.84813 8.62547 3.79931 8.62547 3.75C8.62547 3.70069 8.61576 3.65187 8.59689 3.60631C8.57802 3.56076 8.55037 3.51937 8.5155 3.4845C8.48063 3.44963 8.43924 3.42198 8.39369 3.40311C8.34813 3.38424 8.29931 3.37453 8.25 3.37453C8.20069 3.37453 8.15187 3.38424 8.10631 3.40311C8.06076 3.42198 8.01937 3.44963 7.9845 3.4845L6 5.46975L4.0155 3.4845Z\"\n fill=\"white\"\n />\n </g>\n <defs>\n <clipPath id=\"clip0_955_44892\">\n <rect width=\"12\" height=\"12\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n </RemoveButton>\n );\n};\n\nif (!file) return null;\nconst fileName = file?.name;\nreturn (\n <FileContainer>\n <IconContainer>\n <FileIcon className=\"bi bi-file-earmark-fill\" />\n </IconContainer>\n {fileName && (\n <FileInfo>\n <FileTitle>{fileName}</FileTitle>\n <FileExtension>\n {fileName.split('.').pop()?.toUpperCase()}\n </FileExtension>\n </FileInfo>\n )}\n <ResetFileIcon resetFile={resetFile} />\n </FileContainer>\n);\n" }, "Calimero.DocsChain.Navbar.HorizontalNavbar": { "": "const NavigationBar = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-left: 1rem;\n padding-right: 1rem;\n border-bottom: 1px solid #282933;\n`;\n\nconst VerticalSeparatorFull = styled.div`\n width: 1px;\n height: 80px;\n background-color: #282933;\n`;\n\nconst ItemsContainer = styled.div`\n display: flex;\n ${({ align }) => align && 'align-items: center;'}\n`;\n\nreturn (\n <NavigationBar>\n <ItemsContainer align={true}>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Logo.DocsChainLogo'}\n props={{\n justify: true,\n }}\n />\n <VerticalSeparatorFull />\n </ItemsContainer>\n </NavigationBar>\n);\n" }, "Calimero.TaskChain.ActionStatus.Status": { "": "const { actionStatus, setActionStatusNotVisible } = props;\n\nconst ToastRoot = styled('Toast.Root')`\n display: flex;\n flex-direction: row;\n justify-content: end;\n align-items: center;\n color: #fff;\n background-color: #1e1f28;\n border-radius: 0.5rem;\n padding: 1rem;\n`;\n\nconst Text = styled.p`\n padding: 0;\n margin: 0;\n`;\n\nconst [open, setOpen] = useState(true);\n\nconst onOpenChange = () => {\n setActionStatusNotVisible(actionStatus);\n setOpen(false);\n};\n\nreturn (\n <Toast.Provider swipeDirection=\"right\" duration={3000}>\n <ToastRoot open={open} onOpenChange={onOpenChange}>\n <Toast.Description asChild>\n <Text>{actionStatus.status}</Text>\n </Toast.Description>\n </ToastRoot>\n <Toast.Viewport />\n </Toast.Provider>\n);\n" }, "Calimero.Curb.EmojiSelector.EmojiSelectorPopup": { "": "const OnEmojiSelected = props.OnEmojiSelected;\nconst componentOwnerId = props.componentOwnerId;\nconst onClose = props.onClose;\n\nconst EmojiPopupContainer = styled.div`\n position: fixed;\n left: 0px;\n right: 0px;\n bottom: 0px;\n top: 0px;\n width: 100%;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n z-index: 100;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n z-index: 40;\n position: absolute;\n top: -30px;\n left: 140px;\n cursor: pointer;\n`;\n\nconst CloseButtonContainer = styled.div`\n position: relative;\n`;\n\nreturn (\n <EmojiPopupContainer>\n <CloseButtonContainer>\n <CloseButton onClick={onClose}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n </CloseButtonContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.EmojiSelector.EmojiSelector`}\n props={{\n OnEmojiSelected: (emoji) => OnEmojiSelected(emoji),\n }}\n key={'emojis-popup-component'}\n />\n </EmojiPopupContainer>\n);\n" }, "Calimero.Curb.Logger": { "": "const render = props.render;\n\nfunction Logger(scope, args, debug) {\n scope = typeof scope === 'string' ? { name: scope, stack: [] } : scope;\n let tag = `[${scope.name}]${\n scope.stack.length ? ` {${scope.stack.join(' > ')}}` : ''\n }`;\n let log = (str, args) => debug && console.log(tag, str, args);\n let error = (str, args) => console.log(tag, str, args);\n let span = (name, args) =>\n Logger({ name: scope.name, stack: [...scope.stack, name] }, args, debug);\n if (args && debug) console.log(tag + ' args', args);\n let self = { log, error, span };\n self.fallback = () => (logger) =>\n logger && typeof logger.span === 'function' ? logger : self;\n return self;\n}\n\nreturn render({ Logger });\n" }, "Calimero.TaskChain.Loader.Loader": { "": "const Loader = styled.div`\n display: inline-block;\n ${({ size }) =>\n size\n ? `width: ${size}px; height: ${size}px;`\n : 'width: 20px; height: 20px;'}\n border: 3px solid rgba(255, 255, 255, 0.3);\n border-radius: 50%;\n ${({ color }) =>\n color ? `border-top-color: ${color};` : 'border-top-color: #fff;'}\n animation: spin 1s ease-in-out infinite;\n -webkit-animation: spin 1s ease-in-out infinite;\n`;\n\nreturn <Loader size={props.size} color={props.color} />;\n" }, "Calimero.TaskChain.CreateBoardDialog": { "": "const {\n open,\n componentOwnerId,\n addCreateBoardStatus,\n accountId,\n handleBoards,\n contract,\n createBoardStatus,\n createBoardButton,\n addBoard,\n} = props;\n\nconst Overlay = styled.div`\n background-color: var(--blackA9);\n position: fixed;\n z-index: 1000;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);\n`;\n\nconst ModalContent = styled.div`\n position: absolute;\n width: 500px;\n padding: 30px;\n border-radius: 20px;\n color: white;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n font-style: normal;\n`;\n\nconst Title = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 30%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n border: none;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingText = styled.div`\n position: absolute;\n right: 36%;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 14px;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DialogTitle = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst [boardName, setBoardName] = useState('');\nconst [addBoardNameMissing, setAddBoardNameMissing] = useState(null);\nconst [isCreateBoardDialogVisible, setIsCreateBoardDialogVisible] =\n useState(false);\n\nconst onChangeBoardName = ({ target }) => {\n setAddBoardNameMissing(null);\n setBoardName(target.value);\n};\n\nconst handleOnClose = useCallback(() => {\n setIsCreateBoardDialogVisible(false);\n setBoardName(null);\n}, []);\n\nconst createBoard = useCallback(async () => {\n let newStatus = { name: boardName, status: 'Saving' };\n try {\n addCreateBoardStatus(newStatus);\n\n const newBoard = [null, boardName];\n\n addBoard(newBoard);\n\n Near.fakCalimeroCall(contract, 'create_new_project', {\n name: boardName,\n owner: accountId,\n members: [accountId],\n statuses: ['TO DO', 'IN PROGRESS', 'DONE'],\n }).then(() => {\n newStatus.status = 'Saved';\n addCreateBoardStatus(newStatus);\n });\n } catch {\n newStatus.status = 'Error';\n addCreateBoardStatus(newStatus);\n }\n setIsCreateBoardDialogVisible(false);\n setBoardName('');\n}, [boardName, addCreateBoardStatus, accountId, contract]);\n\nconst content = (\n <Overlay>\n <ModalContent>\n <PopupContainer>\n <Header>\n <DialogTitle>Create new Board</DialogTitle>\n <CloseButton onClick={handleOnClose}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n </Header>\n <FieldContainer>\n <Title\n onChange={onChangeBoardName}\n value={boardName}\n placeholder=\"Add Name\"\n />\n {addBoardNameMissing && (\n <MissingTitle>{addBoardNameMissing}</MissingTitle>\n )}\n </FieldContainer>\n <FunctionButton\n onClick={() => {\n if (boardName) {\n createBoard();\n } else {\n setAddBoardNameMissing('Missing name');\n }\n }}\n >\n Create\n </FunctionButton>\n </PopupContainer>\n </ModalContent>\n </Overlay>\n);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.BaseModal`}\n props={{\n toggle: createBoardButton,\n content,\n open: isCreateBoardDialogVisible,\n onOpenChange: (open) => setIsCreateBoardDialogVisible(open),\n }}\n />\n);\n" }, "Calimero.TaskChain.SideMenu.EditBoardDialog": { "": "const onClose = props.onClose;\nconst functionLoader = props.functionLoader;\nconst onEditBoardName = props.onEditBoardName;\nconst componentOwnerId = props.componentOwnerId;\nconst selectedProjectId = props.selectedProjectId;\nconst users = props.users;\nconst selectedUser = props.selectedUser;\nconst setSelectedUser = props.setSelectedUser;\nconst addActionStatus = props.addActionStatus;\nconst contract = props.contract;\nconst editedBoard = props.editedBoard;\nconst updateBoardName = props.updateBoardName;\nconst deleteBoard = props.deleteBoard;\nconst onChangeShowEditBoardDialog = props.onChangeShowEditBoardDialog;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n`;\n\nconst ActionsContainer = styled.div`\n display: flex;\n flex-direction: row;\n gap: 2px;\n align-items: center;\n items-center: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Name = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 70%;\n height: 40px;\n padding: 8px 60px 8px 0px;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst CloseButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst P = styled.p`\n display: inline-block;\n width: 100px;\n margin: 0;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 22px;\n margin-bottom: 16px;\n`;\n\nconst SuccessIcon = styled.div`\n color: #00ff66;\n`;\n\nconst ErrorIcon = styled.div`\n color: #dc3545;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst StatusIcon = styled.div`\n padding-right: 60px;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 16px;\n top: 40px;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst EditBoardNameButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n display: flex;\n flex-direction: row;\n cursor: pointer;\n justify-content: center;\n align-items: center;\n gap: 5px;\n`;\n\nconst CloseAddMeberButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n position: absolute;\n right: 10px;\n top: 10px;\n`;\n\nconst FunctionButton = styled.button`\n background-color: red;\n :hover {\n opacity: 0.8;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DeleteColumnContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst CloseDeleteColumnButton = styled.div`\n background-color: transparent;\n display: flex;\n justify-content: center;\n color: #6b7280;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst TextContainer = styled.div`\n display: flex;\n justify-content: center;\n flex-direction: column;\n width: 100%;\n text-align: center;\n margin-bottom: 1rem;\n margin-top: 1rem;\n`;\n\nconst [selectedOption, setSelectedOption] = useState(1);\nconst [\n isRemoveMemberConfirmationVisible,\n setIsRemoveMemberConfirmationVisible,\n] = useState(false);\nconst [removingMember, setRemovingMember] = useState(undefined);\nconst [boardMembers, setBoardMembers] = useState([]);\nconst [addMemberStatus, setAddMemberStatus] = useState(undefined);\nconst [addMemberInputOpen, setMemberInputOpen] = useState(false);\nconst [editedBoardName, setEditedBoardName] = useState(editedBoard[1]);\nconst [editBoardNameStatus, setEditBoardNameStatus] = useState(null);\nconst [editBoardNameMissing, setEditBoardNameMissing] = useState(false);\n\nconst [showDeleteBoardDialog, setShowDeleteBoardDialog] = useState(false);\n\nconst onChangeEditBoardName = ({ target }) => {\n setEditedBoardName(target.value);\n};\n\nconst onChangeShowDeleteBoardDialog = (open) => {\n setShowDeleteBoardDialog(open);\n};\n\nconst editBoardName = useCallback(async () => {\n if (!editedBoardName || editedBoardName.trim() === '') {\n setEditBoardNameMissing(true);\n setTimeout(() => setEditedBoardName(editedBoard[1]), 3000);\n setTimeout(() => setEditBoardNameMissing(false), 3000);\n return;\n }\n if (editedBoardName === editedBoard[1]) {\n return;\n }\n\n try {\n setEditBoardNameStatus('Saving...');\n\n const newBoard = [editedBoard[0], editedBoardName];\n\n updateBoardName(newBoard);\n\n Near.fakCalimeroCall(contract, 'edit_name', {\n project_id: editedBoard[0],\n new_name: editedBoardName,\n }).then(() => {\n setEditBoardNameStatus('Saved');\n setTimeout(() => setEditBoardNameStatus(null), 3000);\n });\n } catch (e) {\n setEditBoardNameStatus('Error saving');\n }\n}, [editedBoardName, editedBoard]);\n\nconst handleDeleteBoard = useCallback(() => {\n const actionStatusPrefix = 'Delete board';\n let newActionStatus = {\n id: actionStatusPrefix + editedBoard[0],\n status: `Deleting board: ${editedBoard[1]}`,\n seen: false,\n };\n try {\n addActionStatus(newActionStatus);\n\n deleteBoard(editedBoard[0]);\n\n Near.fakCalimeroCall(contract, 'delete_project', {\n project_id: editedBoard[0],\n }).then(() => {\n newActionStatus.status = `Deleted board: ${editedBoard[1]}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n newActionStatus.status = `Error deleting board: ${editedBoard[1]}`;\n addActionStatus(newActionStatus);\n }\n onChangeShowEditBoardDialog(false, undefined);\n onChangeShowDeleteBoardDialog(false);\n}, [editedBoard]);\n\nconst removeBoardMember = useCallback(\n (member) => {\n setBoardMembers(boardMembers.filter((m) => m.id !== member.id));\n },\n [boardMembers, setBoardMembers],\n);\n\nconst onSelectRemovingMember = (member) => {\n setIsRemoveMemberConfirmationVisible(true);\n setRemovingMember(member);\n};\n\nconst removeMember = useCallback(() => {\n const actionStatusPrefix = 'Remove member';\n let newActionStatus = {\n id: actionStatusPrefix + editedBoard[0] + removingMember.id,\n status: `Removing member ${removingMember.id} from board ${editedBoardName}`,\n seen: false,\n };\n try {\n addActionStatus(newActionStatus);\n removeBoardMember(removingMember);\n\n Near.fakCalimeroCall(contract, 'remove_member', {\n project_id: selectedProjectId,\n member: removingMember.id,\n }).then(() => {\n newActionStatus.status = `Removed member ${removingMember.id} from board ${boardName}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n console.log('removeMember', e);\n newActionStatus.status = `Error removing member ${removingMember.id} from board ${boardName}`;\n addActionStatus(newActionStatus);\n }\n setIsRemoveMemberConfirmationVisible(false);\n}, [removingMember]);\n\nconst addMember = useCallback(\n async (accountId) => {\n setMemberInputOpen(false);\n const oldMembers = JSON.parse(JSON.stringify(boardMembers));\n try {\n setAddMemberStatus('Saving...');\n Near.fakCalimeroCall(contract, 'add_member', {\n project_id: selectedProjectId,\n new_member: accountId,\n }).then(() => {\n setAddMemberStatus('Saved');\n setTimeout(() => setAddMemberStatus(undefined), 3000);\n });\n setSelectedUser(undefined);\n setBoardMembers([...oldMembers, { id: accountId }]);\n } catch (e) {\n setBoardMembers(oldMembers);\n setAddMemberStatus('Error');\n }\n },\n [boardMembers, selectedProjectId, contract],\n);\n\nconst getMembers = useCallback(\n async (projectId) => {\n try {\n Near.asyncCalimeroView(contract, 'get_members', {\n project_id: projectId,\n }).then((members) => {\n setBoardMembers(members.map((member) => ({ id: member })));\n });\n } catch (e) {\n console.log('getMembers', e);\n }\n },\n [contract],\n);\n\nuseEffect(() => {\n if (selectedProjectId) {\n getMembers(selectedProjectId);\n }\n}, [selectedProjectId]);\n\nconst BoardDetailsContent = () => (\n <OverlayContainer>\n <PopupContainer>\n <Header>\n <FieldContainer>\n <Name\n onChange={onChangeEditBoardName}\n value={editedBoardName}\n onBlur={editBoardName}\n placeholder=\"Add Title\"\n />\n {editBoardNameMissing && <MissingTitle>Missing name</MissingTitle>}\n </FieldContainer>\n <ActionsContainer>\n {editBoardNameStatus && (\n <StatusIcon>\n {editBoardNameStatus === 'Saving...' && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Loader.Loader`}\n props={{ size: 16 }}\n />\n )}\n {editBoardNameStatus === 'Saved' && (\n <SuccessIcon>\n <i className=\"bi bi-check\"></i>\n </SuccessIcon>\n )}\n {editBoardNameStatus === 'Error' && (\n <ErrorIcon>\n <i className=\"bi bi-x-circle\"></i>\n </ErrorIcon>\n )}\n </StatusIcon>\n )}\n <CloseButton onClick={onClose}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </ActionsContainer>\n </Header>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Selector`}\n props={{\n selectorOptions: [\n { id: 1, title: 'About', icon: 'bi bi-info-circle-fill' },\n {\n id: 2,\n title: `Members ${boardMembers.length}`,\n icon: 'bi bi-people-fill',\n },\n ],\n onClick: setSelectedOption,\n selected: selectedOption,\n }}\n />\n <Divider />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.EditBoardOptions`}\n props={{\n onChangeShowDeleteBoardDialog,\n editedBoardId: editedBoard[0],\n addMemberInputOpen,\n componentOwnerId,\n setSelectedUser,\n setMemberInputOpen,\n selectedUser,\n boardMembers,\n selectedOption,\n users: users.filter(\n (user) => !boardMembers.some((member) => member.id === user.id),\n ),\n addMember,\n addMemberStatus,\n onSelectRemovingMember,\n }}\n />\n </PopupContainer>\n </OverlayContainer>\n);\n\nconst ConfirmationDialog = ({\n message,\n onConfirm,\n confirmLabel,\n onClose,\n closeLabel,\n}) => (\n <OverlayContainer>\n <PopupContainer>\n <DeleteColumnContainer>\n <TextContainer>\n <Text>{message}</Text>\n </TextContainer>\n <FunctionButton onClick={onConfirm}>{confirmLabel}</FunctionButton>\n <CloseDeleteColumnButton onClick={onClose}>\n {closeLabel}\n </CloseDeleteColumnButton>\n </DeleteColumnContainer>\n </PopupContainer>\n </OverlayContainer>\n);\n\nconst RemoveMemberContent = () => (\n <ConfirmationDialog\n message={`Are you sure you want to remove member ${removingMember.id}?`}\n onConfirm={removeMember}\n confirmLabel=\"Remove\"\n onClose={() => setIsRemoveMemberConfirmationVisible(false)}\n closeLabel=\"Close\"\n />\n);\n\nconst DeleteBoardContent = () => (\n <ConfirmationDialog\n message=\"Are you sure you want to delete this board?\"\n onConfirm={handleDeleteBoard}\n confirmLabel=\"Delete\"\n onClose={() => setShowDeleteBoardDialog(false)}\n closeLabel=\"Close\"\n />\n);\n\nif (isRemoveMemberConfirmationVisible) {\n return <RemoveMemberContent />;\n} else if (showDeleteBoardDialog) {\n return <DeleteBoardContent />;\n} else {\n return <BoardDetailsContent />;\n}\n" }, "Calimero.Curb.Settings.MemberDetails": { "": "const componentOwnerId = props.componentOwnerId;\nconst userList = props.userList;\nconst addMember = props.addMember;\nconst channelName = props.channelName;\nconst chatUsers = props.chatUsers;\nconst setOptionsOpen = props.setOptionsOpen;\nconst optionsOpen = props.optionsOpen;\nconst promoteModerator = props.promoteModerator;\nconst removeUserFromChannel = props.removeUserFromChannel;\nconst channelOwner = props.channelOwner;\nconst getNonInvitedUsers = props.getNonInvitedUsers;\nconst selectedUser = props.selectedUser;\nconst setSelectedUser = props.setSelectedUser;\n\nconst groupMembers = userList.map((user) => user.id);\n\nconst AddMemberButton = styled.div`\n display: flex;\n column-gap: 0.5rem;\n padding-left: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n color: #fff;\n :hover {\n background-color: #5765f2;\n }\n border-radius: 4px;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n cursor: pointer;\n`;\n\nconst UserListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n :hover {\n background-color: #25252a;\n }\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n cursor: pointer;\n`;\n\nconst UserList = styled.div`\n overflow-y: scroll;\n max-height: 24rem;\n @media (max-width: 1024px) {\n max-height: 12rem;\n }\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst UserInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst Text = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n color: ${({ isSelected }) => (isSelected ? '#5765F2' : '#fff')};\n`;\n\nconst AddUserDialog = ({\n addMember,\n channelName,\n componentOwnerId,\n getNonInvitedUsers,\n nonInvitedUserList,\n}) => {\n const addUser = useCallback(\n (account) => addMember({ account, channel: channelName }),\n [addMember, channelName, channelName],\n );\n const isValidAccountName = useCallback((value) => {\n getNonInvitedUsers(value, channelName);\n const regex = /^[a-z\\d]+[-_]*[a-z\\d]+[-_]*[a-z\\d]+\\.(near|testnet)$/;\n let isValid = false;\n let error = '';\n\n if (!regex.test(value)) {\n isValid = false;\n error = '';\n } else if (groupMembers?.includes(value)) {\n isValid = false;\n error = 'User already in channel.';\n } else {\n isValid = true;\n error = '';\n }\n return { isValid, error };\n }, []);\n\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.InputPopup`}\n props={{\n componentOwnerId,\n title: `Invite user to #${channelName}`,\n placeholder: 'ex: username.near',\n buttonText: 'Invite',\n functionLoader: addUser,\n colors: {\n base: '#5765f2',\n hover: '#717cf0',\n disabled: '#3B487A',\n },\n toggle: (\n <AddMemberButton>\n <i className=\"bi bi-plus-circle-fill\" />\n Add new member\n </AddMemberButton>\n ),\n isChild: true,\n validator: isValidAccountName,\n autocomplete: true,\n nonInvitedUserList,\n }}\n />\n );\n};\n\nconst OptionsButton = ({ handleClick }) => {\n return (\n <div onClick={handleClick}>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill=\"white\"\n className=\"bi bi-three-dots\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M3 9.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z\" />\n </svg>\n </div>\n );\n};\n\nconst OptionsWindow = styled.div`\n width: 252px;\n display: flex;\n flex-direction: column;\n padding-top: 4px;\n padding-bottom: 4px;\n border-radius: 0px 0px 4px 4px;\n background-color: #25252a;\n pointer-events: auto;\n`;\n\nconst Option = styled.div`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n padding-left: 1rem;\n padding-right: 1rem;\n padding-top: 4px;\n padding-bottom: 4px;\n font-weight: 400;\n line-height: 150%;\n -webkit-font-smoothing: antialiased applied;\n cursor: pointer;\n :hover {\n background-color: #2a2b37;\n }\n`;\n\nconst RoleText = styled.div`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 12px;\n font-style: normal;\n font-weight: 700;\n line-height: 100%;\n`;\n\nconst ModeratorOptions = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 12px;\n`;\n\nconst isMod = userList?.some(\n (user) => user.id === context.accountId && user.moderator === true,\n);\nconst isOwner = context.accountId === channelOwner;\n\nconst OverLay = styled.div`\n position: absolute;\n z-index: 10;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst TopOverlay = styled.div`\n position: absolute;\n z-index: 20;\n`;\n\nconst ModeratorOptionsPopup = ({ id, user }) => {\n return (\n <Popover.Root>\n <Popover.Trigger asChild>\n <OptionsButton\n handleClick={() => {\n setOptionsOpen(id);\n setSelectedUser(user);\n }}\n />\n </Popover.Trigger>\n <TopOverlay>\n <Popover.Content\n className=\"PopoverContent\"\n side=\"bottom\"\n align=\"end\"\n sticky=\"always\"\n onInteractOutside={() => setOptionsOpen(-1)}\n >\n <OptionsWindow>\n {selectedUser.id !== channelOwner && isOwner && (\n <Option\n onClick={() =>\n promoteModerator(selectedUser.id, !selectedUser.moderator)\n }\n >{`${\n selectedUser.moderator ? 'Remove moderator' : 'Make moderator'\n }`}</Option>\n )}\n <Option onClick={() => removeUserFromChannel(selectedUser.id)}>\n Remove from channel\n </Option>\n </OptionsWindow>\n </Popover.Content>\n </TopOverlay>\n </Popover.Root>\n );\n};\n\nreturn (\n <>\n <AddUserDialog {...props} />\n {optionsOpen !== -1 && <OverLay />}\n <UserList>\n {userList.length &&\n userList.map((user, id) => (\n <UserListItem key={id}>\n <UserInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n active: user.active,\n componentOwnerId,\n }}\n />\n <Text isSelected={optionsOpen === id}>{user.id}</Text>\n </UserInfo>\n <ModeratorOptions>\n {(user.moderator || channelOwner === user.id) && (\n <RoleText>{`${\n channelOwner === user.id\n ? 'Channel Owner'\n : 'Channel Moderator'\n }`}</RoleText>\n )}\n {((isMod && !user.moderator && channelOwner !== user.id) ||\n isOwner) && <ModeratorOptionsPopup id={id} user={user} />}\n </ModeratorOptions>\n </UserListItem>\n ))}\n </UserList>\n </>\n);\n" }, "Calimero.DocsChain.Sidebar.DocsSidebar": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst SideMenu = styled.div`\n background-color: #0e0e10;\n padding-top: 1rem;\n width: 317px;\n`;\n\nconst HorizontalSeparatorLine = styled.div`\n background-color: '#BF4F74';\n width: 317px;\n height: 1px;\n background-color: #282933;\n margin-top: 1rem;\n margin-bottom: 1rem;\n`;\n\nconst handleCreatePage = useCallback(\n (isPrivate) => {\n props.onOpenCreatePage(isPrivate);\n },\n [props.createPageOpen],\n);\n\nreturn (\n <SideMenu>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.Header'}\n config={redirectConfig}\n props={{\n title: 'Shared Pages',\n onChange: () => handleCreatePage(false),\n componentName: 'CreateArticle',\n }}\n />\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.AllArticlesList'}\n config={redirectConfig}\n />\n <HorizontalSeparatorLine />\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.Header'}\n config={redirectConfig}\n props={{\n title: 'Settings',\n }}\n />\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.SettingsList'}\n config={redirectConfig}\n />\n </SideMenu>\n);\n" }, "Calimero.DocsChain.CreateArticle": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst initialBody = '# Hello world';\n\nconst PageContainer = styled.div`\n width: 100%;\n height: 100vh;\n padding-left: 60px;\n padding-right: 60px;\n background-color: #0e0e10;\n color: #ffffff;\n`;\n\nconst initialCreateArticleState = {\n articleId: '',\n articleBody: initialBody,\n createPage: { open: false, private: false },\n onOpenCreatePage: (isPrivate) =>\n State.update({ openCreatePage: { open: true, private: isPrivate } }),\n};\n\nState.init(initialCreateArticleState);\n\nconst saveArticle = () => {\n if (!state.articleBody) {\n return;\n }\n const params = {\n article_id: state.articleId,\n body: state.articleBody,\n };\n Near.fakCalimeroCall(contract, 'post_article', params);\n};\n\nreturn (\n <PageContainer>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Navbar.HorizontalNavbar'}\n config={redirectConfig}\n />\n <div className=\"d-flex\">\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.DocsSidebar'}\n config={redirectConfig}\n props={{\n onOpenCreatePage: state.onOpenCreatePage,\n createPageOpen: state.createPage.open,\n }}\n />\n <div>\n <div>\n <div>\n <button\n type=\"submit\"\n className=\"btn btn-success\"\n onClick={saveArticle}\n >\n Save Article\n </button>\n </div>\n <div className=\"d-flex flex-column pt-3\">\n <label htmlFor=\"inputArticleId\">\n Input article id (case-sensitive, without spaces):\n </label>\n <label htmlFor=\"inputArticleId\" className=\"small text-danger\">\n {state.errorId}\n </label>\n <input\n className=\"form-control mt-2\"\n id=\"inputArticleId\"\n value={state.articleId}\n placeholder=\"Input article id\"\n onChange={(e) => {\n State.update({\n ...state,\n articleId: e.target.value.replace(/\\s+/g, ''),\n });\n }}\n />\n </div>\n <div className=\"d-flex flex-column pt-3\">\n <label htmlFor=\"textareaArticleBody\">\n Input article body (in markdown format):\n </label>\n <label htmlFor=\"textareaArticleBody\" className=\"small text-danger\">\n {state.errorBody}\n </label>\n <div className=\"d-flex gap-2\" style={{ minHeight: '300px' }}>\n <div className=\"w-50\">\n <Widget\n src=\"calimero.near/widget/Calimero.DocsChain.MarkdownEditorIframe\"\n config={redirectConfig}\n props={{\n initialText: initialBody,\n onChange: (articleBody) => State.update({ articleBody }),\n }}\n />\n </div>\n <div className=\"w-50\">\n <Widget\n src=\"calimero.near/widget/Calimero.DocsChain.SocialMarkdown\"\n config={redirectConfig}\n props={{ text: state.articleBody }}\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </PageContainer>\n);\n" }, "Calimero.DocsChain.AllArticlesList": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst getDateLastEdit = (timestamp) => {\n const date = new Date(Number(timestamp) / 1e6);\n const dateString = `${date.toLocaleDateString()} / ${date.toLocaleTimeString()}`;\n return dateString;\n};\n\nconst allArticles = Near.calimeroView(\n contract,\n 'get_article_ids_paged',\n {},\n).map((articleId) => {\n return {\n id: articleId,\n data: Near.calimeroView(contract, 'get_article', { article_id: articleId }),\n };\n});\nconsole.log(allArticles);\n\nreturn (\n <ol>\n {allArticles &&\n allArticles.map((article) => (\n <li key={article.id}>\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.ArticleView?articleId=${article.id}&blockHeight=${article.data.blockHeight}&lastEditor=${article.data.author}`,\n )}\n >\n {article.id}{' '}\n <small>\n (author: {article.data.author}\n {getDateLastEdit(article.data.timestamp)})\n </small>\n </a>\n </li>\n ))}\n </ol>\n);\n" }, "Calimero.Curb.ProfileIcon.UserProfileIcon": { "": "const ProfileIconContainer = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n ${({ width }) => width && `width: ${width};`}\n ${({ height }) => height && `height: ${width};`}\n`;\n\nconst ActiveStatusCricle = styled.div`\n position: absolute;\n bottom: -2px;\n right: -2px;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n ${({ active }) =>\n active ? 'background-color: #00FF66;' : 'background-color: #777583;'}\n border: 1px solid #1A1A1D;\n`;\nconst accountId = props.accountId;\nconst showStatus = props.showStatus ?? true;\nconst width = props.width ?? '24px';\nconst height = props.height ?? '24px';\nconst componentOwnerId = props.componentOwnerId;\n\nreturn (\n <ProfileIconContainer width={width} height={height}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ProfileIcon.Image`}\n props={{\n accountId,\n alt: `profile-icon-${accountId}`,\n className: 'rounded-circle',\n style: { width: width, height: height, objectFit: 'cover' },\n thumbnail: 'thumbnail',\n fallbackUrl: 'https://i.imgur.com/e8buxpa.png',\n componentOwnerId: componentOwnerId,\n }}\n />\n {showStatus && <ActiveStatusCricle active={props.active} />}\n </ProfileIconContainer>\n);\n" }, "Calimero.TaskChain.Popups.CreateTaskPopup": { "": "const {\n componentOwnerId,\n createTaskButton,\n addCreateTaskStatus,\n cleanCreateTaskStatus,\n handleColumns,\n projectId,\n contract,\n columnTitle,\n columnIndex,\n addTaskToColumn,\n} = props;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst DescriptionInput = styled.textarea`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n height: 40px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-width: 0px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n display: block;\n flex: 1;\n`;\n\nconst TitleInput = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%;\n letter-spacing: 0em;\n text-align: left;\n flex: 1;\n height: 40px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-width: 0px;\n }\n ::placeholder {\n color: #ffffff7f;\n }\n border: none;\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 30%;\n height: 40px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-width: 0px;\n }\n z-index: 10;\n border: none;\n display: flex;\n align-items: center;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst DescriptionContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n width: 100%;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n width: 100%;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n right: 15.5rem;\n top: 7rem;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst Overlay = styled.div`\n background-color: var(--blackA9);\n position: fixed;\n z-index: 1000;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);\n`;\n\nconst ModalContent = styled.div`\n position: absolute;\n width: 500px;\n padding: 30px;\n border-radius: 20px;\n color: white;\n`;\n\nconst DialogTitle = styled.div`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 15px;\n font-style: normal;\n font-weight: 700;\n line-height: 100%;\n`;\n\nconst [taskTitle, setTaskTitle] = useState('');\nconst [taskDescription, setTaskDescription] = useState('');\nconst [addTaskTitleMissing, setAddTaskTitleMissing] = useState(false);\nconst [isCreateTaskVisible, setIsCreateTaskVisible] = useState(false);\nconst [assignee, setAssignee] = useState(null);\n\nconst onChangeTaskTitle = ({ target }) => {\n setAddTaskTitleMissing(false);\n setTaskTitle(target.value);\n};\n\nconst onChangeTaskDescription = ({ target }) => {\n setTaskDescription(target.value);\n};\n\nconst handleOnClose = useCallback(() => {\n setIsCreateTaskVisible(false);\n setAddTaskTitleMissing(false);\n setAssignee(null);\n setTaskTitle('');\n setTaskDescription('');\n}, []);\n\nconst handleCreateTask = useCallback(() => {\n if (!taskTitle || taskTitle.trim() === '') {\n setAddTaskTitleMissing(true);\n return;\n }\n\n const newStatus = { title: taskTitle, status: 'Saving...' };\n try {\n addCreateTaskStatus(newStatus);\n\n const newTask = {\n project_id: projectId,\n title: taskTitle,\n description: taskDescription,\n status: columnTitle,\n assignee: assignee === 'no_assignee' ? null : assignee,\n reporter: context.accountId,\n };\n\n addTaskToColumn(columnIndex, newTask);\n\n Near.fakCalimeroCall(contract, 'create_task', {\n project_id: projectId,\n title: taskTitle,\n description: taskDescription,\n labels: [],\n assignee: assignee === 'no_assignee' ? null : assignee,\n }).then(() => {\n newStatus.status = 'Saved';\n addCreateTaskStatus(newStatus);\n setTimeout(() => cleanCreateTaskStatus(taskTitle), 3000);\n });\n } catch (e) {\n newStatus.status = 'Error saving';\n addCreateTaskStatus(newStatus);\n Storage.privateSet('tempColumns' + contract + projectId, '');\n setTimeout(() => cleanCreateTaskStatus(taskTitle), 3000);\n }\n setIsCreateTaskVisible(false);\n setTaskTitle('');\n setTaskDescription('');\n setAssignee(null);\n}, [\n taskTitle,\n taskDescription,\n setIsCreateTaskVisible,\n setTaskTitle,\n setTaskDescription,\n assignee,\n]);\n\nconst content = (\n <Overlay>\n <ModalContent>\n <PopupContainer>\n <Header>\n <DialogTitle>Create new Task</DialogTitle>\n <CloseButton onClick={handleOnClose}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <InputContainer>\n <Label>Title</Label>\n <TitleInput\n onChange={onChangeTaskTitle}\n value={taskTitle}\n placeholder=\"Set a Task Title\"\n />\n </InputContainer>\n {addTaskTitleMissing && <MissingTitle>Missing title</MissingTitle>}\n </FieldContainer>\n <FieldContainer>\n <InputContainer>\n <Label>Assignee</Label>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.AssigneeDropdown`}\n props={{\n componentOwnerId,\n assignee,\n setAssignee,\n contract,\n projectId,\n }}\n />\n </InputContainer>\n </FieldContainer>\n <FieldContainer>\n <DescriptionContainer>\n <Label>Description</Label>\n <DescriptionInput\n onChange={onChangeTaskDescription}\n value={taskDescription}\n placeholder=\"Add Description\"\n />\n </DescriptionContainer>\n </FieldContainer>\n <FunctionButton\n onClick={() =>\n handleCreateTask(\n taskTitle,\n taskDescription,\n setIsCreateTaskVisible,\n setAddTaskTitleMissing,\n setTaskTitle,\n setTaskDescription,\n )\n }\n >\n Create\n </FunctionButton>\n </PopupContainer>\n </ModalContent>\n </Overlay>\n);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.BaseModal`}\n props={{\n toggle: createTaskButton,\n content,\n open: isCreateTaskVisible,\n onOpenChange: (open) => setIsCreateTaskVisible(open),\n }}\n />\n);\n" }, "Calimero.Curb.SideSelector.DirectMessagesDropdown": { "": "const title = props.title;\nconst onToggleDMs = props.onToggleDMs;\nconst directMessagesOpen = props.directMessagesOpen;\n\nconst Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n color: #777583;\n :hover {\n color: #ffffff;\n }\n cursor: pointer;\n @media (max-width: 1024px) {\n width: 100%;\n }\n`;\nconst TextBold = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n`;\nconst IconChevronContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n font-size: 1.25rem;\n`;\n\nconst IconChevron = styled.i`\n transition: transform 5s ease;\n cursor: pointer;\n`;\n\nreturn (\n <Container onClick={() => onToggleDMs(!directMessagesOpen)}>\n <TextBold>{title}</TextBold>\n <IconChevronContainer>\n <IconChevron\n className={`${\n directMessagesOpen ? 'bi bi-chevron-down' : 'bi bi-chevron-up'\n }`}\n />\n </IconChevronContainer>\n </Container>\n);\n" }, "Calimero.Curb.EmojiSelector.EmojiSelector": { "": "const emojiObj = {\n People: [\n {\n emoji: '😀',\n title: 'Grinning Face',\n },\n {\n emoji: '😃',\n title: 'Grinning Face with Big Eyes',\n },\n {\n emoji: '😄',\n title: 'Grinning Face with Smiling Eyes',\n },\n {\n emoji: '😁',\n title: 'Beaming Face with Smiling Eyes',\n },\n {\n emoji: '😆',\n title: 'Grinning Squinting Face',\n },\n {\n emoji: '😅',\n title: 'Grinning Face with Sweat',\n },\n {\n emoji: '🤣',\n title: 'Rolling on the Floor Laughing',\n },\n {\n emoji: '😂',\n title: 'Face with Tears of Joy',\n },\n {\n emoji: '🙂',\n title: 'Slightly Smiling Face',\n },\n {\n emoji: '🙃',\n title: 'Upside-Down Face',\n },\n {\n emoji: '😉',\n title: 'Winking Face',\n },\n {\n emoji: '😊',\n title: 'Smiling Face with Smiling Eyes',\n },\n {\n emoji: '😇',\n title: 'Smiling Face with Halo',\n },\n {\n emoji: '🥰',\n title: 'Smiling Face with Hearts',\n },\n {\n emoji: '😍',\n title: 'Smiling Face with Heart-Eyes',\n },\n {\n emoji: '🤩',\n title: 'Star-Struck',\n },\n {\n emoji: '😘',\n title: 'Face Blowing a Kiss',\n },\n {\n emoji: '😗',\n title: 'Kissing Face',\n },\n {\n emoji: '☺️',\n title: 'Smiling Face',\n },\n {\n emoji: '😚',\n title: 'Kissing Face with Closed Eyes',\n },\n {\n emoji: '😙',\n title: 'Kissing Face with Smiling Eyes',\n },\n {\n emoji: '🥲',\n title: 'Smiling Face with Tear',\n },\n {\n emoji: '😋',\n title: 'Face Savoring Food',\n },\n {\n emoji: '😛',\n title: 'Face with Tongue',\n },\n {\n emoji: '😜',\n title: 'Winking Face with Tongue',\n },\n {\n emoji: '🤪',\n title: 'Zany Face',\n },\n {\n emoji: '😝',\n title: 'Squinting Face with Tongue',\n },\n {\n emoji: '🤑',\n title: 'Money-Mouth Face',\n },\n {\n emoji: '🤗',\n title: 'Smiling Face with Open Hands',\n },\n {\n emoji: '🤭',\n title: 'Face with Hand Over Mouth',\n },\n {\n emoji: '🤫',\n title: 'Shushing Face',\n },\n {\n emoji: '🤔',\n title: 'Thinking Face',\n },\n {\n emoji: '🤐',\n title: 'Zipper-Mouth Face',\n },\n {\n emoji: '🤨',\n title: 'Face with Raised Eyebrow',\n },\n {\n emoji: '😐',\n title: 'Neutral Face',\n },\n {\n emoji: '😑',\n title: 'Expressionless Face',\n },\n {\n emoji: '😶',\n title: 'Face Without Mouth',\n },\n {\n emoji: '😶‍🌫️',\n title: 'Face in Clouds',\n },\n {\n emoji: '😏',\n title: 'Smirking Face',\n },\n {\n emoji: '😒',\n title: 'Unamused Face',\n },\n {\n emoji: '🙄',\n title: 'Face with Rolling Eyes',\n },\n {\n emoji: '😬',\n title: 'Grimacing Face',\n },\n {\n emoji: '😮‍💨',\n title: 'Face Exhaling',\n },\n {\n emoji: '🤥',\n title: 'Lying Face',\n },\n {\n emoji: '😌',\n title: 'Relieved Face',\n },\n {\n emoji: '😔',\n title: 'Pensive Face',\n },\n {\n emoji: '😪',\n title: 'Sleepy Face',\n },\n {\n emoji: '🤤',\n title: 'Drooling Face',\n },\n {\n emoji: '😴',\n title: 'Sleeping Face',\n },\n {\n emoji: '😷',\n title: 'Face with Medical Mask',\n },\n {\n emoji: '🤒',\n title: 'Face with Thermometer',\n },\n {\n emoji: '🤕',\n title: 'Face with Head-Bandage',\n },\n {\n emoji: '🤢',\n title: 'Nauseated Face',\n },\n {\n emoji: '🤮',\n title: 'Face Vomiting',\n },\n {\n emoji: '🤧',\n title: 'Sneezing Face',\n },\n {\n emoji: '🥵',\n title: 'Hot Face',\n },\n {\n emoji: '🥶',\n title: 'Cold Face',\n },\n {\n emoji: '🥴',\n title: 'Woozy Face',\n },\n {\n emoji: '😵',\n title: 'Face with Crossed-Out Eyes',\n },\n {\n emoji: '😵‍💫',\n title: 'Face with Spiral Eyes',\n },\n {\n emoji: '🤯',\n title: 'Exploding Head',\n },\n {\n emoji: '🤠',\n title: 'Cowboy Hat Face',\n },\n {\n emoji: '🥳',\n title: 'Partying Face',\n },\n {\n emoji: '🥸',\n title: 'Disguised Face',\n },\n {\n emoji: '😎',\n title: 'Smiling Face with Sunglasses',\n },\n {\n emoji: '🤓',\n title: 'Nerd Face',\n },\n {\n emoji: '🧐',\n title: 'Face with Monocle',\n },\n {\n emoji: '😕',\n title: 'Confused Face',\n },\n {\n emoji: '😟',\n title: 'Worried Face',\n },\n {\n emoji: '🙁',\n title: 'Slightly Frowning Face',\n },\n {\n emoji: '☹️',\n title: 'Frowning Face',\n },\n {\n emoji: '😮',\n title: 'Face with Open Mouth',\n },\n {\n emoji: '😯',\n title: 'Hushed Face',\n },\n {\n emoji: '😲',\n title: 'Astonished Face',\n },\n {\n emoji: '😳',\n title: 'Flushed Face',\n },\n {\n emoji: '🥺',\n title: 'Pleading Face',\n },\n {\n emoji: '😦',\n title: 'Frowning Face with Open Mouth',\n },\n {\n emoji: '😧',\n title: 'Anguished Face',\n },\n {\n emoji: '😨',\n title: 'Fearful Face',\n },\n {\n emoji: '😰',\n title: 'Anxious Face with Sweat',\n },\n {\n emoji: '😥',\n title: 'Sad but Relieved Face',\n },\n {\n emoji: '😢',\n title: 'Crying Face',\n },\n {\n emoji: '😭',\n title: 'Loudly Crying Face',\n },\n {\n emoji: '😱',\n title: 'Face Screaming in Fear',\n },\n {\n emoji: '😖',\n title: 'Confounded Face',\n },\n {\n emoji: '😣',\n title: 'Persevering Face',\n },\n {\n emoji: '😞',\n title: 'Disappointed Face',\n },\n {\n emoji: '😓',\n title: 'Downcast Face with Sweat',\n },\n {\n emoji: '😩',\n title: 'Weary Face',\n },\n {\n emoji: '😫',\n title: 'Tired Face',\n },\n {\n emoji: '🥱',\n title: 'Yawning Face',\n },\n {\n emoji: '😤',\n title: 'Face with Steam From Nose',\n },\n {\n emoji: '😡',\n title: 'Enraged Face',\n },\n {\n emoji: '😠',\n title: 'Angry Face',\n },\n {\n emoji: '🤬',\n title: 'Face with Symbols on Mouth',\n },\n {\n emoji: '😈',\n title: 'Smiling Face with Horns',\n },\n {\n emoji: '👿',\n title: 'Angry Face with Horns',\n },\n {\n emoji: '💀',\n title: 'Skull',\n },\n {\n emoji: '☠️',\n title: 'Skull and Crossbones',\n },\n {\n emoji: '💩',\n title: 'Pile of Poo',\n },\n {\n emoji: '🤡',\n title: 'Clown Face',\n },\n {\n emoji: '👹',\n title: 'Ogre',\n },\n {\n emoji: '👺',\n title: 'Goblin',\n },\n {\n emoji: '👻',\n title: 'Ghost',\n },\n {\n emoji: '👽',\n title: 'Alien',\n },\n {\n emoji: '👾',\n title: 'Alien Monster',\n },\n {\n emoji: '🤖',\n title: 'Robot',\n },\n {\n emoji: '😺',\n title: 'Grinning Cat',\n },\n {\n emoji: '😸',\n title: 'Grinning Cat with Smiling Eyes',\n },\n {\n emoji: '😹',\n title: 'Cat with Tears of Joy',\n },\n {\n emoji: '😻',\n title: 'Smiling Cat with Heart-Eyes',\n },\n {\n emoji: '😼',\n title: 'Cat with Wry Smile',\n },\n {\n emoji: '😽',\n title: 'Kissing Cat',\n },\n {\n emoji: '🙀',\n title: 'Weary Cat',\n },\n {\n emoji: '😿',\n title: 'Crying Cat',\n },\n {\n emoji: '😾',\n title: 'Pouting Cat',\n },\n {\n emoji: '💋',\n title: 'Kiss Mark',\n },\n {\n emoji: '👋',\n title: 'Waving Hand',\n },\n {\n emoji: '🤚',\n title: 'Raised Back of Hand',\n },\n {\n emoji: '🖐️',\n title: 'Hand with Fingers Splayed',\n },\n {\n emoji: '✋',\n title: 'Raised Hand',\n },\n {\n emoji: '🖖',\n title: 'Vulcan Salute',\n },\n {\n emoji: '👌',\n title: 'OK Hand',\n },\n {\n emoji: '🤌',\n title: 'Pinched Fingers',\n },\n {\n emoji: '🤏',\n title: 'Pinching Hand',\n },\n {\n emoji: '✌️',\n title: 'Victory Hand',\n },\n {\n emoji: '🤞',\n title: 'Crossed Fingers',\n },\n {\n emoji: '🤟',\n title: 'Love-You Gesture',\n },\n {\n emoji: '🤘',\n title: 'Sign of the Horns',\n },\n {\n emoji: '🤙',\n title: 'Call Me Hand',\n },\n {\n emoji: '👈',\n title: 'Backhand Index Pointing Left',\n },\n {\n emoji: '👉',\n title: 'Backhand Index Pointing Right',\n },\n {\n emoji: '👆',\n title: 'Backhand Index Pointing Up',\n },\n {\n emoji: '🖕',\n title: 'Middle Finger',\n },\n {\n emoji: '👇',\n title: 'Backhand Index Pointing Down',\n },\n {\n emoji: '☝️',\n title: 'Index Pointing Up',\n },\n {\n emoji: '👍',\n title: 'Thumbs Up',\n },\n {\n emoji: '👎',\n title: 'Thumbs Down',\n },\n {\n emoji: '✊',\n title: 'Raised Fist',\n },\n {\n emoji: '👊',\n title: 'Oncoming Fist',\n },\n {\n emoji: '🤛',\n title: 'Left-Facing Fist',\n },\n {\n emoji: '🤜',\n title: 'Right-Facing Fist',\n },\n {\n emoji: '👏',\n title: 'Clapping Hands',\n },\n {\n emoji: '🙌',\n title: 'Raising Hands',\n },\n {\n emoji: '👐',\n title: 'Open Hands',\n },\n {\n emoji: '🤲',\n title: 'Palms Up Together',\n },\n {\n emoji: '🤝',\n title: 'Handshake',\n },\n {\n emoji: '🙏',\n title: 'Folded Hands',\n },\n {\n emoji: '✍️',\n title: 'Writing Hand',\n },\n {\n emoji: '💅',\n title: 'Nail Polish',\n },\n {\n emoji: '🤳',\n title: 'Selfie',\n },\n {\n emoji: '💪',\n title: 'Flexed Biceps',\n },\n {\n emoji: '🦾',\n title: 'Mechanical Arm',\n },\n {\n emoji: '🦿',\n title: 'Mechanical Leg',\n },\n {\n emoji: '🦵',\n title: 'Leg',\n },\n {\n emoji: '🦶',\n title: 'Foot',\n },\n {\n emoji: '👂',\n title: 'Ear',\n },\n {\n emoji: '🦻',\n title: 'Ear with Hearing Aid',\n },\n {\n emoji: '👃',\n title: 'Nose',\n },\n {\n emoji: '🧠',\n title: 'Brain',\n },\n {\n emoji: '🫀',\n title: 'Anatomical Heart',\n },\n {\n emoji: '🫁',\n title: 'Lungs',\n },\n {\n emoji: '🦷',\n title: 'Tooth',\n },\n {\n emoji: '🦴',\n title: 'Bone',\n },\n {\n emoji: '👀',\n title: 'Eyes',\n },\n {\n emoji: '👁️',\n title: 'Eye',\n },\n {\n emoji: '👅',\n title: 'Tongue',\n },\n {\n emoji: '👄',\n title: 'Mouth',\n },\n {\n emoji: '👶',\n title: 'Baby',\n },\n {\n emoji: '🧒',\n title: 'Child',\n },\n {\n emoji: '👦',\n title: 'Boy',\n },\n {\n emoji: '👧',\n title: 'Girl',\n },\n {\n emoji: '🧑',\n title: 'Person',\n },\n {\n emoji: '👱',\n title: 'Person: Blond Hair',\n },\n {\n emoji: '👨',\n title: 'Man',\n },\n {\n emoji: '🧔',\n title: 'Person: Beard',\n },\n {\n emoji: '👨‍🦰',\n title: 'Man: Red Hair',\n },\n {\n emoji: '👨‍🦱',\n title: 'Man: Curly Hair',\n },\n {\n emoji: '👨‍🦳',\n title: 'Man: White Hair',\n },\n {\n emoji: '👨‍🦲',\n title: 'Man: Bald',\n },\n {\n emoji: '👩',\n title: 'Woman',\n },\n {\n emoji: '👩‍🦰',\n title: 'Woman: Red Hair',\n },\n {\n emoji: '🧑‍🦰',\n title: 'Person: Red Hair',\n },\n {\n emoji: '👩‍🦱',\n title: 'Woman: Curly Hair',\n },\n {\n emoji: '🧑‍🦱',\n title: 'Person: Curly Hair',\n },\n {\n emoji: '👩‍🦳',\n title: 'Woman: White Hair',\n },\n {\n emoji: '🧑‍🦳',\n title: 'Person: White Hair',\n },\n {\n emoji: '👩‍🦲',\n title: 'Woman: Bald',\n },\n {\n emoji: '🧑‍🦲',\n title: 'Person: Bald',\n },\n {\n emoji: '👱‍♀️',\n title: 'Woman: Blond Hair',\n },\n {\n emoji: '👱‍♂️',\n title: 'Man: Blond Hair',\n },\n {\n emoji: '🧓',\n title: 'Older Person',\n },\n {\n emoji: '👴',\n title: 'Old Man',\n },\n {\n emoji: '👵',\n title: 'Old Woman',\n },\n {\n emoji: '🙍',\n title: 'Person Frowning',\n },\n {\n emoji: '🙍‍♂️',\n title: 'Man Frowning',\n },\n {\n emoji: '🙍‍♀️',\n title: 'Woman Frowning',\n },\n {\n emoji: '🙎',\n title: 'Person Pouting',\n },\n {\n emoji: '🙎‍♂️',\n title: 'Man Pouting',\n },\n {\n emoji: '🙎‍♀️',\n title: 'Woman Pouting',\n },\n {\n emoji: '🙅',\n title: 'Person Gesturing No',\n },\n {\n emoji: '🙅‍♂️',\n title: 'Man Gesturing No',\n },\n {\n emoji: '🙅‍♀️',\n title: 'Woman Gesturing No',\n },\n {\n emoji: '🙆',\n title: 'Person Gesturing OK',\n },\n {\n emoji: '🙆‍♂️',\n title: 'Man Gesturing OK',\n },\n {\n emoji: '🙆‍♀️',\n title: 'Woman Gesturing OK',\n },\n {\n emoji: '💁',\n title: 'Person Tipping Hand',\n },\n {\n emoji: '💁‍♂️',\n title: 'Man Tipping Hand',\n },\n {\n emoji: '💁‍♀️',\n title: 'Woman Tipping Hand',\n },\n {\n emoji: '🙋',\n title: 'Person Raising Hand',\n },\n {\n emoji: '🙋‍♂️',\n title: 'Man Raising Hand',\n },\n {\n emoji: '🙋‍♀️',\n title: 'Woman Raising Hand',\n },\n {\n emoji: '🧏',\n title: 'Deaf Person',\n },\n {\n emoji: '🧏‍♂️',\n title: 'Deaf Man',\n },\n {\n emoji: '🧏‍♀️',\n title: 'Deaf Woman',\n },\n {\n emoji: '🙇',\n title: 'Person Bowing',\n },\n {\n emoji: '🙇‍♂️',\n title: 'Man Bowing',\n },\n {\n emoji: '🙇‍♀️',\n title: 'Woman Bowing',\n },\n {\n emoji: '🤦',\n title: 'Person Facepalming',\n },\n {\n emoji: '🤦‍♂️',\n title: 'Man Facepalming',\n },\n {\n emoji: '🤦‍♀️',\n title: 'Woman Facepalming',\n },\n {\n emoji: '🤷',\n title: 'Person Shrugging',\n },\n {\n emoji: '🤷‍♂️',\n title: 'Man Shrugging',\n },\n {\n emoji: '🤷‍♀️',\n title: 'Woman Shrugging',\n },\n {\n emoji: '🧑‍⚕️',\n title: 'Health Worker',\n },\n {\n emoji: '👨‍⚕️',\n title: 'Man Health Worker',\n },\n {\n emoji: '👩‍⚕️',\n title: 'Woman Health Worker',\n },\n {\n emoji: '🧑‍🎓',\n title: 'Student',\n },\n {\n emoji: '👨‍🎓',\n title: 'Man Student',\n },\n {\n emoji: '👩‍🎓',\n title: 'Woman Student',\n },\n {\n emoji: '🧑‍🏫',\n title: 'Teacher',\n },\n {\n emoji: '👨‍🏫',\n title: 'Man Teacher',\n },\n {\n emoji: '👩‍🏫',\n title: 'Woman Teacher',\n },\n {\n emoji: '🧑‍⚖️',\n title: 'Judge',\n },\n {\n emoji: '👨‍⚖️',\n title: 'Man Judge',\n },\n {\n emoji: '👩‍⚖️',\n title: 'Woman Judge',\n },\n {\n emoji: '🧑‍🌾',\n title: 'Farmer',\n },\n {\n emoji: '👨‍🌾',\n title: 'Man Farmer',\n },\n {\n emoji: '👩‍🌾',\n title: 'Woman Farmer',\n },\n {\n emoji: '🧑‍🍳',\n title: 'Cook',\n },\n {\n emoji: '👨‍🍳',\n title: 'Man Cook',\n },\n {\n emoji: '👩‍🍳',\n title: 'Woman Cook',\n },\n {\n emoji: '🧑‍🔧',\n title: 'Mechanic',\n },\n {\n emoji: '👨‍🔧',\n title: 'Man Mechanic',\n },\n {\n emoji: '👩‍🔧',\n title: 'Woman Mechanic',\n },\n {\n emoji: '🧑‍🏭',\n title: 'Factory Worker',\n },\n {\n emoji: '👨‍🏭',\n title: 'Man Factory Worker',\n },\n {\n emoji: '👩‍🏭',\n title: 'Woman Factory Worker',\n },\n {\n emoji: '🧑‍💼',\n title: 'Office Worker',\n },\n {\n emoji: '👨‍💼',\n title: 'Man Office Worker',\n },\n {\n emoji: '👩‍💼',\n title: 'Woman Office Worker',\n },\n {\n emoji: '🧑‍🔬',\n title: 'Scientist',\n },\n {\n emoji: '👨‍🔬',\n title: 'Man Scientist',\n },\n {\n emoji: '👩‍🔬',\n title: 'Woman Scientist',\n },\n {\n emoji: '🧑‍💻',\n title: 'Technologist',\n },\n {\n emoji: '👨‍💻',\n title: 'Man Technologist',\n },\n {\n emoji: '👩‍💻',\n title: 'Woman Technologist',\n },\n {\n emoji: '🧑‍🎤',\n title: 'Singer',\n },\n {\n emoji: '👨‍🎤',\n title: 'Man Singer',\n },\n {\n emoji: '👩‍🎤',\n title: 'Woman Singer',\n },\n {\n emoji: '🧑‍🎨',\n title: 'Artist',\n },\n {\n emoji: '👨‍🎨',\n title: 'Man Artist',\n },\n {\n emoji: '👩‍🎨',\n title: 'Woman Artist',\n },\n {\n emoji: '🧑‍✈️',\n title: 'Pilot',\n },\n {\n emoji: '👨‍✈️',\n title: 'Man Pilot',\n },\n {\n emoji: '👩‍✈️',\n title: 'Woman Pilot',\n },\n {\n emoji: '🧑‍🚀',\n title: 'Astronaut',\n },\n {\n emoji: '👨‍🚀',\n title: 'Man Astronaut',\n },\n {\n emoji: '👩‍🚀',\n title: 'Woman Astronaut',\n },\n {\n emoji: '🧑‍🚒',\n title: 'Firefighter',\n },\n {\n emoji: '👨‍🚒',\n title: 'Man Firefighter',\n },\n {\n emoji: '👩‍🚒',\n title: 'Woman Firefighter',\n },\n {\n emoji: '👮',\n title: 'Police Officer',\n },\n {\n emoji: '👮‍♂️',\n title: 'Man Police Officer',\n },\n {\n emoji: '👮‍♀️',\n title: 'Woman Police Officer',\n },\n {\n emoji: '🕵️',\n title: 'Detective',\n },\n {\n emoji: '🕵️‍♂️',\n title: 'Man Detective',\n },\n {\n emoji: '🕵️‍♀️',\n title: 'Woman Detective',\n },\n {\n emoji: '💂',\n title: 'Guard',\n },\n {\n emoji: '💂‍♂️',\n title: 'Man Guard',\n },\n {\n emoji: '💂‍♀️',\n title: 'Woman Guard',\n },\n {\n emoji: '🥷',\n title: 'Ninja',\n },\n {\n emoji: '👷',\n title: 'Construction Worker',\n },\n {\n emoji: '👷‍♂️',\n title: 'Man Construction Worker',\n },\n {\n emoji: '👷‍♀️',\n title: 'Woman Construction Worker',\n },\n {\n emoji: '🤴',\n title: 'Prince',\n },\n {\n emoji: '👸',\n title: 'Princess',\n },\n {\n emoji: '👳',\n title: 'Person Wearing Turban',\n },\n {\n emoji: '👳‍♂️',\n title: 'Man Wearing Turban',\n },\n {\n emoji: '👳‍♀️',\n title: 'Woman Wearing Turban',\n },\n {\n emoji: '👲',\n title: 'Person with Skullcap',\n },\n {\n emoji: '🧕',\n title: 'Woman with Headscarf',\n },\n {\n emoji: '🤵',\n title: 'Person in Tuxedo',\n },\n {\n emoji: '🤵‍♂️',\n title: 'Man in Tuxedo',\n },\n {\n emoji: '🤵‍♀️',\n title: 'Woman in Tuxedo',\n },\n {\n emoji: '👰',\n title: 'Person with Veil',\n },\n {\n emoji: '👰‍♂️',\n title: 'Man with Veil',\n },\n {\n emoji: '👰‍♀️',\n title: 'Woman with Veil',\n },\n {\n emoji: '🤰',\n title: 'Pregnant Woman',\n },\n {\n emoji: '🤱',\n title: 'Breast-Feeding',\n },\n {\n emoji: '👩‍🍼',\n title: 'Woman Feeding Baby',\n },\n {\n emoji: '👨‍🍼',\n title: 'Man Feeding Baby',\n },\n {\n emoji: '🧑‍🍼',\n title: 'Person Feeding Baby',\n },\n {\n emoji: '👼',\n title: 'Baby Angel',\n },\n {\n emoji: '🎅',\n title: 'Santa Claus',\n },\n {\n emoji: '🤶',\n title: 'Mrs. Claus',\n },\n {\n emoji: '🧑‍🎄',\n title: 'Mx Claus',\n },\n {\n emoji: '🦸',\n title: 'Superhero',\n },\n {\n emoji: '🦸‍♂️',\n title: 'Man Superhero',\n },\n {\n emoji: '🦸‍♀️',\n title: 'Woman Superhero',\n },\n {\n emoji: '🦹',\n title: 'Supervillain',\n },\n {\n emoji: '🦹‍♂️',\n title: 'Man Supervillain',\n },\n {\n emoji: '🦹‍♀️',\n title: 'Woman Supervillain',\n },\n {\n emoji: '🧙',\n title: 'Mage',\n },\n {\n emoji: '🧙‍♂️',\n title: 'Man Mage',\n },\n {\n emoji: '🧙‍♀️',\n title: 'Woman Mage',\n },\n {\n emoji: '🧚',\n title: 'Fairy',\n },\n {\n emoji: '🧚‍♂️',\n title: 'Man Fairy',\n },\n {\n emoji: '🧚‍♀️',\n title: 'Woman Fairy',\n },\n {\n emoji: '🧛',\n title: 'Vampire',\n },\n {\n emoji: '🧛‍♂️',\n title: 'Man Vampire',\n },\n {\n emoji: '🧛‍♀️',\n title: 'Woman Vampire',\n },\n {\n emoji: '🧜',\n title: 'Merperson',\n },\n {\n emoji: '🧜‍♂️',\n title: 'Merman',\n },\n {\n emoji: '🧜‍♀️',\n title: 'Mermaid',\n },\n {\n emoji: '🧝',\n title: 'Elf',\n },\n {\n emoji: '🧝‍♂️',\n title: 'Man Elf',\n },\n {\n emoji: '🧝‍♀️',\n title: 'Woman Elf',\n },\n {\n emoji: '🧞',\n title: 'Genie',\n },\n {\n emoji: '🧞‍♂️',\n title: 'Man Genie',\n },\n {\n emoji: '🧞‍♀️',\n title: 'Woman Genie',\n },\n {\n emoji: '🧟',\n title: 'Zombie',\n },\n {\n emoji: '🧟‍♂️',\n title: 'Man Zombie',\n },\n {\n emoji: '🧟‍♀️',\n title: 'Woman Zombie',\n },\n {\n emoji: '💆',\n title: 'Person Getting Massage',\n },\n {\n emoji: '💆‍♂️',\n title: 'Man Getting Massage',\n },\n {\n emoji: '💆‍♀️',\n title: 'Woman Getting Massage',\n },\n {\n emoji: '💇',\n title: 'Person Getting Haircut',\n },\n {\n emoji: '💇‍♂️',\n title: 'Man Getting Haircut',\n },\n {\n emoji: '💇‍♀️',\n title: 'Woman Getting Haircut',\n },\n {\n emoji: '🚶',\n title: 'Person Walking',\n },\n {\n emoji: '🚶‍♂️',\n title: 'Man Walking',\n },\n {\n emoji: '🚶‍♀️',\n title: 'Woman Walking',\n },\n {\n emoji: '🧍',\n title: 'Person Standing',\n },\n {\n emoji: '🧍‍♂️',\n title: 'Man Standing',\n },\n {\n emoji: '🧍‍♀️',\n title: 'Woman Standing',\n },\n {\n emoji: '🧎',\n title: 'Person Kneeling',\n },\n {\n emoji: '🧎‍♂️',\n title: 'Man Kneeling',\n },\n {\n emoji: '🧎‍♀️',\n title: 'Woman Kneeling',\n },\n {\n emoji: '🧑‍🦯',\n title: 'Person with White Cane',\n },\n {\n emoji: '👨‍🦯',\n title: 'Man with White Cane',\n },\n {\n emoji: '👩‍🦯',\n title: 'Woman with White Cane',\n },\n {\n emoji: '🧑‍🦼',\n title: 'Person in Motorized Wheelchair',\n },\n {\n emoji: '👨‍🦼',\n title: 'Man in Motorized Wheelchair',\n },\n {\n emoji: '👩‍🦼',\n title: 'Woman in Motorized Wheelchair',\n },\n {\n emoji: '🧑‍🦽',\n title: 'Person in Manual Wheelchair',\n },\n {\n emoji: '👨‍🦽',\n title: 'Man in Manual Wheelchair',\n },\n {\n emoji: '👩‍🦽',\n title: 'Woman in Manual Wheelchair',\n },\n {\n emoji: '🏃',\n title: 'Person Running',\n },\n {\n emoji: '🏃‍♂️',\n title: 'Man Running',\n },\n {\n emoji: '🏃‍♀️',\n title: 'Woman Running',\n },\n {\n emoji: '💃',\n title: 'Woman Dancing',\n },\n {\n emoji: '🕺',\n title: 'Man Dancing',\n },\n {\n emoji: '🕴️',\n title: 'Person in Suit Levitating',\n },\n {\n emoji: '👯',\n title: 'People with Bunny Ears',\n },\n {\n emoji: '👯‍♂️',\n title: 'Men with Bunny Ears',\n },\n {\n emoji: '👯‍♀️',\n title: 'Women with Bunny Ears',\n },\n {\n emoji: '🧖',\n title: 'Person in Steamy Room',\n },\n {\n emoji: '🧖‍♂️',\n title: 'Man in Steamy Room',\n },\n {\n emoji: '🧖‍♀️',\n title: 'Woman in Steamy Room',\n },\n {\n emoji: '🧘',\n title: 'Person in Lotus Position',\n },\n {\n emoji: '🧑‍🤝‍🧑',\n title: 'People Holding Hands',\n },\n {\n emoji: '👭',\n title: 'Women Holding Hands',\n },\n {\n emoji: '👫',\n title: 'Woman and Man Holding Hands',\n },\n {\n emoji: '👬',\n title: 'Men Holding Hands',\n },\n {\n emoji: '💏',\n title: 'Kiss',\n },\n {\n emoji: '👩‍❤️‍💋‍👨',\n title: 'Kiss: Woman, Man',\n },\n {\n emoji: '👨‍❤️‍💋‍👨',\n title: 'Kiss: Man, Man',\n },\n {\n emoji: '👩‍❤️‍💋‍👩',\n title: 'Kiss: Woman, Woman',\n },\n {\n emoji: '💑',\n title: 'Couple with Heart',\n },\n {\n emoji: '👩‍❤️‍👨',\n title: 'Couple with Heart: Woman, Man',\n },\n {\n emoji: '👨‍❤️‍👨',\n title: 'Couple with Heart: Man, Man',\n },\n {\n emoji: '👩‍❤️‍👩',\n title: 'Couple with Heart: Woman, Woman',\n },\n {\n emoji: '👪',\n title: 'Family',\n },\n {\n emoji: '👨‍👩‍👦',\n title: 'Family: Man, Woman, Boy',\n },\n {\n emoji: '👨‍👩‍👧',\n title: 'Family: Man, Woman, Girl',\n },\n {\n emoji: '👨‍👩‍👧‍👦',\n title: 'Family: Man, Woman, Girl, Boy',\n },\n {\n emoji: '👨‍👩‍👦‍👦',\n title: 'Family: Man, Woman, Boy, Boy',\n },\n {\n emoji: '👨‍👩‍👧‍👧',\n title: 'Family: Man, Woman, Girl, Girl',\n },\n {\n emoji: '👨‍👨‍👦',\n title: 'Family: Man, Man, Boy',\n },\n {\n emoji: '👨‍👨‍👧',\n title: 'Family: Man, Man, Girl',\n },\n {\n emoji: '👨‍👨‍👧‍👦',\n title: 'Family: Man, Man, Girl, Boy',\n },\n {\n emoji: '👨‍👨‍👦‍👦',\n title: 'Family: Man, Man, Boy, Boy',\n },\n {\n emoji: '👨‍👨‍👧‍👧',\n title: 'Family: Man, Man, Girl, Girl',\n },\n {\n emoji: '👩‍👩‍👦',\n title: 'Family: Woman, Woman, Boy',\n },\n {\n emoji: '👩‍👩‍👧',\n title: 'Family: Woman, Woman, Girl',\n },\n {\n emoji: '👩‍👩‍👧‍👦',\n title: 'Family: Woman, Woman, Girl, Boy',\n },\n {\n emoji: '👩‍👩‍👦‍👦',\n title: 'Family: Woman, Woman, Boy, Boy',\n },\n {\n emoji: '👩‍👩‍👧‍👧',\n title: 'Family: Woman, Woman, Girl, Girl',\n },\n {\n emoji: '👨‍👦',\n title: 'Family: Man, Boy',\n },\n {\n emoji: '👨‍👦‍👦',\n title: 'Family: Man, Boy, Boy',\n },\n {\n emoji: '👨‍👧',\n title: 'Family: Man, Girl',\n },\n {\n emoji: '👨‍👧‍👦',\n title: 'Family: Man, Girl, Boy',\n },\n {\n emoji: '👨‍👧‍👧',\n title: 'Family: Man, Girl, Girl',\n },\n {\n emoji: '👩‍👦',\n title: 'Family: Woman, Boy',\n },\n {\n emoji: '👩‍👦‍👦',\n title: 'Family: Woman, Boy, Boy',\n },\n {\n emoji: '👩‍👧',\n title: 'Family: Woman, Girl',\n },\n {\n emoji: '👩‍👧‍👦',\n title: 'Family: Woman, Girl, Boy',\n },\n {\n emoji: '👩‍👧‍👧',\n title: 'Family: Woman, Girl, Girl',\n },\n {\n emoji: '🗣️',\n title: 'Speaking Head',\n },\n {\n emoji: '👤',\n title: 'Bust in Silhouette',\n },\n {\n emoji: '👥',\n title: 'Busts in Silhouette',\n },\n {\n emoji: '🫂',\n title: 'People Hugging',\n },\n {\n emoji: '👣',\n title: 'Footprints',\n },\n {\n emoji: '🧳',\n title: 'Luggage',\n },\n {\n emoji: '🌂',\n title: 'Closed Umbrella',\n },\n {\n emoji: '☂️',\n title: 'Umbrella',\n },\n {\n emoji: '🎃',\n title: 'Jack-O-Lantern',\n },\n {\n emoji: '🧵',\n title: 'Thread',\n },\n {\n emoji: '🧶',\n title: 'Yarn',\n },\n {\n emoji: '👓',\n title: 'Glasses',\n },\n {\n emoji: '🕶️',\n title: 'Sunglasses',\n },\n {\n emoji: '🥽',\n title: 'Goggles',\n },\n {\n emoji: '🥼',\n title: 'Lab Coat',\n },\n {\n emoji: '🦺',\n title: 'Safety Vest',\n },\n {\n emoji: '👔',\n title: 'Necktie',\n },\n {\n emoji: '👕',\n title: 'T-Shirt',\n },\n {\n emoji: '👖',\n title: 'Jeans',\n },\n {\n emoji: '🧣',\n title: 'Scarf',\n },\n {\n emoji: '🧤',\n title: 'Gloves',\n },\n {\n emoji: '🧥',\n title: 'Coat',\n },\n {\n emoji: '🧦',\n title: 'Socks',\n },\n {\n emoji: '👗',\n title: 'Dress',\n },\n {\n emoji: '👘',\n title: 'Kimono',\n },\n {\n emoji: '🥻',\n title: 'Sari',\n },\n {\n emoji: '🩱',\n title: 'One-Piece Swimsuit',\n },\n {\n emoji: '🩲',\n title: 'Briefs',\n },\n {\n emoji: '🩳',\n title: 'Shorts',\n },\n {\n emoji: '👙',\n title: 'Bikini',\n },\n {\n emoji: '👚',\n title: 'Woman’s Clothes',\n },\n {\n emoji: '👛',\n title: 'Purse',\n },\n {\n emoji: '👜',\n title: 'Handbag',\n },\n {\n emoji: '👝',\n title: 'Clutch Bag',\n },\n {\n emoji: '🎒',\n title: 'Backpack',\n },\n {\n emoji: '🩴',\n title: 'Thong Sandal',\n },\n {\n emoji: '👞',\n title: 'Man’s Shoe',\n },\n {\n emoji: '👟',\n title: 'Running Shoe',\n },\n {\n emoji: '🥾',\n title: 'Hiking Boot',\n },\n {\n emoji: '🥿',\n title: 'Flat Shoe',\n },\n {\n emoji: '👠',\n title: 'High-Heeled Shoe',\n },\n {\n emoji: '👡',\n title: 'Woman’s Sandal',\n },\n {\n emoji: '🩰',\n title: 'Ballet Shoes',\n },\n {\n emoji: '👢',\n title: 'Woman’s Boot',\n },\n {\n emoji: '👑',\n title: 'Crown',\n },\n {\n emoji: '👒',\n title: 'Woman’s Hat',\n },\n {\n emoji: '🎩',\n title: 'Top Hat',\n },\n {\n emoji: '🎓',\n title: 'Graduation Cap',\n },\n {\n emoji: '🧢',\n title: 'Billed Cap',\n },\n {\n emoji: '🪖',\n title: 'Military Helmet',\n },\n {\n emoji: '⛑️',\n title: 'Rescue Worker’s Helmet',\n },\n {\n emoji: '💄',\n title: 'Lipstick',\n },\n {\n emoji: '💍',\n title: 'Ring',\n },\n {\n emoji: '💼',\n title: 'Briefcase',\n },\n {\n emoji: '🩸',\n title: 'Drop of Blood',\n },\n ],\n Nature: [\n {\n emoji: '🙈',\n title: 'See-No-Evil Monkey',\n },\n {\n emoji: '🙉',\n title: 'Hear-No-Evil Monkey',\n },\n {\n emoji: '🙊',\n title: 'Speak-No-Evil Monkey',\n },\n {\n emoji: '💥',\n title: 'Collision',\n },\n {\n emoji: '💫',\n title: 'Dizzy',\n },\n {\n emoji: '💦',\n title: 'Sweat Droplets',\n },\n {\n emoji: '💨',\n title: 'Dashing Away',\n },\n {\n emoji: '🐵',\n title: 'Monkey Face',\n },\n {\n emoji: '🐒',\n title: 'Monkey',\n },\n {\n emoji: '🦍',\n title: 'Gorilla',\n },\n {\n emoji: '🦧',\n title: 'Orangutan',\n },\n {\n emoji: '🐶',\n title: 'Dog Face',\n },\n {\n emoji: '🐕',\n title: 'Dog',\n },\n {\n emoji: '🦮',\n title: 'Guide Dog',\n },\n {\n emoji: '🐕‍🦺',\n title: 'Service Dog',\n },\n {\n emoji: '🐩',\n title: 'Poodle',\n },\n {\n emoji: '🐺',\n title: 'Wolf',\n },\n {\n emoji: '🦊',\n title: 'Fox',\n },\n {\n emoji: '🦝',\n title: 'Raccoon',\n },\n {\n emoji: '🐱',\n title: 'Cat Face',\n },\n {\n emoji: '🐈',\n title: 'Cat',\n },\n {\n emoji: '🐈‍⬛',\n title: 'Black Cat',\n },\n {\n emoji: '🦁',\n title: 'Lion',\n },\n {\n emoji: '🐯',\n title: 'Tiger Face',\n },\n {\n emoji: '🐅',\n title: 'Tiger',\n },\n {\n emoji: '🐆',\n title: 'Leopard',\n },\n {\n emoji: '🐴',\n title: 'Horse Face',\n },\n {\n emoji: '🐎',\n title: 'Horse',\n },\n {\n emoji: '🦄',\n title: 'Unicorn',\n },\n {\n emoji: '🦓',\n title: 'Zebra',\n },\n {\n emoji: '🦌',\n title: 'Deer',\n },\n {\n emoji: '🦬',\n title: 'Bison',\n },\n {\n emoji: '🐮',\n title: 'Cow Face',\n },\n {\n emoji: '🐂',\n title: 'Ox',\n },\n {\n emoji: '🐃',\n title: 'Water Buffalo',\n },\n {\n emoji: '🐄',\n title: 'Cow',\n },\n {\n emoji: '🐷',\n title: 'Pig Face',\n },\n {\n emoji: '🐖',\n title: 'Pig',\n },\n {\n emoji: '🐗',\n title: 'Boar',\n },\n {\n emoji: '🐽',\n title: 'Pig Nose',\n },\n {\n emoji: '🐏',\n title: 'Ram',\n },\n {\n emoji: '🐑',\n title: 'Ewe',\n },\n {\n emoji: '🐐',\n title: 'Goat',\n },\n {\n emoji: '🐪',\n title: 'Camel',\n },\n {\n emoji: '🐫',\n title: 'Two-Hump Camel',\n },\n {\n emoji: '🦙',\n title: 'Llama',\n },\n {\n emoji: '🦒',\n title: 'Giraffe',\n },\n {\n emoji: '🐘',\n title: 'Elephant',\n },\n {\n emoji: '🦣',\n title: 'Mammoth',\n },\n {\n emoji: '🦏',\n title: 'Rhinoceros',\n },\n {\n emoji: '🦛',\n title: 'Hippopotamus',\n },\n {\n emoji: '🐭',\n title: 'Mouse Face',\n },\n {\n emoji: '🐁',\n title: 'Mouse',\n },\n {\n emoji: '🐀',\n title: 'Rat',\n },\n {\n emoji: '🐹',\n title: 'Hamster',\n },\n {\n emoji: '🐰',\n title: 'Rabbit Face',\n },\n {\n emoji: '🐇',\n title: 'Rabbit',\n },\n {\n emoji: '🐿️',\n title: 'Chipmunk',\n },\n {\n emoji: '🦫',\n title: 'Beaver',\n },\n {\n emoji: '🦔',\n title: 'Hedgehog',\n },\n {\n emoji: '🦇',\n title: 'Bat',\n },\n {\n emoji: '🐻',\n title: 'Bear',\n },\n {\n emoji: '🐻‍❄️',\n title: 'Polar Bear',\n },\n {\n emoji: '🐨',\n title: 'Koala',\n },\n {\n emoji: '🐼',\n title: 'Panda',\n },\n {\n emoji: '🦥',\n title: 'Sloth',\n },\n {\n emoji: '🦦',\n title: 'Otter',\n },\n {\n emoji: '🦨',\n title: 'Skunk',\n },\n {\n emoji: '🦘',\n title: 'Kangaroo',\n },\n {\n emoji: '🦡',\n title: 'Badger',\n },\n {\n emoji: '🐾',\n title: 'Paw Prints',\n },\n {\n emoji: '🦃',\n title: 'Turkey',\n },\n {\n emoji: '🐔',\n title: 'Chicken',\n },\n {\n emoji: '🐓',\n title: 'Rooster',\n },\n {\n emoji: '🐣',\n title: 'Hatching Chick',\n },\n {\n emoji: '🐤',\n title: 'Baby Chick',\n },\n {\n emoji: '🐥',\n title: 'Front-Facing Baby Chick',\n },\n {\n emoji: '🐦',\n title: 'Bird',\n },\n {\n emoji: '🐧',\n title: 'Penguin',\n },\n {\n emoji: '🕊️',\n title: 'Dove',\n },\n {\n emoji: '🦅',\n title: 'Eagle',\n },\n {\n emoji: '🦆',\n title: 'Duck',\n },\n {\n emoji: '🦢',\n title: 'Swan',\n },\n {\n emoji: '🦉',\n title: 'Owl',\n },\n {\n emoji: '🦤',\n title: 'Dodo',\n },\n {\n emoji: '🪶',\n title: 'Feather',\n },\n {\n emoji: '🦩',\n title: 'Flamingo',\n },\n {\n emoji: '🦚',\n title: 'Peacock',\n },\n {\n emoji: '🦜',\n title: 'Parrot',\n },\n {\n emoji: '🐸',\n title: 'Frog',\n },\n {\n emoji: '🐊',\n title: 'Crocodile',\n },\n {\n emoji: '🐢',\n title: 'Turtle',\n },\n {\n emoji: '🦎',\n title: 'Lizard',\n },\n {\n emoji: '🐍',\n title: 'Snake',\n },\n {\n emoji: '🐲',\n title: 'Dragon Face',\n },\n {\n emoji: '🐉',\n title: 'Dragon',\n },\n {\n emoji: '🦕',\n title: 'Sauropod',\n },\n {\n emoji: '🦖',\n title: 'T-Rex',\n },\n {\n emoji: '🐳',\n title: 'Spouting Whale',\n },\n {\n emoji: '🐋',\n title: 'Whale',\n },\n {\n emoji: '🐬',\n title: 'Dolphin',\n },\n {\n emoji: '🦭',\n title: 'Seal',\n },\n {\n emoji: '🐟',\n title: 'Fish',\n },\n {\n emoji: '🐠',\n title: 'Tropical Fish',\n },\n {\n emoji: '🐡',\n title: 'Blowfish',\n },\n {\n emoji: '🦈',\n title: 'Shark',\n },\n {\n emoji: '🐙',\n title: 'Octopus',\n },\n {\n emoji: '🐚',\n title: 'Spiral Shell',\n },\n {\n emoji: '🐌',\n title: 'Snail',\n },\n {\n emoji: '🦋',\n title: 'Butterfly',\n },\n {\n emoji: '🐛',\n title: 'Bug',\n },\n {\n emoji: '🐜',\n title: 'Ant',\n },\n {\n emoji: '🐝',\n title: 'Honeybee',\n },\n {\n emoji: '🪲',\n title: 'Beetle',\n },\n {\n emoji: '🐞',\n title: 'Lady Beetle',\n },\n {\n emoji: '🦗',\n title: 'Cricket',\n },\n {\n emoji: '🪳',\n title: 'Cockroach',\n },\n {\n emoji: '🕷️',\n title: 'Spider',\n },\n {\n emoji: '🕸️',\n title: 'Spider Web',\n },\n {\n emoji: '🦂',\n title: 'Scorpion',\n },\n {\n emoji: '🦟',\n title: 'Mosquito',\n },\n {\n emoji: '🪰',\n title: 'Fly',\n },\n {\n emoji: '🪱',\n title: 'Worm',\n },\n {\n emoji: '🦠',\n title: 'Microbe',\n },\n {\n emoji: '💐',\n title: 'Bouquet',\n },\n {\n emoji: '🌸',\n title: 'Cherry Blossom',\n },\n {\n emoji: '💮',\n title: 'White Flower',\n },\n {\n emoji: '🏵️',\n title: 'Rosette',\n },\n {\n emoji: '🌹',\n title: 'Rose',\n },\n {\n emoji: '🥀',\n title: 'Wilted Flower',\n },\n {\n emoji: '🌺',\n title: 'Hibiscus',\n },\n {\n emoji: '🌻',\n title: 'Sunflower',\n },\n {\n emoji: '🌼',\n title: 'Blossom',\n },\n {\n emoji: '🌷',\n title: 'Tulip',\n },\n {\n emoji: '🌱',\n title: 'Seedling',\n },\n {\n emoji: '🪴',\n title: 'Potted Plant',\n },\n {\n emoji: '🌲',\n title: 'Evergreen Tree',\n },\n {\n emoji: '🌳',\n title: 'Deciduous Tree',\n },\n {\n emoji: '🌴',\n title: 'Palm Tree',\n },\n {\n emoji: '🌵',\n title: 'Cactus',\n },\n {\n emoji: '🌾',\n title: 'Sheaf of Rice',\n },\n {\n emoji: '🌿',\n title: 'Herb',\n },\n {\n emoji: '☘️',\n title: 'Shamrock',\n },\n {\n emoji: '🍀',\n title: 'Four Leaf Clover',\n },\n {\n emoji: '🍁',\n title: 'Maple Leaf',\n },\n {\n emoji: '🍂',\n title: 'Fallen Leaf',\n },\n {\n emoji: '🍃',\n title: 'Leaf Fluttering in Wind',\n },\n {\n emoji: '🍄',\n title: 'Mushroom',\n },\n {\n emoji: '🌰',\n title: 'Chestnut',\n },\n {\n emoji: '🦀',\n title: 'Crab',\n },\n {\n emoji: '🦞',\n title: 'Lobster',\n },\n {\n emoji: '🦐',\n title: 'Shrimp',\n },\n {\n emoji: '🦑',\n title: 'Squid',\n },\n {\n emoji: '🌍',\n title: 'Globe Showing Europe-Africa',\n },\n {\n emoji: '🌎',\n title: 'Globe Showing Americas',\n },\n {\n emoji: '🌏',\n title: 'Globe Showing Asia-Australia',\n },\n {\n emoji: '🌐',\n title: 'Globe with Meridians',\n },\n {\n emoji: '🪨',\n title: 'Rock',\n },\n {\n emoji: '🌑',\n title: 'New Moon',\n },\n {\n emoji: '🌒',\n title: 'Waxing Crescent Moon',\n },\n {\n emoji: '🌓',\n title: 'First Quarter Moon',\n },\n {\n emoji: '🌔',\n title: 'Waxing Gibbous Moon',\n },\n {\n emoji: '🌕',\n title: 'Full Moon',\n },\n {\n emoji: '🌖',\n title: 'Waning Gibbous Moon',\n },\n {\n emoji: '🌗',\n title: 'Last Quarter Moon',\n },\n {\n emoji: '🌘',\n title: 'Waning Crescent Moon',\n },\n {\n emoji: '🌙',\n title: 'Crescent Moon',\n },\n {\n emoji: '🌚',\n title: 'New Moon Face',\n },\n {\n emoji: '🌛',\n title: 'First Quarter Moon Face',\n },\n {\n emoji: '🌜',\n title: 'Last Quarter Moon Face',\n },\n {\n emoji: '☀️',\n title: 'Sun',\n },\n {\n emoji: '🌝',\n title: 'Full Moon Face',\n },\n {\n emoji: '🌞',\n title: 'Sun with Face',\n },\n {\n emoji: '⭐',\n title: 'Star',\n },\n {\n emoji: '🌟',\n title: 'Glowing Star',\n },\n {\n emoji: '🌠',\n title: 'Shooting Star',\n },\n {\n emoji: '☁️',\n title: 'Cloud',\n },\n {\n emoji: '⛅',\n title: 'Sun Behind Cloud',\n },\n {\n emoji: '⛈️',\n title: 'Cloud with Lightning and Rain',\n },\n {\n emoji: '🌤️',\n title: 'Sun Behind Small Cloud',\n },\n {\n emoji: '🌥️',\n title: 'Sun Behind Large Cloud',\n },\n {\n emoji: '🌦️',\n title: 'Sun Behind Rain Cloud',\n },\n {\n emoji: '🌧️',\n title: 'Cloud with Rain',\n },\n {\n emoji: '🌨️',\n title: 'Cloud with Snow',\n },\n {\n emoji: '🌩️',\n title: 'Cloud with Lightning',\n },\n {\n emoji: '🌪️',\n title: 'Tornado',\n },\n {\n emoji: '🌫️',\n title: 'Fog',\n },\n {\n emoji: '🌬️',\n title: 'Wind Face',\n },\n {\n emoji: '🌈',\n title: 'Rainbow',\n },\n {\n emoji: '☂️',\n title: 'Umbrella',\n },\n {\n emoji: '☔',\n title: 'Umbrella with Rain Drops',\n },\n {\n emoji: '⚡',\n title: 'High Voltage',\n },\n {\n emoji: '❄️',\n title: 'Snowflake',\n },\n {\n emoji: '☃️',\n title: 'Snowman',\n },\n {\n emoji: '⛄',\n title: 'Snowman Without Snow',\n },\n {\n emoji: '☄️',\n title: 'Comet',\n },\n {\n emoji: '🔥',\n title: 'Fire',\n },\n {\n emoji: '💧',\n title: 'Droplet',\n },\n {\n emoji: '🌊',\n title: 'Water Wave',\n },\n {\n emoji: '🎄',\n title: 'Christmas Tree',\n },\n {\n emoji: '✨',\n title: 'Sparkles',\n },\n {\n emoji: '🎋',\n title: 'Tanabata Tree',\n },\n {\n emoji: '🎍',\n title: 'Pine Decoration',\n },\n ],\n 'Food-drink': [\n {\n emoji: '🍇',\n title: 'Grapes',\n },\n {\n emoji: '🍈',\n title: 'Melon',\n },\n {\n emoji: '🍉',\n title: 'Watermelon',\n },\n {\n emoji: '🍊',\n title: 'Tangerine',\n },\n {\n emoji: '🍋',\n title: 'Lemon',\n },\n {\n emoji: '🍌',\n title: 'Banana',\n },\n {\n emoji: '🍍',\n title: 'Pineapple',\n },\n {\n emoji: '🥭',\n title: 'Mango',\n },\n {\n emoji: '🍎',\n title: 'Red Apple',\n },\n {\n emoji: '🍏',\n title: 'Green Apple',\n },\n {\n emoji: '🍐',\n title: 'Pear',\n },\n {\n emoji: '🍑',\n title: 'Peach',\n },\n {\n emoji: '🍒',\n title: 'Cherries',\n },\n {\n emoji: '🍓',\n title: 'Strawberry',\n },\n {\n emoji: '🫐',\n title: 'Blueberries',\n },\n {\n emoji: '🥝',\n title: 'Kiwi Fruit',\n },\n {\n emoji: '🍅',\n title: 'Tomato',\n },\n {\n emoji: '🫒',\n title: 'Olive',\n },\n {\n emoji: '🥥',\n title: 'Coconut',\n },\n {\n emoji: '🥑',\n title: 'Avocado',\n },\n {\n emoji: '🍆',\n title: 'Eggplant',\n },\n {\n emoji: '🥔',\n title: 'Potato',\n },\n {\n emoji: '🥕',\n title: 'Carrot',\n },\n {\n emoji: '🌽',\n title: 'Ear of Corn',\n },\n {\n emoji: '🌶️',\n title: 'Hot Pepper',\n },\n {\n emoji: '🫑',\n title: 'Bell Pepper',\n },\n {\n emoji: '🥒',\n title: 'Cucumber',\n },\n {\n emoji: '🥬',\n title: 'Leafy Green',\n },\n {\n emoji: '🥦',\n title: 'Broccoli',\n },\n {\n emoji: '🧄',\n title: 'Garlic',\n },\n {\n emoji: '🧅',\n title: 'Onion',\n },\n {\n emoji: '🍄',\n title: 'Mushroom',\n },\n {\n emoji: '🥜',\n title: 'Peanuts',\n },\n {\n emoji: '🌰',\n title: 'Chestnut',\n },\n {\n emoji: '🍞',\n title: 'Bread',\n },\n {\n emoji: '🥐',\n title: 'Croissant',\n },\n {\n emoji: '🥖',\n title: 'Baguette Bread',\n },\n {\n emoji: '🫓',\n title: 'Flatbread',\n },\n {\n emoji: '🥨',\n title: 'Pretzel',\n },\n {\n emoji: '🥯',\n title: 'Bagel',\n },\n {\n emoji: '🥞',\n title: 'Pancakes',\n },\n {\n emoji: '🧇',\n title: 'Waffle',\n },\n {\n emoji: '🧀',\n title: 'Cheese Wedge',\n },\n {\n emoji: '🍖',\n title: 'Meat on Bone',\n },\n {\n emoji: '🍗',\n title: 'Poultry Leg',\n },\n {\n emoji: '🥩',\n title: 'Cut of Meat',\n },\n {\n emoji: '🥓',\n title: 'Bacon',\n },\n {\n emoji: '🍔',\n title: 'Hamburger',\n },\n {\n emoji: '🍟',\n title: 'French Fries',\n },\n {\n emoji: '🍕',\n title: 'Pizza',\n },\n {\n emoji: '🌭',\n title: 'Hot Dog',\n },\n {\n emoji: '🥪',\n title: 'Sandwich',\n },\n {\n emoji: '🌮',\n title: 'Taco',\n },\n {\n emoji: '🌯',\n title: 'Burrito',\n },\n {\n emoji: '🫔',\n title: 'Tamale',\n },\n {\n emoji: '🥙',\n title: 'Stuffed Flatbread',\n },\n {\n emoji: '🧆',\n title: 'Falafel',\n },\n {\n emoji: '🥚',\n title: 'Egg',\n },\n {\n emoji: '🍳',\n title: 'Cooking',\n },\n {\n emoji: '🥘',\n title: 'Shallow Pan of Food',\n },\n {\n emoji: '🍲',\n title: 'Pot of Food',\n },\n {\n emoji: '🫕',\n title: 'Fondue',\n },\n {\n emoji: '🥣',\n title: 'Bowl with Spoon',\n },\n {\n emoji: '🥗',\n title: 'Green Salad',\n },\n {\n emoji: '🍿',\n title: 'Popcorn',\n },\n {\n emoji: '🧈',\n title: 'Butter',\n },\n {\n emoji: '🧂',\n title: 'Salt',\n },\n {\n emoji: '🥫',\n title: 'Canned Food',\n },\n {\n emoji: '🍱',\n title: 'Bento Box',\n },\n {\n emoji: '🍘',\n title: 'Rice Cracker',\n },\n {\n emoji: '🍙',\n title: 'Rice Ball',\n },\n {\n emoji: '🍚',\n title: 'Cooked Rice',\n },\n {\n emoji: '🍛',\n title: 'Curry Rice',\n },\n {\n emoji: '🍜',\n title: 'Steaming Bowl',\n },\n {\n emoji: '🍝',\n title: 'Spaghetti',\n },\n {\n emoji: '🍠',\n title: 'Roasted Sweet Potato',\n },\n {\n emoji: '🍢',\n title: 'Oden',\n },\n {\n emoji: '🍣',\n title: 'Sushi',\n },\n {\n emoji: '🍤',\n title: 'Fried Shrimp',\n },\n {\n emoji: '🍥',\n title: 'Fish Cake with Swirl',\n },\n {\n emoji: '🥮',\n title: 'Moon Cake',\n },\n {\n emoji: '🍡',\n title: 'Dango',\n },\n {\n emoji: '🥟',\n title: 'Dumpling',\n },\n {\n emoji: '🥠',\n title: 'Fortune Cookie',\n },\n {\n emoji: '🥡',\n title: 'Takeout Box',\n },\n {\n emoji: '🦪',\n title: 'Oyster',\n },\n {\n emoji: '🍦',\n title: 'Soft Ice Cream',\n },\n {\n emoji: '🍧',\n title: 'Shaved Ice',\n },\n {\n emoji: '🍨',\n title: 'Ice Cream',\n },\n {\n emoji: '🍩',\n title: 'Doughnut',\n },\n {\n emoji: '🍪',\n title: 'Cookie',\n },\n {\n emoji: '🎂',\n title: 'Birthday Cake',\n },\n {\n emoji: '🍰',\n title: 'Shortcake',\n },\n {\n emoji: '🧁',\n title: 'Cupcake',\n },\n {\n emoji: '🥧',\n title: 'Pie',\n },\n {\n emoji: '🍫',\n title: 'Chocolate Bar',\n },\n {\n emoji: '🍬',\n title: 'Candy',\n },\n {\n emoji: '🍭',\n title: 'Lollipop',\n },\n {\n emoji: '🍮',\n title: 'Custard',\n },\n {\n emoji: '🍯',\n title: 'Honey Pot',\n },\n {\n emoji: '🍼',\n title: 'Baby Bottle',\n },\n {\n emoji: '🥛',\n title: 'Glass of Milk',\n },\n {\n emoji: '☕',\n title: 'Hot Beverage',\n },\n {\n emoji: '🫖',\n title: 'Teapot',\n },\n {\n emoji: '🍵',\n title: 'Teacup Without Handle',\n },\n {\n emoji: '🍶',\n title: 'Sake',\n },\n {\n emoji: '🍾',\n title: 'Bottle with Popping Cork',\n },\n {\n emoji: '🍷',\n title: 'Wine Glass',\n },\n {\n emoji: '🍸',\n title: 'Cocktail Glass',\n },\n {\n emoji: '🍹',\n title: 'Tropical Drink',\n },\n {\n emoji: '🍺',\n title: 'Beer Mug',\n },\n {\n emoji: '🍻',\n title: 'Clinking Beer Mugs',\n },\n {\n emoji: '🥂',\n title: 'Clinking Glasses',\n },\n {\n emoji: '🥃',\n title: 'Tumbler Glass',\n },\n {\n emoji: '🥤',\n title: 'Cup with Straw',\n },\n {\n emoji: '🧋',\n title: 'Bubble Tea',\n },\n {\n emoji: '🧃',\n title: 'Beverage Box',\n },\n {\n emoji: '🧉',\n title: 'Mate',\n },\n {\n emoji: '🧊',\n title: 'Ice',\n },\n {\n emoji: '🥢',\n title: 'Chopsticks',\n },\n {\n emoji: '🍽️',\n title: 'Fork and Knife with Plate',\n },\n {\n emoji: '🍴',\n title: 'Fork and Knife',\n },\n {\n emoji: '🥄',\n title: 'Spoon',\n },\n ],\n Activity: [\n {\n emoji: '🕴️',\n title: 'Person in Suit Levitating',\n },\n {\n emoji: '🧗',\n title: 'Person Climbing',\n },\n {\n emoji: '🧗‍♂️',\n title: 'Man Climbing',\n },\n {\n emoji: '🧗‍♀️',\n title: 'Woman Climbing',\n },\n {\n emoji: '🤺',\n title: 'Person Fencing',\n },\n {\n emoji: '🏇',\n title: 'Horse Racing',\n },\n {\n emoji: '⛷️',\n title: 'Skier',\n },\n {\n emoji: '🏂',\n title: 'Snowboarder',\n },\n {\n emoji: '🏌️',\n title: 'Person Golfing',\n },\n {\n emoji: '🏌️‍♂️',\n title: 'Man Golfing',\n },\n {\n emoji: '🏌️‍♀️',\n title: 'Woman Golfing',\n },\n {\n emoji: '🏄',\n title: 'Person Surfing',\n },\n {\n emoji: '🏄‍♂️',\n title: 'Man Surfing',\n },\n {\n emoji: '🏄‍♀️',\n title: 'Woman Surfing',\n },\n {\n emoji: '🚣',\n title: 'Person Rowing Boat',\n },\n {\n emoji: '🚣‍♂️',\n title: 'Man Rowing Boat',\n },\n {\n emoji: '🚣‍♀️',\n title: 'Woman Rowing Boat',\n },\n {\n emoji: '🏊',\n title: 'Person Swimming',\n },\n {\n emoji: '🏊‍♂️',\n title: 'Man Swimming',\n },\n {\n emoji: '🏊‍♀️',\n title: 'Woman Swimming',\n },\n {\n emoji: '⛹️',\n title: 'Person Bouncing Ball',\n },\n {\n emoji: '⛹️‍♂️',\n title: 'Man Bouncing Ball',\n },\n {\n emoji: '⛹️‍♀️',\n title: 'Woman Bouncing Ball',\n },\n {\n emoji: '🏋️',\n title: 'Person Lifting Weights',\n },\n {\n emoji: '🏋️‍♂️',\n title: 'Man Lifting Weights',\n },\n {\n emoji: '🏋️‍♀️',\n title: 'Woman Lifting Weights',\n },\n {\n emoji: '🚴',\n title: 'Person Biking',\n },\n {\n emoji: '🚴‍♂️',\n title: 'Man Biking',\n },\n {\n emoji: '🚴‍♀️',\n title: 'Woman Biking',\n },\n {\n emoji: '🚵',\n title: 'Person Mountain Biking',\n },\n {\n emoji: '🚵‍♂️',\n title: 'Man Mountain Biking',\n },\n {\n emoji: '🚵‍♀️',\n title: 'Woman Mountain Biking',\n },\n {\n emoji: '🤸',\n title: 'Person Cartwheeling',\n },\n {\n emoji: '🤸‍♂️',\n title: 'Man Cartwheeling',\n },\n {\n emoji: '🤸‍♀️',\n title: 'Woman Cartwheeling',\n },\n {\n emoji: '🤼',\n title: 'People Wrestling',\n },\n {\n emoji: '🤼‍♂️',\n title: 'Men Wrestling',\n },\n {\n emoji: '🤼‍♀️',\n title: 'Women Wrestling',\n },\n {\n emoji: '🤽',\n title: 'Person Playing Water Polo',\n },\n {\n emoji: '🤽‍♂️',\n title: 'Man Playing Water Polo',\n },\n {\n emoji: '🤽‍♀️',\n title: 'Woman Playing Water Polo',\n },\n {\n emoji: '🤾',\n title: 'Person Playing Handball',\n },\n {\n emoji: '🤾‍♂️',\n title: 'Man Playing Handball',\n },\n {\n emoji: '🤾‍♀️',\n title: 'Woman Playing Handball',\n },\n {\n emoji: '🤹',\n title: 'Person Juggling',\n },\n {\n emoji: '🤹‍♂️',\n title: 'Man Juggling',\n },\n {\n emoji: '🤹‍♀️',\n title: 'Woman Juggling',\n },\n {\n emoji: '🧘',\n title: 'Person in Lotus Position',\n },\n {\n emoji: '🧘‍♂️',\n title: 'Man in Lotus Position',\n },\n {\n emoji: '🧘‍♀️',\n title: 'Woman in Lotus Position',\n },\n {\n emoji: '🎪',\n title: 'Circus Tent',\n },\n {\n emoji: '🛹',\n title: 'Skateboard',\n },\n {\n emoji: '🛼',\n title: 'Roller Skate',\n },\n {\n emoji: '🛶',\n title: 'Canoe',\n },\n {\n emoji: '🎗️',\n title: 'Reminder Ribbon',\n },\n {\n emoji: '🎟️',\n title: 'Admission Tickets',\n },\n {\n emoji: '🎫',\n title: 'Ticket',\n },\n {\n emoji: '🎖️',\n title: 'Military Medal',\n },\n {\n emoji: '🏆',\n title: 'Trophy',\n },\n {\n emoji: '🏅',\n title: 'Sports Medal',\n },\n {\n emoji: '🥇',\n title: '1st Place Medal',\n },\n {\n emoji: '🥈',\n title: '2nd Place Medal',\n },\n {\n emoji: '🥉',\n title: '3rd Place Medal',\n },\n {\n emoji: '⚽',\n title: 'Soccer Ball',\n },\n {\n emoji: '⚾',\n title: 'Baseball',\n },\n {\n emoji: '🥎',\n title: 'Softball',\n },\n {\n emoji: '🏀',\n title: 'Basketball',\n },\n {\n emoji: '🏐',\n title: 'Volleyball',\n },\n {\n emoji: '🏈',\n title: 'American Football',\n },\n {\n emoji: '🏉',\n title: 'Rugby Football',\n },\n {\n emoji: '🎾',\n title: 'Tennis',\n },\n {\n emoji: '🥏',\n title: 'Flying Disc',\n },\n {\n emoji: '🎳',\n title: 'Bowling',\n },\n {\n emoji: '🏏',\n title: 'Cricket Game',\n },\n {\n emoji: '🏑',\n title: 'Field Hockey',\n },\n {\n emoji: '🏒',\n title: 'Ice Hockey',\n },\n {\n emoji: '🥍',\n title: 'Lacrosse',\n },\n {\n emoji: '🏓',\n title: 'Ping Pong',\n },\n {\n emoji: '🏸',\n title: 'Badminton',\n },\n {\n emoji: '🥊',\n title: 'Boxing Glove',\n },\n {\n emoji: '🥋',\n title: 'Martial Arts Uniform',\n },\n {\n emoji: '🥅',\n title: 'Goal Net',\n },\n {\n emoji: '⛳',\n title: 'Flag in Hole',\n },\n {\n emoji: '⛸️',\n title: 'Ice Skate',\n },\n {\n emoji: '🎣',\n title: 'Fishing Pole',\n },\n {\n emoji: '🎽',\n title: 'Running Shirt',\n },\n {\n emoji: '🎿',\n title: 'Skis',\n },\n {\n emoji: '🛷',\n title: 'Sled',\n },\n {\n emoji: '🥌',\n title: 'Curling Stone',\n },\n {\n emoji: '🎯',\n title: 'Bullseye',\n },\n {\n emoji: '🎱',\n title: 'Pool 8 Ball',\n },\n {\n emoji: '🎮',\n title: 'Video Game',\n },\n {\n emoji: '🎰',\n title: 'Slot Machine',\n },\n {\n emoji: '🎲',\n title: 'Game Die',\n },\n {\n emoji: '🧩',\n title: 'Puzzle Piece',\n },\n {\n emoji: '♟️',\n title: 'Chess Pawn',\n },\n {\n emoji: '🎭',\n title: 'Performing Arts',\n },\n {\n emoji: '🎨',\n title: 'Artist Palette',\n },\n {\n emoji: '🧵',\n title: 'Thread',\n },\n {\n emoji: '🧶',\n title: 'Yarn',\n },\n {\n emoji: '🎼',\n title: 'Musical Score',\n },\n {\n emoji: '🎤',\n title: 'Microphone',\n },\n {\n emoji: '🎧',\n title: 'Headphone',\n },\n {\n emoji: '🎷',\n title: 'Saxophone',\n },\n {\n emoji: '🪗',\n title: 'Accordion',\n },\n {\n emoji: '🎸',\n title: 'Guitar',\n },\n {\n emoji: '🎹',\n title: 'Musical Keyboard',\n },\n {\n emoji: '🎺',\n title: 'Trumpet',\n },\n {\n emoji: '🎻',\n title: 'Violin',\n },\n {\n emoji: '🥁',\n title: 'Drum',\n },\n {\n emoji: '🪘',\n title: 'Long Drum',\n },\n {\n emoji: '🎬',\n title: 'Clapper Board',\n },\n {\n emoji: '🏹',\n title: 'Bow and Arrow',\n },\n ],\n 'Travel-places': [\n {\n emoji: '🚣',\n title: 'Person Rowing Boat',\n },\n {\n emoji: '🗾',\n title: 'Map of Japan',\n },\n {\n emoji: '🏔️',\n title: 'Snow-Capped Mountain',\n },\n {\n emoji: '⛰️',\n title: 'Mountain',\n },\n {\n emoji: '🌋',\n title: 'Volcano',\n },\n {\n emoji: '🗻',\n title: 'Mount Fuji',\n },\n {\n emoji: '🏕️',\n title: 'Camping',\n },\n {\n emoji: '🏖️',\n title: 'Beach with Umbrella',\n },\n {\n emoji: '🏜️',\n title: 'Desert',\n },\n {\n emoji: '🏝️',\n title: 'Desert Island',\n },\n {\n emoji: '🏞️',\n title: 'National Park',\n },\n {\n emoji: '🏟️',\n title: 'Stadium',\n },\n {\n emoji: '🏛️',\n title: 'Classical Building',\n },\n {\n emoji: '🏗️',\n title: 'Building Construction',\n },\n {\n emoji: '🛖',\n title: 'Hut',\n },\n {\n emoji: '🏘️',\n title: 'Houses',\n },\n {\n emoji: '🏚️',\n title: 'Derelict House',\n },\n {\n emoji: '🏠',\n title: 'House',\n },\n {\n emoji: '🏡',\n title: 'House with Garden',\n },\n {\n emoji: '🏢',\n title: 'Office Building',\n },\n {\n emoji: '🏣',\n title: 'Japanese Post Office',\n },\n {\n emoji: '🏤',\n title: 'Post Office',\n },\n {\n emoji: '🏥',\n title: 'Hospital',\n },\n {\n emoji: '🏦',\n title: 'Bank',\n },\n {\n emoji: '🏨',\n title: 'Hotel',\n },\n {\n emoji: '🏩',\n title: 'Love Hotel',\n },\n {\n emoji: '🏪',\n title: 'Convenience Store',\n },\n {\n emoji: '🏫',\n title: 'School',\n },\n {\n emoji: '🏬',\n title: 'Department Store',\n },\n {\n emoji: '🏭',\n title: 'Factory',\n },\n {\n emoji: '🏯',\n title: 'Japanese Castle',\n },\n {\n emoji: '🏰',\n title: 'Castle',\n },\n {\n emoji: '💒',\n title: 'Wedding',\n },\n {\n emoji: '🗼',\n title: 'Tokyo Tower',\n },\n {\n emoji: '🗽',\n title: 'Statue of Liberty',\n },\n {\n emoji: '⛪',\n title: 'Church',\n },\n {\n emoji: '🕌',\n title: 'Mosque',\n },\n {\n emoji: '🛕',\n title: 'Hindu Temple',\n },\n {\n emoji: '🕍',\n title: 'Synagogue',\n },\n {\n emoji: '⛩️',\n title: 'Shinto Shrine',\n },\n {\n emoji: '🕋',\n title: 'Kaaba',\n },\n {\n emoji: '⛲',\n title: 'Fountain',\n },\n {\n emoji: '⛺',\n title: 'Tent',\n },\n {\n emoji: '🌁',\n title: 'Foggy',\n },\n {\n emoji: '🌃',\n title: 'Night with Stars',\n },\n {\n emoji: '🏙️',\n title: 'Cityscape',\n },\n {\n emoji: '🌄',\n title: 'Sunrise Over Mountains',\n },\n {\n emoji: '🌅',\n title: 'Sunrise',\n },\n {\n emoji: '🌆',\n title: 'Cityscape at Dusk',\n },\n {\n emoji: '🌇',\n title: 'Sunset',\n },\n {\n emoji: '🌉',\n title: 'Bridge at Night',\n },\n {\n emoji: '🎠',\n title: 'Carousel Horse',\n },\n {\n emoji: '🎡',\n title: 'Ferris Wheel',\n },\n {\n emoji: '🎢',\n title: 'Roller Coaster',\n },\n {\n emoji: '🚂',\n title: 'Locomotive',\n },\n {\n emoji: '🚃',\n title: 'Railway Car',\n },\n {\n emoji: '🚄',\n title: 'High-Speed Train',\n },\n {\n emoji: '🚅',\n title: 'Bullet Train',\n },\n {\n emoji: '🚆',\n title: 'Train',\n },\n {\n emoji: '🚇',\n title: 'Metro',\n },\n {\n emoji: '🚈',\n title: 'Light Rail',\n },\n {\n emoji: '🚉',\n title: 'Station',\n },\n {\n emoji: '🚊',\n title: 'Tram',\n },\n {\n emoji: '🚝',\n title: 'Monorail',\n },\n {\n emoji: '🚞',\n title: 'Mountain Railway',\n },\n {\n emoji: '🚋',\n title: 'Tram Car',\n },\n {\n emoji: '🚌',\n title: 'Bus',\n },\n {\n emoji: '🚍',\n title: 'Oncoming Bus',\n },\n {\n emoji: '🚎',\n title: 'Trolleybus',\n },\n {\n emoji: '🚐',\n title: 'Minibus',\n },\n {\n emoji: '🚑',\n title: 'Ambulance',\n },\n {\n emoji: '🚒',\n title: 'Fire Engine',\n },\n {\n emoji: '🚓',\n title: 'Police Car',\n },\n {\n emoji: '🚔',\n title: 'Oncoming Police Car',\n },\n {\n emoji: '🚕',\n title: 'Taxi',\n },\n {\n emoji: '🚖',\n title: 'Oncoming Taxi',\n },\n {\n emoji: '🚗',\n title: 'Automobile',\n },\n {\n emoji: '🚘',\n title: 'Oncoming Automobile',\n },\n {\n emoji: '🚙',\n title: 'Sport Utility Vehicle',\n },\n {\n emoji: '🛻',\n title: 'Pickup Truck',\n },\n {\n emoji: '🚚',\n title: 'Delivery Truck',\n },\n {\n emoji: '🚛',\n title: 'Articulated Lorry',\n },\n {\n emoji: '🚜',\n title: 'Tractor',\n },\n {\n emoji: '🏎️',\n title: 'Racing Car',\n },\n {\n emoji: '🏍️',\n title: 'Motorcycle',\n },\n {\n emoji: '🛵',\n title: 'Motor Scooter',\n },\n {\n emoji: '🛺',\n title: 'Auto Rickshaw',\n },\n {\n emoji: '🚲',\n title: 'Bicycle',\n },\n {\n emoji: '🛴',\n title: 'Kick Scooter',\n },\n {\n emoji: '🚏',\n title: 'Bus Stop',\n },\n {\n emoji: '🛣️',\n title: 'Motorway',\n },\n {\n emoji: '🛤️',\n title: 'Railway Track',\n },\n {\n emoji: '⛽',\n title: 'Fuel Pump',\n },\n {\n emoji: '🚨',\n title: 'Police Car Light',\n },\n {\n emoji: '🚥',\n title: 'Horizontal Traffic Light',\n },\n {\n emoji: '🚦',\n title: 'Vertical Traffic Light',\n },\n {\n emoji: '🚧',\n title: 'Construction',\n },\n {\n emoji: '⚓',\n title: 'Anchor',\n },\n {\n emoji: '⛵',\n title: 'Sailboat',\n },\n {\n emoji: '🚤',\n title: 'Speedboat',\n },\n {\n emoji: '🛳️',\n title: 'Passenger Ship',\n },\n {\n emoji: '⛴️',\n title: 'Ferry',\n },\n {\n emoji: '🛥️',\n title: 'Motor Boat',\n },\n {\n emoji: '🚢',\n title: 'Ship',\n },\n {\n emoji: '✈️',\n title: 'Airplane',\n },\n {\n emoji: '🛩️',\n title: 'Small Airplane',\n },\n {\n emoji: '🛫',\n title: 'Airplane Departure',\n },\n {\n emoji: '🛬',\n title: 'Airplane Arrival',\n },\n {\n emoji: '🪂',\n title: 'Parachute',\n },\n {\n emoji: '💺',\n title: 'Seat',\n },\n {\n emoji: '🚁',\n title: 'Helicopter',\n },\n {\n emoji: '🚟',\n title: 'Suspension Railway',\n },\n {\n emoji: '🚠',\n title: 'Mountain Cableway',\n },\n {\n emoji: '🚡',\n title: 'Aerial Tramway',\n },\n {\n emoji: '🛰️',\n title: 'Satellite',\n },\n {\n emoji: '🚀',\n title: 'Rocket',\n },\n {\n emoji: '🛸',\n title: 'Flying Saucer',\n },\n {\n emoji: '🪐',\n title: 'Ringed Planet',\n },\n {\n emoji: '🌠',\n title: 'Shooting Star',\n },\n {\n emoji: '🌌',\n title: 'Milky Way',\n },\n {\n emoji: '⛱️',\n title: 'Umbrella on Ground',\n },\n {\n emoji: '🎆',\n title: 'Fireworks',\n },\n {\n emoji: '🎇',\n title: 'Sparkler',\n },\n {\n emoji: '🎑',\n title: 'Moon Viewing Ceremony',\n },\n {\n emoji: '💴',\n title: 'Yen Banknote',\n },\n {\n emoji: '💵',\n title: 'Dollar Banknote',\n },\n {\n emoji: '💶',\n title: 'Euro Banknote',\n },\n {\n emoji: '💷',\n title: 'Pound Banknote',\n },\n {\n emoji: '🗿',\n title: 'Moai',\n },\n {\n emoji: '🛂',\n title: 'Passport Control',\n },\n {\n emoji: '🛃',\n title: 'Customs',\n },\n {\n emoji: '🛄',\n title: 'Baggage Claim',\n },\n {\n emoji: '🛅',\n title: 'Left Luggage',\n },\n ],\n Objects: [\n {\n emoji: '💌',\n title: 'Love Letter',\n },\n {\n emoji: '🕳️',\n title: 'Hole',\n },\n {\n emoji: '💣',\n title: 'Bomb',\n },\n {\n emoji: '🛀',\n title: 'Person Taking Bath',\n },\n {\n emoji: '🛌',\n title: 'Person in Bed',\n },\n {\n emoji: '🔪',\n title: 'Kitchen Knife',\n },\n {\n emoji: '🏺',\n title: 'Amphora',\n },\n {\n emoji: '🗺️',\n title: 'World Map',\n },\n {\n emoji: '🧭',\n title: 'Compass',\n },\n {\n emoji: '🧱',\n title: 'Brick',\n },\n {\n emoji: '💈',\n title: 'Barber Pole',\n },\n {\n emoji: '🦽',\n title: 'Manual Wheelchair',\n },\n {\n emoji: '🦼',\n title: 'Motorized Wheelchair',\n },\n {\n emoji: '🛢️',\n title: 'Oil Drum',\n },\n {\n emoji: '🛎️',\n title: 'Bellhop Bell',\n },\n {\n emoji: '🧳',\n title: 'Luggage',\n },\n {\n emoji: '⌛',\n title: 'Hourglass Done',\n },\n {\n emoji: '⏳',\n title: 'Hourglass Not Done',\n },\n {\n emoji: '⌚',\n title: 'Watch',\n },\n {\n emoji: '⏰',\n title: 'Alarm Clock',\n },\n {\n emoji: '⏱️',\n title: 'Stopwatch',\n },\n {\n emoji: '⏲️',\n title: 'Timer Clock',\n },\n {\n emoji: '🕰️',\n title: 'Mantelpiece Clock',\n },\n {\n emoji: '🌡️',\n title: 'Thermometer',\n },\n {\n emoji: '⛱️',\n title: 'Umbrella on Ground',\n },\n {\n emoji: '🧨',\n title: 'Firecracker',\n },\n {\n emoji: '🎈',\n title: 'Balloon',\n },\n {\n emoji: '🎉',\n title: 'Party Popper',\n },\n {\n emoji: '🎊',\n title: 'Confetti Ball',\n },\n {\n emoji: '🎎',\n title: 'Japanese Dolls',\n },\n {\n emoji: '🎏',\n title: 'Carp Streamer',\n },\n {\n emoji: '🎐',\n title: 'Wind Chime',\n },\n {\n emoji: '🧧',\n title: 'Red Envelope',\n },\n {\n emoji: '🎀',\n title: 'Ribbon',\n },\n {\n emoji: '🎁',\n title: 'Wrapped Gift',\n },\n {\n emoji: '🤿',\n title: 'Diving Mask',\n },\n {\n emoji: '🪀',\n title: 'Yo-Yo',\n },\n {\n emoji: '🪁',\n title: 'Kite',\n },\n {\n emoji: '🔮',\n title: 'Crystal Ball',\n },\n {\n emoji: '🪄',\n title: 'Magic Wand',\n },\n {\n emoji: '🧿',\n title: 'Nazar Amulet',\n },\n {\n emoji: '🕹️',\n title: 'Joystick',\n },\n {\n emoji: '🧸',\n title: 'Teddy Bear',\n },\n {\n emoji: '🪅',\n title: 'Piñata',\n },\n {\n emoji: '🪆',\n title: 'Nesting Dolls',\n },\n {\n emoji: '🖼️',\n title: 'Framed Picture',\n },\n {\n emoji: '🧵',\n title: 'Thread',\n },\n {\n emoji: '🪡',\n title: 'Sewing Needle',\n },\n {\n emoji: '🧶',\n title: 'Yarn',\n },\n {\n emoji: '🪢',\n title: 'Knot',\n },\n {\n emoji: '🛍️',\n title: 'Shopping Bags',\n },\n {\n emoji: '📿',\n title: 'Prayer Beads',\n },\n {\n emoji: '💎',\n title: 'Gem Stone',\n },\n {\n emoji: '📯',\n title: 'Postal Horn',\n },\n {\n emoji: '🎙️',\n title: 'Studio Microphone',\n },\n {\n emoji: '🎚️',\n title: 'Level Slider',\n },\n {\n emoji: '🎛️',\n title: 'Control Knobs',\n },\n {\n emoji: '📻',\n title: 'Radio',\n },\n {\n emoji: '🪕',\n title: 'Banjo',\n },\n {\n emoji: '📱',\n title: 'Mobile Phone',\n },\n {\n emoji: '📲',\n title: 'Mobile Phone with Arrow',\n },\n {\n emoji: '☎️',\n title: 'Telephone',\n },\n {\n emoji: '📞',\n title: 'Telephone Receiver',\n },\n {\n emoji: '📟',\n title: 'Pager',\n },\n {\n emoji: '📠',\n title: 'Fax Machine',\n },\n {\n emoji: '🔋',\n title: 'Battery',\n },\n {\n emoji: '🔌',\n title: 'Electric Plug',\n },\n {\n emoji: '💻',\n title: 'Laptop',\n },\n {\n emoji: '🖥️',\n title: 'Desktop Computer',\n },\n {\n emoji: '🖨️',\n title: 'Printer',\n },\n {\n emoji: '⌨️',\n title: 'Keyboard',\n },\n {\n emoji: '🖱️',\n title: 'Computer Mouse',\n },\n {\n emoji: '🖲️',\n title: 'Trackball',\n },\n {\n emoji: '💽',\n title: 'Computer Disk',\n },\n {\n emoji: '💾',\n title: 'Floppy Disk',\n },\n {\n emoji: '💿',\n title: 'Optical Disk',\n },\n {\n emoji: '📀',\n title: 'DVD',\n },\n {\n emoji: '🧮',\n title: 'Abacus',\n },\n {\n emoji: '🎥',\n title: 'Movie Camera',\n },\n {\n emoji: '🎞️',\n title: 'Film Frames',\n },\n {\n emoji: '📽️',\n title: 'Film Projector',\n },\n {\n emoji: '📺',\n title: 'Television',\n },\n {\n emoji: '📷',\n title: 'Camera',\n },\n {\n emoji: '📸',\n title: 'Camera with Flash',\n },\n {\n emoji: '📹',\n title: 'Video Camera',\n },\n {\n emoji: '📼',\n title: 'Videocassette',\n },\n {\n emoji: '🔍',\n title: 'Magnifying Glass Tilted Left',\n },\n {\n emoji: '🔎',\n title: 'Magnifying Glass Tilted Right',\n },\n {\n emoji: '🕯️',\n title: 'Candle',\n },\n {\n emoji: '💡',\n title: 'Light Bulb',\n },\n {\n emoji: '🔦',\n title: 'Flashlight',\n },\n {\n emoji: '🏮',\n title: 'Red Paper Lantern',\n },\n {\n emoji: '🪔',\n title: 'Diya Lamp',\n },\n {\n emoji: '📔',\n title: 'Notebook with Decorative Cover',\n },\n {\n emoji: '📕',\n title: 'Closed Book',\n },\n {\n emoji: '📖',\n title: 'Open Book',\n },\n {\n emoji: '📗',\n title: 'Green Book',\n },\n {\n emoji: '📘',\n title: 'Blue Book',\n },\n {\n emoji: '📙',\n title: 'Orange Book',\n },\n {\n emoji: '📚',\n title: 'Books',\n },\n {\n emoji: '📓',\n title: 'Notebook',\n },\n {\n emoji: '📒',\n title: 'Ledger',\n },\n {\n emoji: '📃',\n title: 'Page with Curl',\n },\n {\n emoji: '📜',\n title: 'Scroll',\n },\n {\n emoji: '📄',\n title: 'Page Facing Up',\n },\n {\n emoji: '📰',\n title: 'Newspaper',\n },\n {\n emoji: '🗞️',\n title: 'Rolled-Up Newspaper',\n },\n {\n emoji: '📑',\n title: 'Bookmark Tabs',\n },\n {\n emoji: '🔖',\n title: 'Bookmark',\n },\n {\n emoji: '🏷️',\n title: 'Label',\n },\n {\n emoji: '💰',\n title: 'Money Bag',\n },\n {\n emoji: '🪙',\n title: 'Coin',\n },\n {\n emoji: '💴',\n title: 'Yen Banknote',\n },\n {\n emoji: '💵',\n title: 'Dollar Banknote',\n },\n {\n emoji: '💶',\n title: 'Euro Banknote',\n },\n {\n emoji: '💷',\n title: 'Pound Banknote',\n },\n {\n emoji: '💸',\n title: 'Money with Wings',\n },\n {\n emoji: '💳',\n title: 'Credit Card',\n },\n {\n emoji: '🧾',\n title: 'Receipt',\n },\n {\n emoji: '✉️',\n title: 'Envelope',\n },\n {\n emoji: '📧',\n title: 'E-Mail',\n },\n {\n emoji: '📨',\n title: 'Incoming Envelope',\n },\n {\n emoji: '📩',\n title: 'Envelope with Arrow',\n },\n {\n emoji: '📤',\n title: 'Outbox Tray',\n },\n {\n emoji: '📥',\n title: 'Inbox Tray',\n },\n {\n emoji: '📦',\n title: 'Package',\n },\n {\n emoji: '📫',\n title: 'Closed Mailbox with Raised Flag',\n },\n {\n emoji: '📪',\n title: 'Closed Mailbox with Lowered Flag',\n },\n {\n emoji: '📬',\n title: 'Open Mailbox with Raised Flag',\n },\n {\n emoji: '📭',\n title: 'Open Mailbox with Lowered Flag',\n },\n {\n emoji: '📮',\n title: 'Postbox',\n },\n {\n emoji: '🗳️',\n title: 'Ballot Box with Ballot',\n },\n {\n emoji: '✏️',\n title: 'Pencil',\n },\n {\n emoji: '✒️',\n title: 'Black Nib',\n },\n {\n emoji: '🖋️',\n title: 'Fountain Pen',\n },\n {\n emoji: '🖊️',\n title: 'Pen',\n },\n {\n emoji: '🖌️',\n title: 'Paintbrush',\n },\n {\n emoji: '🖍️',\n title: 'Crayon',\n },\n {\n emoji: '📝',\n title: 'Memo',\n },\n {\n emoji: '📁',\n title: 'File Folder',\n },\n {\n emoji: '📂',\n title: 'Open File Folder',\n },\n {\n emoji: '🗂️',\n title: 'Card Index Dividers',\n },\n {\n emoji: '📅',\n title: 'Calendar',\n },\n {\n emoji: '📆',\n title: 'Tear-Off Calendar',\n },\n {\n emoji: '🗒️',\n title: 'Spiral Notepad',\n },\n {\n emoji: '🗓️',\n title: 'Spiral Calendar',\n },\n {\n emoji: '📇',\n title: 'Card Index',\n },\n {\n emoji: '📈',\n title: 'Chart Increasing',\n },\n {\n emoji: '📉',\n title: 'Chart Decreasing',\n },\n {\n emoji: '📊',\n title: 'Bar Chart',\n },\n {\n emoji: '📋',\n title: 'Clipboard',\n },\n {\n emoji: '📌',\n title: 'Pushpin',\n },\n {\n emoji: '📍',\n title: 'Round Pushpin',\n },\n {\n emoji: '📎',\n title: 'Paperclip',\n },\n {\n emoji: '🖇️',\n title: 'Linked Paperclips',\n },\n {\n emoji: '📏',\n title: 'Straight Ruler',\n },\n {\n emoji: '📐',\n title: 'Triangular Ruler',\n },\n {\n emoji: '✂️',\n title: 'Scissors',\n },\n {\n emoji: '🗃️',\n title: 'Card File Box',\n },\n {\n emoji: '🗄️',\n title: 'File Cabinet',\n },\n {\n emoji: '🗑️',\n title: 'Wastebasket',\n },\n {\n emoji: '🔒',\n title: 'Locked',\n },\n {\n emoji: '🔓',\n title: 'Unlocked',\n },\n {\n emoji: '🔏',\n title: 'Locked with Pen',\n },\n {\n emoji: '🔐',\n title: 'Locked with Key',\n },\n {\n emoji: '🔑',\n title: 'Key',\n },\n {\n emoji: '🗝️',\n title: 'Old Key',\n },\n {\n emoji: '🔨',\n title: 'Hammer',\n },\n {\n emoji: '🪓',\n title: 'Axe',\n },\n {\n emoji: '⛏️',\n title: 'Pick',\n },\n {\n emoji: '⚒️',\n title: 'Hammer and Pick',\n },\n {\n emoji: '🛠️',\n title: 'Hammer and Wrench',\n },\n {\n emoji: '🗡️',\n title: 'Dagger',\n },\n {\n emoji: '⚔️',\n title: 'Crossed Swords',\n },\n {\n emoji: '🔫',\n title: 'Water Pistol',\n },\n {\n emoji: '🪃',\n title: 'Boomerang',\n },\n {\n emoji: '🛡️',\n title: 'Shield',\n },\n {\n emoji: '🪚',\n title: 'Carpentry Saw',\n },\n {\n emoji: '🔧',\n title: 'Wrench',\n },\n {\n emoji: '🪛',\n title: 'Screwdriver',\n },\n {\n emoji: '🔩',\n title: 'Nut and Bolt',\n },\n {\n emoji: '⚙️',\n title: 'Gear',\n },\n {\n emoji: '🗜️',\n title: 'Clamp',\n },\n {\n emoji: '⚖️',\n title: 'Balance Scale',\n },\n {\n emoji: '🦯',\n title: 'White Cane',\n },\n {\n emoji: '🔗',\n title: 'Link',\n },\n {\n emoji: '⛓️',\n title: 'Chains',\n },\n {\n emoji: '🪝',\n title: 'Hook',\n },\n {\n emoji: '🧰',\n title: 'Toolbox',\n },\n {\n emoji: '🧲',\n title: 'Magnet',\n },\n {\n emoji: '🪜',\n title: 'Ladder',\n },\n {\n emoji: '⚗️',\n title: 'Alembic',\n },\n {\n emoji: '🧪',\n title: 'Test Tube',\n },\n {\n emoji: '🧫',\n title: 'Petri Dish',\n },\n {\n emoji: '🧬',\n title: 'DNA',\n },\n {\n emoji: '🔬',\n title: 'Microscope',\n },\n {\n emoji: '🔭',\n title: 'Telescope',\n },\n {\n emoji: '📡',\n title: 'Satellite Antenna',\n },\n {\n emoji: '💉',\n title: 'Syringe',\n },\n {\n emoji: '🩸',\n title: 'Drop of Blood',\n },\n {\n emoji: '💊',\n title: 'Pill',\n },\n {\n emoji: '🩹',\n title: 'Adhesive Bandage',\n },\n {\n emoji: '🩺',\n title: 'Stethoscope',\n },\n {\n emoji: '🚪',\n title: 'Door',\n },\n {\n emoji: '🪞',\n title: 'Mirror',\n },\n {\n emoji: '🪟',\n title: 'Window',\n },\n {\n emoji: '🛏️',\n title: 'Bed',\n },\n {\n emoji: '🛋️',\n title: 'Couch and Lamp',\n },\n {\n emoji: '🪑',\n title: 'Chair',\n },\n {\n emoji: '🚽',\n title: 'Toilet',\n },\n {\n emoji: '🪠',\n title: 'Plunger',\n },\n {\n emoji: '🚿',\n title: 'Shower',\n },\n {\n emoji: '🛁',\n title: 'Bathtub',\n },\n {\n emoji: '🪤',\n title: 'Mouse Trap',\n },\n {\n emoji: '🪒',\n title: 'Razor',\n },\n {\n emoji: '🧴',\n title: 'Lotion Bottle',\n },\n {\n emoji: '🧷',\n title: 'Safety Pin',\n },\n {\n emoji: '🧹',\n title: 'Broom',\n },\n {\n emoji: '🧺',\n title: 'Basket',\n },\n {\n emoji: '🧻',\n title: 'Roll of Paper',\n },\n {\n emoji: '🪣',\n title: 'Bucket',\n },\n {\n emoji: '🧼',\n title: 'Soap',\n },\n {\n emoji: '🪥',\n title: 'Toothbrush',\n },\n {\n emoji: '🧽',\n title: 'Sponge',\n },\n {\n emoji: '🧯',\n title: 'Fire Extinguisher',\n },\n {\n emoji: '🛒',\n title: 'Shopping Cart',\n },\n {\n emoji: '🚬',\n title: 'Cigarette',\n },\n {\n emoji: '⚰️',\n title: 'Coffin',\n },\n {\n emoji: '🪦',\n title: 'Headstone',\n },\n {\n emoji: '⚱️',\n title: 'Funeral Urn',\n },\n {\n emoji: '🗿',\n title: 'Moai',\n },\n {\n emoji: '🪧',\n title: 'Placard',\n },\n {\n emoji: '🚰',\n title: 'Potable Water',\n },\n ],\n Symbols: [\n {\n emoji: '💘',\n title: 'Heart with Arrow',\n },\n {\n emoji: '💝',\n title: 'Heart with Ribbon',\n },\n {\n emoji: '💖',\n title: 'Sparkling Heart',\n },\n {\n emoji: '💗',\n title: 'Growing Heart',\n },\n {\n emoji: '💓',\n title: 'Beating Heart',\n },\n {\n emoji: '💞',\n title: 'Revolving Hearts',\n },\n {\n emoji: '💕',\n title: 'Two Hearts',\n },\n {\n emoji: '💟',\n title: 'Heart Decoration',\n },\n {\n emoji: '❣️',\n title: 'Heart Exclamation',\n },\n {\n emoji: '💔',\n title: 'Broken Heart',\n },\n {\n emoji: '❤️‍🔥',\n title: 'Heart on Fire',\n },\n {\n emoji: '❤️‍🩹',\n title: 'Mending Heart',\n },\n {\n emoji: '❤️',\n title: 'Red Heart',\n },\n {\n emoji: '🧡',\n title: 'Orange Heart',\n },\n {\n emoji: '💛',\n title: 'Yellow Heart',\n },\n {\n emoji: '💚',\n title: 'Green Heart',\n },\n {\n emoji: '💙',\n title: 'Blue Heart',\n },\n {\n emoji: '💜',\n title: 'Purple Heart',\n },\n {\n emoji: '🤎',\n title: 'Brown Heart',\n },\n {\n emoji: '🖤',\n title: 'Black Heart',\n },\n {\n emoji: '🤍',\n title: 'White Heart',\n },\n {\n emoji: '💯',\n title: 'Hundred Points',\n },\n {\n emoji: '💢',\n title: 'Anger Symbol',\n },\n {\n emoji: '💬',\n title: 'Speech Balloon',\n },\n {\n emoji: '👁️‍🗨️',\n title: 'Eye in Speech Bubble',\n },\n {\n emoji: '🗨️',\n title: 'Left Speech Bubble',\n },\n {\n emoji: '🗯️',\n title: 'Right Anger Bubble',\n },\n {\n emoji: '💭',\n title: 'Thought Balloon',\n },\n {\n emoji: '💤',\n title: 'Zzz',\n },\n {\n emoji: '💮',\n title: 'White Flower',\n },\n {\n emoji: '♨️',\n title: 'Hot Springs',\n },\n {\n emoji: '💈',\n title: 'Barber Pole',\n },\n {\n emoji: '🛑',\n title: 'Stop Sign',\n },\n {\n emoji: '🕛',\n title: 'Twelve O’Clock',\n },\n {\n emoji: '🕧',\n title: 'Twelve-Thirty',\n },\n {\n emoji: '🕐',\n title: 'One O’Clock',\n },\n {\n emoji: '🕜',\n title: 'One-Thirty',\n },\n {\n emoji: '🕑',\n title: 'Two O’Clock',\n },\n {\n emoji: '🕝',\n title: 'Two-Thirty',\n },\n {\n emoji: '🕒',\n title: 'Three O’Clock',\n },\n {\n emoji: '🕞',\n title: 'Three-Thirty',\n },\n {\n emoji: '🕓',\n title: 'Four O’Clock',\n },\n {\n emoji: '🕟',\n title: 'Four-Thirty',\n },\n {\n emoji: '🕔',\n title: 'Five O’Clock',\n },\n {\n emoji: '🕠',\n title: 'Five-Thirty',\n },\n {\n emoji: '🕕',\n title: 'Six O’Clock',\n },\n {\n emoji: '🕡',\n title: 'Six-Thirty',\n },\n {\n emoji: '🕖',\n title: 'Seven O’Clock',\n },\n {\n emoji: '🕢',\n title: 'Seven-Thirty',\n },\n {\n emoji: '🕗',\n title: 'Eight O’Clock',\n },\n {\n emoji: '🕣',\n title: 'Eight-Thirty',\n },\n {\n emoji: '🕘',\n title: 'Nine O’Clock',\n },\n {\n emoji: '🕤',\n title: 'Nine-Thirty',\n },\n {\n emoji: '🕙',\n title: 'Ten O’Clock',\n },\n {\n emoji: '🕥',\n title: 'Ten-Thirty',\n },\n {\n emoji: '🕚',\n title: 'Eleven O’Clock',\n },\n {\n emoji: '🕦',\n title: 'Eleven-Thirty',\n },\n {\n emoji: '🌀',\n title: 'Cyclone',\n },\n {\n emoji: '♠️',\n title: 'Spade Suit',\n },\n {\n emoji: '♥️',\n title: 'Heart Suit',\n },\n {\n emoji: '♦️',\n title: 'Diamond Suit',\n },\n {\n emoji: '♣️',\n title: 'Club Suit',\n },\n {\n emoji: '🃏',\n title: 'Joker',\n },\n {\n emoji: '🀄',\n title: 'Mahjong Red Dragon',\n },\n {\n emoji: '🎴',\n title: 'Flower Playing Cards',\n },\n {\n emoji: '🔇',\n title: 'Muted Speaker',\n },\n {\n emoji: '🔈',\n title: 'Speaker Low Volume',\n },\n {\n emoji: '🔉',\n title: 'Speaker Medium Volume',\n },\n {\n emoji: '🔊',\n title: 'Speaker High Volume',\n },\n {\n emoji: '📢',\n title: 'Loudspeaker',\n },\n {\n emoji: '📣',\n title: 'Megaphone',\n },\n {\n emoji: '📯',\n title: 'Postal Horn',\n },\n {\n emoji: '🔔',\n title: 'Bell',\n },\n {\n emoji: '🔕',\n title: 'Bell with Slash',\n },\n {\n emoji: '🎵',\n title: 'Musical Note',\n },\n {\n emoji: '🎶',\n title: 'Musical Notes',\n },\n {\n emoji: '💹',\n title: 'Chart Increasing with Yen',\n },\n {\n emoji: '🛗',\n title: 'Elevator',\n },\n {\n emoji: '🏧',\n title: 'ATM Sign',\n },\n {\n emoji: '🚮',\n title: 'Litter in Bin Sign',\n },\n {\n emoji: '🚰',\n title: 'Potable Water',\n },\n {\n emoji: '♿',\n title: 'Wheelchair Symbol',\n },\n {\n emoji: '🚹',\n title: 'Men’s Room',\n },\n {\n emoji: '🚺',\n title: 'Women’s Room',\n },\n {\n emoji: '🚻',\n title: 'Restroom',\n },\n {\n emoji: '🚼',\n title: 'Baby Symbol',\n },\n {\n emoji: '🚾',\n title: 'Water Closet',\n },\n {\n emoji: '⚠️',\n title: 'Warning',\n },\n {\n emoji: '🚸',\n title: 'Children Crossing',\n },\n {\n emoji: '⛔',\n title: 'No Entry',\n },\n {\n emoji: '🚫',\n title: 'Prohibited',\n },\n {\n emoji: '🚳',\n title: 'No Bicycles',\n },\n {\n emoji: '🚭',\n title: 'No Smoking',\n },\n {\n emoji: '🚯',\n title: 'No Littering',\n },\n {\n emoji: '🚱',\n title: 'Non-Potable Water',\n },\n {\n emoji: '🚷',\n title: 'No Pedestrians',\n },\n {\n emoji: '📵',\n title: 'No Mobile Phones',\n },\n {\n emoji: '🔞',\n title: 'No One Under Eighteen',\n },\n {\n emoji: '☢️',\n title: 'Radioactive',\n },\n {\n emoji: '☣️',\n title: 'Biohazard',\n },\n {\n emoji: '⬆️',\n title: 'Up Arrow',\n },\n {\n emoji: '↗️',\n title: 'Up-Right Arrow',\n },\n {\n emoji: '➡️',\n title: 'Right Arrow',\n },\n {\n emoji: '↘️',\n title: 'Down-Right Arrow',\n },\n {\n emoji: '⬇️',\n title: 'Down Arrow',\n },\n {\n emoji: '↙️',\n title: 'Down-Left Arrow',\n },\n {\n emoji: '⬅️',\n title: 'Left Arrow',\n },\n {\n emoji: '↖️',\n title: 'Up-Left Arrow',\n },\n {\n emoji: '↕️',\n title: 'Up-Down Arrow',\n },\n {\n emoji: '↔️',\n title: 'Left-Right Arrow',\n },\n {\n emoji: '↩️',\n title: 'Right Arrow Curving Left',\n },\n {\n emoji: '↪️',\n title: 'Left Arrow Curving Right',\n },\n {\n emoji: '⤴️',\n title: 'Right Arrow Curving Up',\n },\n {\n emoji: '⤵️',\n title: 'Right Arrow Curving Down',\n },\n {\n emoji: '🔃',\n title: 'Clockwise Vertical Arrows',\n },\n {\n emoji: '🔄',\n title: 'Counterclockwise Arrows Button',\n },\n {\n emoji: '🔙',\n title: 'Back Arrow',\n },\n {\n emoji: '🔚',\n title: 'End Arrow',\n },\n {\n emoji: '🔛',\n title: 'On! Arrow',\n },\n {\n emoji: '🔜',\n title: 'Soon Arrow',\n },\n {\n emoji: '🔝',\n title: 'Top Arrow',\n },\n {\n emoji: '🛐',\n title: 'Place of Worship',\n },\n {\n emoji: '⚛️',\n title: 'Atom Symbol',\n },\n {\n emoji: '🕉️',\n title: 'Om',\n },\n {\n emoji: '✡️',\n title: 'Star of David',\n },\n {\n emoji: '☸️',\n title: 'Wheel of Dharma',\n },\n {\n emoji: '☯️',\n title: 'Yin Yang',\n },\n {\n emoji: '✝️',\n title: 'Latin Cross',\n },\n {\n emoji: '☦️',\n title: 'Orthodox Cross',\n },\n {\n emoji: '☪️',\n title: 'Star and Crescent',\n },\n {\n emoji: '☮️',\n title: 'Peace Symbol',\n },\n {\n emoji: '🕎',\n title: 'Menorah',\n },\n {\n emoji: '🔯',\n title: 'Dotted Six-Pointed Star',\n },\n {\n emoji: '♈',\n title: 'Aries',\n },\n {\n emoji: '♉',\n title: 'Taurus',\n },\n {\n emoji: '♊',\n title: 'Gemini',\n },\n {\n emoji: '♋',\n title: 'Cancer',\n },\n {\n emoji: '♌',\n title: 'Leo',\n },\n {\n emoji: '♍',\n title: 'Virgo',\n },\n {\n emoji: '♎',\n title: 'Libra',\n },\n {\n emoji: '♏',\n title: 'Scorpio',\n },\n {\n emoji: '♐',\n title: 'Sagittarius',\n },\n {\n emoji: '♑',\n title: 'Capricorn',\n },\n {\n emoji: '♒',\n title: 'Aquarius',\n },\n {\n emoji: '♓',\n title: 'Pisces',\n },\n {\n emoji: '⛎',\n title: 'Ophiuchus',\n },\n {\n emoji: '🔀',\n title: 'Shuffle Tracks Button',\n },\n {\n emoji: '🔁',\n title: 'Repeat Button',\n },\n {\n emoji: '🔂',\n title: 'Repeat Single Button',\n },\n {\n emoji: '▶️',\n title: 'Play Button',\n },\n {\n emoji: '⏩',\n title: 'Fast-Forward Button',\n },\n {\n emoji: '⏭️',\n title: 'Next Track Button',\n },\n {\n emoji: '⏯️',\n title: 'Play or Pause Button',\n },\n {\n emoji: '◀️',\n title: 'Reverse Button',\n },\n {\n emoji: '⏪',\n title: 'Fast Reverse Button',\n },\n {\n emoji: '⏮️',\n title: 'Last Track Button',\n },\n {\n emoji: '🔼',\n title: 'Upwards Button',\n },\n {\n emoji: '⏫',\n title: 'Fast Up Button',\n },\n {\n emoji: '🔽',\n title: 'Downwards Button',\n },\n {\n emoji: '⏬',\n title: 'Fast Down Button',\n },\n {\n emoji: '⏸️',\n title: 'Pause Button',\n },\n {\n emoji: '⏹️',\n title: 'Stop Button',\n },\n {\n emoji: '⏺️',\n title: 'Record Button',\n },\n {\n emoji: '⏏️',\n title: 'Eject Button',\n },\n {\n emoji: '🎦',\n title: 'Cinema',\n },\n {\n emoji: '🔅',\n title: 'Dim Button',\n },\n {\n emoji: '🔆',\n title: 'Bright Button',\n },\n {\n emoji: '📶',\n title: 'Antenna Bars',\n },\n {\n emoji: '📳',\n title: 'Vibration Mode',\n },\n {\n emoji: '📴',\n title: 'Mobile Phone Off',\n },\n {\n emoji: '♀️',\n title: 'Female Sign',\n },\n {\n emoji: '♂️',\n title: 'Male Sign',\n },\n {\n emoji: '✖️',\n title: 'Multiply',\n },\n {\n emoji: '➕',\n title: 'Plus',\n },\n {\n emoji: '➖',\n title: 'Minus',\n },\n {\n emoji: '➗',\n title: 'Divide',\n },\n {\n emoji: '♾️',\n title: 'Infinity',\n },\n {\n emoji: '‼️',\n title: '‼ Double Exclamation Mark',\n },\n {\n emoji: '⁉️',\n title: '⁉ Exclamation Question Mark',\n },\n {\n emoji: '❓',\n title: 'Red Question Mark',\n },\n {\n emoji: '❔',\n title: 'White Question Mark',\n },\n {\n emoji: '❕',\n title: 'White Exclamation Mark',\n },\n {\n emoji: '❗',\n title: 'Red Exclamation Mark',\n },\n {\n emoji: '〰️',\n title: '〰 Wavy Dash',\n },\n {\n emoji: '💱',\n title: 'Currency Exchange',\n },\n {\n emoji: '💲',\n title: 'Heavy Dollar Sign',\n },\n {\n emoji: '⚕️',\n title: 'Medical Symbol',\n },\n {\n emoji: '♻️',\n title: 'Recycling Symbol',\n },\n {\n emoji: '⚜️',\n title: 'Fleur-de-lis',\n },\n {\n emoji: '🔱',\n title: 'Trident Emblem',\n },\n {\n emoji: '📛',\n title: 'Name Badge',\n },\n {\n emoji: '🔰',\n title: 'Japanese Symbol for Beginner',\n },\n {\n emoji: '⭕',\n title: 'Hollow Red Circle',\n },\n {\n emoji: '✅',\n title: 'Check Mark Button',\n },\n {\n emoji: '☑️',\n title: 'Check Box with Check',\n },\n {\n emoji: '✔️',\n title: 'Check Mark',\n },\n {\n emoji: '❌',\n title: 'Cross Mark',\n },\n {\n emoji: '❎',\n title: 'Cross Mark Button',\n },\n {\n emoji: '➰',\n title: 'Curly Loop',\n },\n {\n emoji: '➿',\n title: 'Double Curly Loop',\n },\n {\n emoji: '〽️',\n title: '〽 Part Alternation Mark',\n },\n {\n emoji: '✳️',\n title: 'Eight-Spoked Asterisk',\n },\n {\n emoji: '✴️',\n title: 'Eight-Pointed Star',\n },\n {\n emoji: '❇️',\n title: 'Sparkle',\n },\n {\n emoji: '©️',\n title: 'Copyright',\n },\n {\n emoji: '®️',\n title: 'Registered',\n },\n {\n emoji: '™️',\n title: 'Trade Mark',\n },\n {\n emoji: '#️⃣',\n title: '# Keycap Number Sign',\n },\n {\n emoji: '*️⃣',\n title: '* Keycap Asterisk',\n },\n {\n emoji: '0️⃣',\n title: '0 Keycap Digit Zero',\n },\n {\n emoji: '1️⃣',\n title: '1 Keycap Digit One',\n },\n {\n emoji: '2️⃣',\n title: '2 Keycap Digit Two',\n },\n {\n emoji: '3️⃣',\n title: '3 Keycap Digit Three',\n },\n {\n emoji: '4️⃣',\n title: '4 Keycap Digit Four',\n },\n {\n emoji: '5️⃣',\n title: '5 Keycap Digit Five',\n },\n {\n emoji: '6️⃣',\n title: '6 Keycap Digit Six',\n },\n {\n emoji: '7️⃣',\n title: '7 Keycap Digit Seven',\n },\n {\n emoji: '8️⃣',\n title: '8 Keycap Digit Eight',\n },\n {\n emoji: '9️⃣',\n title: '9 Keycap Digit Nine',\n },\n {\n emoji: '🔟',\n title: 'Keycap: 10',\n },\n {\n emoji: '🔠',\n title: 'Input Latin Uppercase',\n },\n {\n emoji: '🔡',\n title: 'Input Latin Lowercase',\n },\n {\n emoji: '🔢',\n title: 'Input Numbers',\n },\n {\n emoji: '🔣',\n title: 'Input Symbols',\n },\n {\n emoji: '🔤',\n title: 'Input Latin Letters',\n },\n {\n emoji: '🅰️',\n title: 'A Button (Blood Type)',\n },\n {\n emoji: '🆎',\n title: 'AB Button (Blood Type)',\n },\n {\n emoji: '🅱️',\n title: 'B Button (Blood Type)',\n },\n {\n emoji: '🆑',\n title: 'CL Button',\n },\n {\n emoji: '🆒',\n title: 'Cool Button',\n },\n {\n emoji: '🆓',\n title: 'Free Button',\n },\n {\n emoji: 'ℹ️',\n title: 'ℹ Information',\n },\n {\n emoji: '🆔',\n title: 'ID Button',\n },\n {\n emoji: 'Ⓜ️',\n title: 'Circled M',\n },\n {\n emoji: '🆕',\n title: 'New Button',\n },\n {\n emoji: '🆖',\n title: 'NG Button',\n },\n {\n emoji: '🅾️',\n title: 'O Button (Blood Type)',\n },\n {\n emoji: '🆗',\n title: 'OK Button',\n },\n {\n emoji: '🅿️',\n title: 'P Button',\n },\n {\n emoji: '🆘',\n title: 'SOS Button',\n },\n {\n emoji: '🆙',\n title: 'Up! Button',\n },\n {\n emoji: '🆚',\n title: 'Vs Button',\n },\n {\n emoji: '🈁',\n title: 'Japanese “Here” Button',\n },\n {\n emoji: '🈂️',\n title: 'Japanese “Service Charge” Button',\n },\n {\n emoji: '🈷️',\n title: 'Japanese “Monthly Amount” Button',\n },\n {\n emoji: '🈶',\n title: 'Japanese “Not Free of Charge” Button',\n },\n {\n emoji: '🈯',\n title: 'Japanese “Reserved” Button',\n },\n {\n emoji: '🉐',\n title: 'Japanese “Bargain” Button',\n },\n {\n emoji: '🈹',\n title: 'Japanese “Discount” Button',\n },\n {\n emoji: '🈚',\n title: 'Japanese “Free of Charge” Button',\n },\n {\n emoji: '🈲',\n title: 'Japanese “Prohibited” Button',\n },\n {\n emoji: '🉑',\n title: 'Japanese “Acceptable” Button',\n },\n {\n emoji: '🈸',\n title: 'Japanese “Application” Button',\n },\n {\n emoji: '🈴',\n title: 'Japanese “Passing Grade” Button',\n },\n {\n emoji: '🈳',\n title: 'Japanese “Vacancy” Button',\n },\n {\n emoji: '㊗️',\n title: 'Japanese “Congratulations” Button',\n },\n {\n emoji: '㊙️',\n title: 'Japanese “Secret” Button',\n },\n {\n emoji: '🈺',\n title: 'Japanese “Open for Business” Button',\n },\n {\n emoji: '🈵',\n title: 'Japanese “No Vacancy” Button',\n },\n {\n emoji: '🔴',\n title: 'Red Circle',\n },\n {\n emoji: '🟠',\n title: 'Orange Circle',\n },\n {\n emoji: '🟡',\n title: 'Yellow Circle',\n },\n {\n emoji: '🟢',\n title: 'Green Circle',\n },\n {\n emoji: '🔵',\n title: 'Blue Circle',\n },\n {\n emoji: '🟣',\n title: 'Purple Circle',\n },\n {\n emoji: '🟤',\n title: 'Brown Circle',\n },\n {\n emoji: '⚫',\n title: 'Black Circle',\n },\n {\n emoji: '⚪',\n title: 'White Circle',\n },\n {\n emoji: '🟥',\n title: 'Red Square',\n },\n {\n emoji: '🟧',\n title: 'Orange Square',\n },\n {\n emoji: '🟨',\n title: 'Yellow Square',\n },\n {\n emoji: '🟩',\n title: 'Green Square',\n },\n {\n emoji: '🟦',\n title: 'Blue Square',\n },\n {\n emoji: '🟪',\n title: 'Purple Square',\n },\n {\n emoji: '🟫',\n title: 'Brown Square',\n },\n {\n emoji: '⬛',\n title: 'Black Large Square',\n },\n {\n emoji: '⬜',\n title: 'White Large Square',\n },\n {\n emoji: '◼️',\n title: 'Black Medium Square',\n },\n {\n emoji: '◻️',\n title: 'White Medium Square',\n },\n {\n emoji: '◾',\n title: 'Black Medium-Small Square',\n },\n {\n emoji: '◽',\n title: 'White Medium-Small Square',\n },\n {\n emoji: '▪️',\n title: 'Black Small Square',\n },\n {\n emoji: '▫️',\n title: 'White Small Square',\n },\n {\n emoji: '🔶',\n title: 'Large Orange Diamond',\n },\n {\n emoji: '🔷',\n title: 'Large Blue Diamond',\n },\n {\n emoji: '🔸',\n title: 'Small Orange Diamond',\n },\n {\n emoji: '🔹',\n title: 'Small Blue Diamond',\n },\n {\n emoji: '🔺',\n title: 'Red Triangle Pointed Up',\n },\n {\n emoji: '🔻',\n title: 'Red Triangle Pointed Down',\n },\n {\n emoji: '💠',\n title: 'Diamond with a Dot',\n },\n {\n emoji: '🔘',\n title: 'Radio Button',\n },\n {\n emoji: '🔳',\n title: 'White Square Button',\n },\n {\n emoji: '🔲',\n title: 'Black Square Button',\n },\n ],\n Flags: [\n {\n emoji: '🏁',\n title: 'Chequered Flag',\n },\n {\n emoji: '🚩',\n title: 'Triangular Flag',\n },\n {\n emoji: '🎌',\n title: 'Crossed Flags',\n },\n {\n emoji: '🏴',\n title: 'Black Flag',\n },\n {\n emoji: '🏳️',\n title: 'White Flag',\n },\n {\n emoji: '🏳️‍🌈',\n title: 'Rainbow Flag',\n },\n {\n emoji: '🏳️‍⚧️',\n title: 'Transgender Flag',\n },\n {\n emoji: '🏴‍☠️',\n title: 'Pirate Flag',\n },\n {\n emoji: '🇦🇨',\n title: 'Flag: Ascension Island',\n },\n {\n emoji: '🇦🇩',\n title: 'Flag: Andorra',\n },\n {\n emoji: '🇦🇪',\n title: 'Flag: United Arab Emirates',\n },\n {\n emoji: '🇦🇫',\n title: 'Flag: Afghanistan',\n },\n {\n emoji: '🇦🇬',\n title: 'Flag: Antigua & Barbuda',\n },\n {\n emoji: '🇦🇮',\n title: 'Flag: Anguilla',\n },\n {\n emoji: '🇦🇱',\n title: 'Flag: Albania',\n },\n {\n emoji: '🇦🇲',\n title: 'Flag: Armenia',\n },\n {\n emoji: '🇦🇴',\n title: 'Flag: Angola',\n },\n {\n emoji: '🇦🇶',\n title: 'Flag: Antarctica',\n },\n {\n emoji: '🇦🇷',\n title: 'Flag: Argentina',\n },\n {\n emoji: '🇦🇸',\n title: 'Flag: American Samoa',\n },\n {\n emoji: '🇦🇹',\n title: 'Flag: Austria',\n },\n {\n emoji: '🇦🇺',\n title: 'Flag: Australia',\n },\n {\n emoji: '🇦🇼',\n title: 'Flag: Aruba',\n },\n {\n emoji: '🇦🇽',\n title: 'Flag: Åland Islands',\n },\n {\n emoji: '🇦🇿',\n title: 'Flag: Azerbaijan',\n },\n {\n emoji: '🇧🇦',\n title: 'Flag: Bosnia & Herzegovina',\n },\n {\n emoji: '🇧🇧',\n title: 'Flag: Barbados',\n },\n {\n emoji: '🇧🇩',\n title: 'Flag: Bangladesh',\n },\n {\n emoji: '🇧🇪',\n title: 'Flag: Belgium',\n },\n {\n emoji: '🇧🇫',\n title: 'Flag: Burkina Faso',\n },\n {\n emoji: '🇧🇬',\n title: 'Flag: Bulgaria',\n },\n {\n emoji: '🇧🇭',\n title: 'Flag: Bahrain',\n },\n {\n emoji: '🇧🇮',\n title: 'Flag: Burundi',\n },\n {\n emoji: '🇧🇯',\n title: 'Flag: Benin',\n },\n {\n emoji: '🇧🇱',\n title: 'Flag: St. Barthélemy',\n },\n {\n emoji: '🇧🇲',\n title: 'Flag: Bermuda',\n },\n {\n emoji: '🇧🇳',\n title: 'Flag: Brunei',\n },\n {\n emoji: '🇧🇴',\n title: 'Flag: Bolivia',\n },\n {\n emoji: '🇧🇶',\n title: 'Flag: Caribbean Netherlands',\n },\n {\n emoji: '🇧🇷',\n title: 'Flag: Brazil',\n },\n {\n emoji: '🇧🇸',\n title: 'Flag: Bahamas',\n },\n {\n emoji: '🇧🇹',\n title: 'Flag: Bhutan',\n },\n {\n emoji: '🇧🇻',\n title: 'Flag: Bouvet Island',\n },\n {\n emoji: '🇧🇼',\n title: 'Flag: Botswana',\n },\n {\n emoji: '🇧🇾',\n title: 'Flag: Belarus',\n },\n {\n emoji: '🇧🇿',\n title: 'Flag: Belize',\n },\n {\n emoji: '🇨🇦',\n title: 'Flag: Canada',\n },\n {\n emoji: '🇨🇨',\n title: 'Flag: Cocos (Keeling) Islands',\n },\n {\n emoji: '🇨🇩',\n title: 'Flag: Congo - Kinshasa',\n },\n {\n emoji: '🇨🇫',\n title: 'Flag: Central African Republic',\n },\n {\n emoji: '🇨🇬',\n title: 'Flag: Congo - Brazzaville',\n },\n {\n emoji: '🇨🇭',\n title: 'Flag: Switzerland',\n },\n {\n emoji: '🇨🇮',\n title: 'Flag: Côte d’Ivoire',\n },\n {\n emoji: '🇨🇰',\n title: 'Flag: Cook Islands',\n },\n {\n emoji: '🇨🇱',\n title: 'Flag: Chile',\n },\n {\n emoji: '🇨🇲',\n title: 'Flag: Cameroon',\n },\n {\n emoji: '🇨🇳',\n title: 'Flag: China',\n },\n {\n emoji: '🇨🇴',\n title: 'Flag: Colombia',\n },\n {\n emoji: '🇨🇵',\n title: 'Flag: Clipperton Island',\n },\n {\n emoji: '🇨🇷',\n title: 'Flag: Costa Rica',\n },\n {\n emoji: '🇨🇺',\n title: 'Flag: Cuba',\n },\n {\n emoji: '🇨🇻',\n title: 'Flag: Cape Verde',\n },\n {\n emoji: '🇨🇼',\n title: 'Flag: Curaçao',\n },\n {\n emoji: '🇨🇽',\n title: 'Flag: Christmas Island',\n },\n {\n emoji: '🇨🇾',\n title: 'Flag: Cyprus',\n },\n {\n emoji: '🇨🇿',\n title: 'Flag: Czechia',\n },\n {\n emoji: '🇩🇪',\n title: 'Flag: Germany',\n },\n {\n emoji: '🇩🇬',\n title: 'Flag: Diego Garcia',\n },\n {\n emoji: '🇩🇯',\n title: 'Flag: Djibouti',\n },\n {\n emoji: '🇩🇰',\n title: 'Flag: Denmark',\n },\n {\n emoji: '🇩🇲',\n title: 'Flag: Dominica',\n },\n {\n emoji: '🇩🇴',\n title: 'Flag: Dominican Republic',\n },\n {\n emoji: '🇩🇿',\n title: 'Flag: Algeria',\n },\n {\n emoji: '🇪🇦',\n title: 'Flag: Ceuta & Melilla',\n },\n {\n emoji: '🇪🇨',\n title: 'Flag: Ecuador',\n },\n {\n emoji: '🇪🇪',\n title: 'Flag: Estonia',\n },\n {\n emoji: '🇪🇬',\n title: 'Flag: Egypt',\n },\n {\n emoji: '🇪🇭',\n title: 'Flag: Western Sahara',\n },\n {\n emoji: '🇪🇷',\n title: 'Flag: Eritrea',\n },\n {\n emoji: '🇪🇸',\n title: 'Flag: Spain',\n },\n {\n emoji: '🇪🇹',\n title: 'Flag: Ethiopia',\n },\n {\n emoji: '🇪🇺',\n title: 'Flag: European Union',\n },\n {\n emoji: '🇫🇮',\n title: 'Flag: Finland',\n },\n {\n emoji: '🇫🇯',\n title: 'Flag: Fiji',\n },\n {\n emoji: '🇫🇰',\n title: 'Flag: Falkland Islands',\n },\n {\n emoji: '🇫🇲',\n title: 'Flag: Micronesia',\n },\n {\n emoji: '🇫🇴',\n title: 'Flag: Faroe Islands',\n },\n {\n emoji: '🇫🇷',\n title: 'Flag: France',\n },\n {\n emoji: '🇬🇦',\n title: 'Flag: Gabon',\n },\n {\n emoji: '🇬🇧',\n title: 'Flag: United Kingdom',\n },\n {\n emoji: '🇬🇩',\n title: 'Flag: Grenada',\n },\n {\n emoji: '🇬🇪',\n title: 'Flag: Georgia',\n },\n {\n emoji: '🇬🇫',\n title: 'Flag: French Guiana',\n },\n {\n emoji: '🇬🇬',\n title: 'Flag: Guernsey',\n },\n {\n emoji: '🇬🇭',\n title: 'Flag: Ghana',\n },\n {\n emoji: '🇬🇮',\n title: 'Flag: Gibraltar',\n },\n {\n emoji: '🇬🇱',\n title: 'Flag: Greenland',\n },\n {\n emoji: '🇬🇲',\n title: 'Flag: Gambia',\n },\n {\n emoji: '🇬🇳',\n title: 'Flag: Guinea',\n },\n {\n emoji: '🇬🇵',\n title: 'Flag: Guadeloupe',\n },\n {\n emoji: '🇬🇶',\n title: 'Flag: Equatorial Guinea',\n },\n {\n emoji: '🇬🇷',\n title: 'Flag: Greece',\n },\n {\n emoji: '🇬🇸',\n title: 'Flag: South Georgia & South Sandwich Islands',\n },\n {\n emoji: '🇬🇹',\n title: 'Flag: Guatemala',\n },\n {\n emoji: '🇬🇺',\n title: 'Flag: Guam',\n },\n {\n emoji: '🇬🇼',\n title: 'Flag: Guinea-Bissau',\n },\n {\n emoji: '🇬🇾',\n title: 'Flag: Guyana',\n },\n {\n emoji: '🇭🇰',\n title: 'Flag: Hong Kong SAR China',\n },\n {\n emoji: '🇭🇲',\n title: 'Flag: Heard & McDonald Islands',\n },\n {\n emoji: '🇭🇳',\n title: 'Flag: Honduras',\n },\n {\n emoji: '🇭🇷',\n title: 'Flag: Croatia',\n },\n {\n emoji: '🇭🇹',\n title: 'Flag: Haiti',\n },\n {\n emoji: '🇭🇺',\n title: 'Flag: Hungary',\n },\n {\n emoji: '🇮🇨',\n title: 'Flag: Canary Islands',\n },\n {\n emoji: '🇮🇩',\n title: 'Flag: Indonesia',\n },\n {\n emoji: '🇮🇪',\n title: 'Flag: Ireland',\n },\n {\n emoji: '🇮🇱',\n title: 'Flag: Israel',\n },\n {\n emoji: '🇮🇲',\n title: 'Flag: Isle of Man',\n },\n {\n emoji: '🇮🇳',\n title: 'Flag: India',\n },\n {\n emoji: '🇮🇴',\n title: 'Flag: British Indian Ocean Territory',\n },\n {\n emoji: '🇮🇶',\n title: 'Flag: Iraq',\n },\n {\n emoji: '🇮🇷',\n title: 'Flag: Iran',\n },\n {\n emoji: '🇮🇸',\n title: 'Flag: Iceland',\n },\n {\n emoji: '🇮🇹',\n title: 'Flag: Italy',\n },\n {\n emoji: '🇯🇪',\n title: 'Flag: Jersey',\n },\n {\n emoji: '🇯🇲',\n title: 'Flag: Jamaica',\n },\n {\n emoji: '🇯🇴',\n title: 'Flag: Jordan',\n },\n {\n emoji: '🇯🇵',\n title: 'Flag: Japan',\n },\n {\n emoji: '🇰🇪',\n title: 'Flag: Kenya',\n },\n {\n emoji: '🇰🇬',\n title: 'Flag: Kyrgyzstan',\n },\n {\n emoji: '🇰🇭',\n title: 'Flag: Cambodia',\n },\n {\n emoji: '🇰🇮',\n title: 'Flag: Kiribati',\n },\n {\n emoji: '🇰🇲',\n title: 'Flag: Comoros',\n },\n {\n emoji: '🇰🇳',\n title: 'Flag: St. Kitts & Nevis',\n },\n {\n emoji: '🇰🇵',\n title: 'Flag: North Korea',\n },\n {\n emoji: '🇰🇷',\n title: 'Flag: South Korea',\n },\n {\n emoji: '🇰🇼',\n title: 'Flag: Kuwait',\n },\n {\n emoji: '🇰🇾',\n title: 'Flag: Cayman Islands',\n },\n {\n emoji: '🇰🇿',\n title: 'Flag: Kazakhstan',\n },\n {\n emoji: '🇱🇦',\n title: 'Flag: Laos',\n },\n {\n emoji: '🇱🇧',\n title: 'Flag: Lebanon',\n },\n {\n emoji: '🇱🇨',\n title: 'Flag: St. Lucia',\n },\n {\n emoji: '🇱🇮',\n title: 'Flag: Liechtenstein',\n },\n {\n emoji: '🇱🇰',\n title: 'Flag: Sri Lanka',\n },\n {\n emoji: '🇱🇷',\n title: 'Flag: Liberia',\n },\n {\n emoji: '🇱🇸',\n title: 'Flag: Lesotho',\n },\n {\n emoji: '🇱🇹',\n title: 'Flag: Lithuania',\n },\n {\n emoji: '🇱🇺',\n title: 'Flag: Luxembourg',\n },\n {\n emoji: '🇱🇻',\n title: 'Flag: Latvia',\n },\n {\n emoji: '🇱🇾',\n title: 'Flag: Libya',\n },\n {\n emoji: '🇲🇦',\n title: 'Flag: Morocco',\n },\n {\n emoji: '🇲🇨',\n title: 'Flag: Monaco',\n },\n {\n emoji: '🇲🇩',\n title: 'Flag: Moldova',\n },\n {\n emoji: '🇲🇪',\n title: 'Flag: Montenegro',\n },\n {\n emoji: '🇲🇫',\n title: 'Flag: St. Martin',\n },\n {\n emoji: '🇲🇬',\n title: 'Flag: Madagascar',\n },\n {\n emoji: '🇲🇭',\n title: 'Flag: Marshall Islands',\n },\n {\n emoji: '🇲🇰',\n title: 'Flag: North Macedonia',\n },\n {\n emoji: '🇲🇱',\n title: 'Flag: Mali',\n },\n {\n emoji: '🇲🇲',\n title: 'Flag: Myanmar (Burma)',\n },\n {\n emoji: '🇲🇳',\n title: 'Flag: Mongolia',\n },\n {\n emoji: '🇲🇴',\n title: 'Flag: Macao Sar China',\n },\n {\n emoji: '🇲🇵',\n title: 'Flag: Northern Mariana Islands',\n },\n {\n emoji: '🇲🇶',\n title: 'Flag: Martinique',\n },\n {\n emoji: '🇲🇷',\n title: 'Flag: Mauritania',\n },\n {\n emoji: '🇲🇸',\n title: 'Flag: Montserrat',\n },\n {\n emoji: '🇲🇹',\n title: 'Flag: Malta',\n },\n {\n emoji: '🇲🇺',\n title: 'Flag: Mauritius',\n },\n {\n emoji: '🇲🇻',\n title: 'Flag: Maldives',\n },\n {\n emoji: '🇲🇼',\n title: 'Flag: Malawi',\n },\n {\n emoji: '🇲🇽',\n title: 'Flag: Mexico',\n },\n {\n emoji: '🇲🇾',\n title: 'Flag: Malaysia',\n },\n {\n emoji: '🇲🇿',\n title: 'Flag: Mozambique',\n },\n {\n emoji: '🇳🇦',\n title: 'Flag: Namibia',\n },\n {\n emoji: '🇳🇨',\n title: 'Flag: New Caledonia',\n },\n {\n emoji: '🇳🇪',\n title: 'Flag: Niger',\n },\n {\n emoji: '🇳🇫',\n title: 'Flag: Norfolk Island',\n },\n {\n emoji: '🇳🇬',\n title: 'Flag: Nigeria',\n },\n {\n emoji: '🇳🇮',\n title: 'Flag: Nicaragua',\n },\n {\n emoji: '🇳🇱',\n title: 'Flag: Netherlands',\n },\n {\n emoji: '🇳🇴',\n title: 'Flag: Norway',\n },\n {\n emoji: '🇳🇵',\n title: 'Flag: Nepal',\n },\n {\n emoji: '🇳🇷',\n title: 'Flag: Nauru',\n },\n {\n emoji: '🇳🇺',\n title: 'Flag: Niue',\n },\n {\n emoji: '🇳🇿',\n title: 'Flag: New Zealand',\n },\n {\n emoji: '🇴🇲',\n title: 'Flag: Oman',\n },\n {\n emoji: '🇵🇦',\n title: 'Flag: Panama',\n },\n {\n emoji: '🇵🇪',\n title: 'Flag: Peru',\n },\n {\n emoji: '🇵🇫',\n title: 'Flag: French Polynesia',\n },\n {\n emoji: '🇵🇬',\n title: 'Flag: Papua New Guinea',\n },\n {\n emoji: '🇵🇭',\n title: 'Flag: Philippines',\n },\n {\n emoji: '🇵🇰',\n title: 'Flag: Pakistan',\n },\n {\n emoji: '🇵🇱',\n title: 'Flag: Poland',\n },\n {\n emoji: '🇵🇲',\n title: 'Flag: St. Pierre & Miquelon',\n },\n {\n emoji: '🇵🇳',\n title: 'Flag: Pitcairn Islands',\n },\n {\n emoji: '🇵🇷',\n title: 'Flag: Puerto Rico',\n },\n {\n emoji: '🇵🇸',\n title: 'Flag: Palestinian Territories',\n },\n {\n emoji: '🇵🇹',\n title: 'Flag: Portugal',\n },\n {\n emoji: '🇵🇼',\n title: 'Flag: Palau',\n },\n {\n emoji: '🇵🇾',\n title: 'Flag: Paraguay',\n },\n {\n emoji: '🇶🇦',\n title: 'Flag: Qatar',\n },\n {\n emoji: '🇷🇪',\n title: 'Flag: Réunion',\n },\n {\n emoji: '🇷🇴',\n title: 'Flag: Romania',\n },\n {\n emoji: '🇷🇸',\n title: 'Flag: Serbia',\n },\n {\n emoji: '🇷🇺',\n title: 'Flag: Russia',\n },\n {\n emoji: '🇷🇼',\n title: 'Flag: Rwanda',\n },\n {\n emoji: '🇸🇦',\n title: 'Flag: Saudi Arabia',\n },\n {\n emoji: '🇸🇧',\n title: 'Flag: Solomon Islands',\n },\n {\n emoji: '🇸🇨',\n title: 'Flag: Seychelles',\n },\n {\n emoji: '🇸🇩',\n title: 'Flag: Sudan',\n },\n {\n emoji: '🇸🇪',\n title: 'Flag: Sweden',\n },\n {\n emoji: '🇸🇬',\n title: 'Flag: Singapore',\n },\n {\n emoji: '🇸🇭',\n title: 'Flag: St. Helena',\n },\n {\n emoji: '🇸🇮',\n title: 'Flag: Slovenia',\n },\n {\n emoji: '🇸🇯',\n title: 'Flag: Svalbard & Jan Mayen',\n },\n {\n emoji: '🇸🇰',\n title: 'Flag: Slovakia',\n },\n {\n emoji: '🇸🇱',\n title: 'Flag: Sierra Leone',\n },\n {\n emoji: '🇸🇲',\n title: 'Flag: San Marino',\n },\n {\n emoji: '🇸🇳',\n title: 'Flag: Senegal',\n },\n {\n emoji: '🇸🇴',\n title: 'Flag: Somalia',\n },\n {\n emoji: '🇸🇷',\n title: 'Flag: Suriname',\n },\n {\n emoji: '🇸🇸',\n title: 'Flag: South Sudan',\n },\n {\n emoji: '🇸🇹',\n title: 'Flag: São Tomé & Príncipe',\n },\n {\n emoji: '🇸🇻',\n title: 'Flag: El Salvador',\n },\n {\n emoji: '🇸🇽',\n title: 'Flag: Sint Maarten',\n },\n {\n emoji: '🇸🇾',\n title: 'Flag: Syria',\n },\n {\n emoji: '🇸🇿',\n title: 'Flag: Eswatini',\n },\n {\n emoji: '🇹🇦',\n title: 'Flag: Tristan Da Cunha',\n },\n {\n emoji: '🇹🇨',\n title: 'Flag: Turks & Caicos Islands',\n },\n {\n emoji: '🇹🇩',\n title: 'Flag: Chad',\n },\n {\n emoji: '🇹🇫',\n title: 'Flag: French Southern Territories',\n },\n {\n emoji: '🇹🇬',\n title: 'Flag: Togo',\n },\n {\n emoji: '🇹🇭',\n title: 'Flag: Thailand',\n },\n {\n emoji: '🇹🇯',\n title: 'Flag: Tajikistan',\n },\n {\n emoji: '🇹🇰',\n title: 'Flag: Tokelau',\n },\n {\n emoji: '🇹🇱',\n title: 'Flag: Timor-Leste',\n },\n {\n emoji: '🇹🇲',\n title: 'Flag: Turkmenistan',\n },\n {\n emoji: '🇹🇳',\n title: 'Flag: Tunisia',\n },\n {\n emoji: '🇹🇴',\n title: 'Flag: Tonga',\n },\n {\n emoji: '🇹🇷',\n title: 'Flag: Turkey',\n },\n {\n emoji: '🇹🇹',\n title: 'Flag: Trinidad & Tobago',\n },\n {\n emoji: '🇹🇻',\n title: 'Flag: Tuvalu',\n },\n {\n emoji: '🇹🇼',\n title: 'Flag: Taiwan',\n },\n {\n emoji: '🇹🇿',\n title: 'Flag: Tanzania',\n },\n {\n emoji: '🇺🇦',\n title: 'Flag: Ukraine',\n },\n {\n emoji: '🇺🇬',\n title: 'Flag: Uganda',\n },\n {\n emoji: '🇺🇲',\n title: 'Flag: U.S. Outlying Islands',\n },\n {\n emoji: '🇺🇳',\n title: 'Flag: United Nations',\n },\n {\n emoji: '🇺🇸',\n title: 'Flag: United States',\n },\n {\n emoji: '🇺🇾',\n title: 'Flag: Uruguay',\n },\n {\n emoji: '🇺🇿',\n title: 'Flag: Uzbekistan',\n },\n {\n emoji: '🇻🇦',\n title: 'Flag: Vatican City',\n },\n {\n emoji: '🇻🇨',\n title: 'Flag: St. Vincent & Grenadines',\n },\n {\n emoji: '🇻🇪',\n title: 'Flag: Venezuela',\n },\n {\n emoji: '🇻🇬',\n title: 'Flag: British Virgin Islands',\n },\n {\n emoji: '🇻🇮',\n title: 'Flag: U.S. Virgin Islands',\n },\n {\n emoji: '🇻🇳',\n title: 'Flag: Vietnam',\n },\n {\n emoji: '🇻🇺',\n title: 'Flag: Vanuatu',\n },\n {\n emoji: '🇼🇫',\n title: 'Flag: Wallis & Futuna',\n },\n {\n emoji: '🇼🇸',\n title: 'Flag: Samoa',\n },\n {\n emoji: '🇽🇰',\n title: 'Flag: Kosovo',\n },\n {\n emoji: '🇾🇪',\n title: 'Flag: Yemen',\n },\n {\n emoji: '🇾🇹',\n title: 'Flag: Mayotte',\n },\n {\n emoji: '🇿🇦',\n title: 'Flag: South Africa',\n },\n {\n emoji: '🇿🇲',\n title: 'Flag: Zambia',\n },\n {\n emoji: '🇿🇼',\n title: 'Flag: Zimbabwe',\n },\n {\n emoji: '🏴󠁧󠁢󠁥󠁮󠁧󠁿',\n title: 'Flag: England',\n },\n {\n emoji: '🏴󠁧󠁢󠁳󠁣󠁴󠁿',\n title: 'Flag: Scotland',\n },\n {\n emoji: '🏴󠁧󠁢󠁷󠁬󠁳󠁿',\n title: 'Flag: Wales',\n },\n {\n emoji: '🏴󠁵󠁳󠁴󠁸󠁿',\n title: 'Flag for Texas (US-TX)',\n },\n ],\n};\n\nState.init({\n emojiPopup: true,\n selected: 0,\n hoverStateHeader: -1,\n hoveredEmoji: null,\n});\n\nconst updateHoveredEmoji = (emoji) => {\n State.update({ hoveredEmoji: emoji });\n};\nconst updateHoverStateHeader = (id) => {\n State.update({\n hoverStateHeader: id,\n });\n};\n\nconst CloseEmojiPopup = () =>\n State.update({\n emojiPopup: false,\n });\n\nconst updateSelectedType = (id) => {\n State.update({\n selected: id,\n });\n};\n\nconst TypeSelectorHeader = styled.div`\n display: flex;\n border-top-left-radius: 0.375rem;\n border-top-right-radius: 0.375rem;\n padding-left: 4px;\n pading-right: 4px;\n border-bottom: 1px solid #777583;\n`;\nconst EmojiComponentContainer = styled.div`\n width: fit-content;\n height: 420px;\n background-color: #0e0e10;\n border: 1px solid #777583;\n border-radius: 0.375rem;\n @media (max-width: 1024px) {\n height: 362px;\n }\n`;\n\nconst IconContainer = styled.div`\n width: 32px;\n height: 32px;\n padding: 8px;\n display: flex;\n justify-content: center;\n align-items: center;\n cursor: pointer;\n ${({ selected }) => selected && 'border-bottom: 1px solid #009900;'}\n transition: background-color 500ms ease\n`;\n\nconst IconContainerClose = styled.div`\n width: 32px;\n height: 32px;\n padding: 8px;\n display: flex;\n justify-content: center;\n align-items: center;\n background-color: #fa932b;\n :hover {\n background-color: #ffac58;\n }\n cursor: pointer;\n`;\n\nconst SelectedTypeTitle = styled.div`\n font-style: normal;\n font-weight: 600;\n line-height: normal;\n font-size: 12px;\n padding-left: 24px;\n margin-top: 0.725rem;\n color: #777583;\n`;\n\nconst types = [\n <i className=\"bi bi-emoji-smile text-light\"></i>,\n <i className=\"bi bi-tree text-light\"></i>,\n <i className=\"bi bi-egg-fill text-light\"></i>,\n <i className=\"bi bi-controller text-light\"></i>,\n <i className=\"bi bi-globe-americas text-light\"></i>,\n <i className=\"bi bi-lamp-fill text-light\"></i>,\n <i className=\"bi bi-shield text-light\"></i>,\n <i className=\"bi bi-flag-fill text-light\"></i>,\n];\n\nconst typesNames = [\n 'People',\n 'Nature',\n 'Food and Drinks',\n 'Activity',\n 'Travel Places',\n 'Objects',\n 'Symbols',\n 'Flags',\n];\n\nconst typesIds = [\n 'People',\n 'Nature',\n 'Food-drink',\n 'Activity',\n 'Travel-places',\n 'Objects',\n 'Symbols',\n 'Flags',\n];\n\nconst EmojiGrid = styled.div`\n height: 300px;\n display: grid;\n grid-template-columns: repeat(7, 32px);\n padding-top: 0.5rem;\n padding-left: 16px;\n padding-right: 8px:\n column-gap: 2px;\n row-gap: 2px;\n overflow: scroll;\n`;\n\nconst EmojiItemDesktop = styled.div`\n cursor: pointer;\n text-align: center;\n padding-bottom: 5px;\n padding-top: 5px;\n height: 30px;\n width: 30px;\n border-radius: 0.375rem;\n :hover {\n background-color: #34343a;\n }\n @media (max-width: 1024px) {\n display: none;\n }\n`;\n\nconst EmojiItemMobile = styled.div`\n cursor: pointer;\n text-align: center;\n padding-bottom: 5px;\n padding-top: 5px;\n height: 30px;\n width: 30px;\n border-radius: 0.375rem;\n :hover {\n background-color: #34343a;\n }\n @media (min-width: 1024px) {\n display: none;\n }\n`;\n\nconst Page = styled.div`\n background-color: #bf4f74;\n padding: 4rem;\n`;\n\nconst DisplayEmojiGrid = styled.div`\n display: flex;\n align-items: center;\n column-gap: 4px;\n width: 100%;\n height: 58px;\n border-bottom-left-radius: 0.375rem;\n border-bottom-right-radius: 0.375rem;\n border-top: 1px solid #777583;\n font-style: normal;\n font-weight: 600;\n padding-top: 1rem;\n line-height: normal;\n font-size: 12px;\n color: #777583;\n padding-left: 8px;\n padding-right: 8px;\n overflow: hidden;\n @media (max-width: 1024px) {\n display: none;\n }\n`;\n\nconst EmojiDisplay = styled.div`\n display: flex;\n align-items: center;\n overflow: hidden;\n font-style: normal;\n font-weight: 600;\n line-height: normal;\n font-size: 12px;\n color: #777583;\n`;\n\nreturn (\n <EmojiComponentContainer>\n <TypeSelectorHeader>\n {types.map((icon, id) => {\n return (\n <IconContainer\n selected={id === state.selected || id === state.hoverStateHeader}\n onClick={() => updateSelectedType(id)}\n key={`types-${id}`}\n onMouseEnter={() => updateHoverStateHeader(id)}\n onMouseLeave={() => updateHoverStateHeader(-1)}\n >\n {icon}\n </IconContainer>\n );\n })}\n </TypeSelectorHeader>\n <SelectedTypeTitle>{typesNames[state.selected]}</SelectedTypeTitle>\n <EmojiGrid>\n {emojiObj[typesIds[state.selected]].map((item, id) => (\n <div key={id}>\n <EmojiItemDesktop\n key={`desktop-${id}`}\n onMouseEnter={() => updateHoveredEmoji(item)}\n onMouseLeave={() => updateHoveredEmoji(undefined)}\n onClick={() => {\n props.OnEmojiSelected(item.emoji);\n }}\n >\n <p>{item.emoji}</p>\n </EmojiItemDesktop>\n <EmojiItemMobile\n key={`mobile-${id}`}\n onClick={() => {\n props.OnEmojiSelected(item.emoji);\n }}\n >\n <p>{item.emoji}</p>\n </EmojiItemMobile>\n </div>\n ))}\n </EmojiGrid>\n <DisplayEmojiGrid>\n <p>Emoji: </p>\n {state.hoveredEmoji && (\n <EmojiDisplay>\n <p>{state.hoveredEmoji.emoji}</p>\n <p>{state.hoveredEmoji.title}</p>\n </EmojiDisplay>\n )}\n </DisplayEmojiGrid>\n </EmojiComponentContainer>\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.CommentInput": { "": "const Container = styled.div`\n width: 100%\n display: flex;\n flex-direction: column;\n justify-content: start;\n align-items: center;\n`;\n\nconst ButtonContainer = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: start;\n align-items: center;\n gap: 10px;\n margin-top: 8px;\n`;\n\nconst InputCommentField = styled.input`\n color: #fff;\n height: 40px;\n width: 100%;\n padding: 8px 16px 8px 16px;\n border-radius: 6px;\n background-color: #0e0e10;\n border: none;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n`;\n\nconst FunctionButton = styled.button`\n ${({ backgroundColor }) =>\n backgroundColor && `background-color: ${backgroundColor};`}\n ${({ color }) => (color ? `color: ${color};` : 'color: #fff;')}\n :hover {\n ${({ hoverColor }) => hoverColor && `background-color: ${hoverColor};`}\n ${({ hoverOutline }) =>\n hoverOutline &&\n `outline-color: ${hoverOutline};\n outline-style: solid;\n outline-width: 1px;`}\n }\n border-radius: 4px;\n padding: 8px 16px 8px 16px;\n border: none;\n`;\n\nconst [comment, setComment] = useState('');\nconst [buttonVisible, setButtonsVisible] = useState(false);\n\nconst handleCommentChange = (e) => {\n setButtonsVisible(true);\n setComment(e.target.value);\n};\n\nconst handleOnClose = () => {\n setButtonsVisible(false);\n setComment('');\n};\n\nconst handlePostComment = useCallback(() => {\n props.addComment(comment);\n setComment('');\n setButtonsVisible(false);\n}, [comment, props.addComment]);\n\nreturn (\n <Container>\n <InputCommentField\n value={comment}\n onClick={() => setButtonsVisible(true)}\n onChange={handleCommentChange}\n placeholder=\"Add a comment\"\n autoFocus\n />\n {buttonVisible && (\n <ButtonContainer>\n <FunctionButton\n backgroundColor=\"#D0FC42\"\n color=\"black\"\n hoverColor=\"#BBE33B\"\n onClick={handlePostComment}\n >\n Post\n </FunctionButton>\n <FunctionButton\n backgroundColor=\"transparent\"\n color=\"#fff\"\n hoverOutline=\"#D0FC42\"\n onClick={handleOnClose}\n >\n Cancel\n </FunctionButton>\n </ButtonContainer>\n )}\n </Container>\n);\n" }, "Calimero.DocsChain.Authors": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst allAuthors = Near.calimeroView(contract, 'get_accounts_paged', {});\nconsole.log('AUTHORS');\nconsole.log(allAuthors);\n\nreturn (\n <>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.MainNavigation'}\n config={redirectConfig}\n props={{ currentNavPill: 'authors' }}\n />\n <h6>Total authors: {allAuthors.length}</h6>\n <ul>\n {allAuthors.map((data) => (\n <li>\n <a\n href={transformUrl(\n `https://near.social/#/mob.near/widget/ProfilePage?accountId=${author}`,\n )}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {data[0]}\n </a>{' '}\n -\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.ArticlesByAuthor?author=${data[0]}`,\n )}\n >\n {data[1].articles.length}\n </a>\n </li>\n ))}\n </ul>\n </>\n);\n" }, "Calimero.DocsChain.ArticlesByAuthor": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst author = props.author;\n\nconst getDateLastEdit = (timestamp) => {\n const date = new Date(Number(timestamp) / 1e6);\n const dateString = `${date.toLocaleDateString()} / ${date.toLocaleTimeString()}`;\n return dateString;\n};\n\nconst allArticleIds = Near.calimeroView(contract, 'get_article_ids_paged', {});\nconsole.log(allArticleIds);\n\nconst allArticles = allArticleIds\n .map((articleId) => {\n return {\n id: articleId,\n data: Near.calimeroView(contract, 'get_article', {\n article_id: articleId,\n }),\n };\n })\n .filter((article) => article.data.author == author);\nconsole.log(allArticles);\n\nreturn (\n <ol>\n {allArticles &&\n allArticles.map((article) => (\n <li key={article.id}>\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.ArticleView?articleId=${article.id}&blockHeight=${article.data.blockHeight}&lastEditor=${article.data.lastEditor}`,\n )}\n >\n {article.id}{' '}\n <small>\n (author: {article.data.author}\n {getDateLastEdit(article.data.timestamp)})\n </small>\n </a>\n </li>\n ))}\n </ol>\n);\n" }, "Calimero.Curb.Chat.ChatDisplaySplit": { "": "const componentOwnerId = props.componentOwnerId;\nconst readMessage = props.readMessage;\nconst handleReaction = props.handleReaction;\nconst openThread = props.openThread;\nconst setOpenThread = props.setOpenThread;\nconst activeChat = props.activeChat;\nconst curbApi = props.curbApi;\nconst accountId = props.accountId;\nconst incomingMessages = props.incomingMessages;\nconst updatedMessages = props.updatedMessages;\nconst resetImage = props.resetImage;\nconst sendMessage = props.sendMessage;\nconst getIconFromCache = props.getIconFromCache;\nconst isThread = props.isThread;\nconst isReadOnly = props.isReadOnly;\nconst toggleEmojiSelector = props.toggleEmojiSelector;\nconst isEmojiSelectorVisible = props.isEmojiSelectorVisible;\nconst channelMeta = props.channelMeta;\nconst channelUserList = props.channelUserList;\nconst setOpenMobileReactions = props.setOpenMobileReactions;\nconst openMobileReactions = props.openMobileReactions;\nconst onMessageDeletion = props.onMessageDeletion;\nconst onEditModeRequested = props.onEditModeRequested;\nconst onEditModeCancelled = props.onEditModeCancelled;\nconst onMessageUpdated = props.onMessageUpdated;\n\nconst ContainerPadding = styled.div`\n @media (max-width: 1024px) {\n height: calc(100vh - 160px) !important;\n padding-left: 0px !important;\n padding-right: 0px !important;\n }\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst ThreadTitle = styled.div`\n color: #fff;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%;\n`;\n\nconst ThreadContainer = styled.div`\n position: relative;\n padding-bottom: 20px;\n border-bottom: 2px solid #282933;\n display: flex;\n justify-content: space-between;\n @media (max-width: 1024px) {\n margin-right: 16px;\n padding-top: 104px;\n }\n`;\n\nconst CloseSvg = styled.svg`\n fill: #777583;\n :hover {\n fill: #fff;\n }\n cursor: pointer;\n`;\n\nconst Wrapper = styled.div`\n @media (max-width: 1024px) {\n width: 100% !important;\n }\n`;\n\nconst containerPaddingStyle = {\n display: 'flex',\n flexDirection: 'row',\n paddingTop: '1rem',\n paddingLeft: '2.5rem',\n paddingRight: '2.5rem',\n paddingBottom: '2.5rem',\n scrollBehavior: 'smooth',\n height: 'calc(100vh - 241px)',\n};\nconst chatStyle = {\n height: '',\n width: '',\n};\n\nconst wrapperStyle = {\n height: '100%',\n width: '100%',\n};\n\nconst CloseButtonSvg = ({ onClose }) => (\n <CloseSvg\n onClick={onClose}\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n className=\"bi bi-x-circle\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z\" />\n <path d=\"M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z\" />\n </CloseSvg>\n);\n\nconst ThreadHeader = ({ onClose }) => (\n <ThreadContainer>\n <ThreadTitle>Thread</ThreadTitle>\n <CloseButtonSvg onClose={onClose} />\n </ThreadContainer>\n);\n\nconst loadInitialMessages = () => {\n if (isThread && openThread.id) {\n return curbApi.fetchMessages({\n chat: activeChat,\n limit: 20,\n parentMessageId: openThread.id,\n });\n }\n return curbApi.fetchMessages({ chat: activeChat, limit: 20 });\n};\n\nconst loadPrevMessages = (id) => {\n if (isThread && id) {\n return curbApi.fetchMessages({\n chat: activeChat,\n beforeId: id,\n limit: 20,\n parentMessageId: openThread.id,\n });\n }\n return curbApi.fetchMessages({ chat: activeChat, beforeId: id, limit: 20 });\n};\n\nconst setThread = useCallback((message) => {\n setOpenThread(message);\n}, []);\n\nconst isModerator = useMemo(\n () =>\n channelUserList?.some(\n (user) => user.id === accountId && user.moderator === true,\n ),\n [channelUserList, accountId],\n);\n\nconst isOwner = accountId === channelMeta.createdBy;\n\nconst renderMessage = useMemo(\n () =>\n createMessageRenderer({\n handleReaction,\n getIconFromCache,\n accountId,\n isThread,\n openMobileReactions,\n setOpenMobileReactions,\n setThread: setThread ? setThread : undefined,\n toggleEmojiSelector,\n onEditModeRequested: onEditModeRequested,\n onEditModeCancelled: onEditModeCancelled,\n onMessageUpdated: onMessageUpdated,\n editable: () => false,\n deleteable: (message) => {\n if (message.sender === accountId) {\n return true;\n }\n return isOwner || isModerator;\n },\n onDeleteMessageRequested: (message) => {\n onMessageDeletion(message);\n },\n }),\n [accountId, isOwner, isModerator, openMobileReactions],\n);\n\nif (openThread && isThread) {\n chatStyle.height = 'calc(100% - 124px)';\n chatStyle.width = '100%';\n chatStyle['overflow'] = 'hidden';\n containerPaddingStyle.flexDirection = 'column';\n containerPaddingStyle.paddingLeft = '0px';\n containerPaddingStyle.height = '100%';\n containerPaddingStyle['width'] = '100%';\n wrapperStyle.width = '100%';\n} else if (openThread && !isThread) {\n chatStyle.height = '100%';\n chatStyle.width = '100%';\n containerPaddingStyle.paddingRight = '0px';\n wrapperStyle.width = '60%';\n} else {\n chatStyle.height = '100%';\n chatStyle.width = '100%';\n chatStyle['overflow'] = 'hidden';\n}\n\nconst EmojiPopupContainer = styled.div`\n position: absolute;\n bottom: 70px;\n right: 2.5rem;\n`;\n\nreturn (\n <Wrapper style={wrapperStyle}>\n <ContainerPadding style={containerPaddingStyle}>\n {openThread && isThread && (\n <ThreadHeader\n openThread={openThread}\n onClose={() => setOpenThread(undefined)}\n />\n )}\n <VirtualizedChat\n loadInitialMessages={loadInitialMessages}\n loadPrevMessages={loadPrevMessages}\n deletedMessages={deletedMessages}\n incomingMessages={incomingMessages}\n updatedMessages={updatedMessages}\n onItemNewItemRender={readMessage}\n shouldTriggerNewItemIndicator={(message) =>\n message.sender !== accountId\n }\n render={renderMessage}\n chatId={isThread ? openThread.id : activeChat} // re-render the chat when the chat/thread changes\n style={chatStyle}\n />\n </ContainerPadding>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.MessageInput`}\n props={{\n componentOwnerId,\n selectedChat:\n activeChat.type === 'channel' ? activeChat.name : activeChat.id,\n sendMessage: sendMessage,\n resetImage: resetImage,\n openThread,\n isThread,\n isReadOnly,\n isOwner,\n isModerator,\n }}\n />\n </Wrapper>\n);\n" }, "Calimero.Curb.Chat.ChatContainer": { "": "const componentOwnerId = props.componentOwnerId;\nconst contract = props.contract;\nconst curbApi = props.curbApi;\nconst activeChat = props.activeChat;\nconst accountId = props.accountId;\nconst wsApi = props.wsApi;\nconst isWsConnectionActive = props.isWsConnectionActive;\n\nconst ChatContainer = styled.div`\n display: flex;\n @media (min-width: 1025px) {\n padding-left: 4px;\n height: calc(100vh - 169px);\n }\n @media (max-width: 1024px) {\n display: flex;\n flex-direction: column;\n padding-top: 104px;\n }\n`;\n\nconst ThreadWrapper = styled.div`\n height: 100%;\n flex: 1;\n border-left: 2px solid #282933;\n padding-left: 20px;\n @media (max-width: 1024px) {\n border-left: none;\n position: fixed;\n left: 0;\n right: 0; /* Set both left and right to 0 to stretch to full width */\n top: 50%; /* Vertically center the element */\n transform: translateY(-50%); /* Center it vertically */\n z-index: 30;\n background-color: #0e0e10;\n }\n`;\n\nconst [openThread, setOpenThread] = useState(undefined);\nconst [incomingMessages, setIncomingMessages] = useState([]);\nconst [updatedMessages, setUpdatedMessages] = useState([]);\nconst [incomingThreadMessages, setIncomingThreadMessages] = useState([]);\nconst [updatedThreadMessages, setUpdatedThreadMessages] = useState([]);\nconst [isEmojiSelectorVisible, setIsEmojiSelectorVisible] = useState(false);\nconst [messageWithEmojiSelector, setMessageWithEmojiSelector] = useState(false);\nconst [channelMeta, setChannelMeta] = useState({});\nconst [channelUserList, setChannelUserList] = useState([]);\n\nconst [openMobileReactions, setOpenMobileReactions] = useState('');\n\nuseEffect(() => {\n if (activeChat.type === 'channel') {\n curbApi.getChannelMeta(activeChat.name).then(setChannelMeta);\n curbApi.getChannelMembers(activeChat.name).then(setChannelUserList);\n }\n}, [activeChat]);\n\nconst toggleEmojiSelector = useCallback(\n (message) => {\n setMessageWithEmojiSelector(message);\n setIsEmojiSelectorVisible((prev) => !prev);\n },\n [setIsEmojiSelectorVisible],\n);\n\nconst activeChatRef = useRef(activeChat);\nconst openThreadRef = useRef(openThread);\n\nuseEffect(() => {\n if (activeChat.type === 'channel' && isWsConnectionActive) {\n // todo! move this to page initialization\n // todo! basically, once the page has loaded,\n // todo! and we've fetched channels, subscribe to them\n wsApi.methods.subscribe(\n { [contract]: [activeChat.name] },\n (err, result) => {\n if (err) return console.log('error subscribing to channel', err);\n console.log('subscribed to', activeChat, err, result);\n curbApi.emit('subscribed', activeChat.name);\n },\n );\n }\n activeChatRef.current = activeChat;\n}, [activeChat, isWsConnectionActive]);\n\nuseEffect(() => {\n setOpenThread(undefined);\n}, [activeChat]);\n\nuseEffect(() => {\n openThreadRef.current = openThread;\n}, [openThread]);\n\nconst computeReaction = useCallback((message, reaction, sender) => {\n const accounts = message.reactions[reaction] ?? [];\n let update;\n if (accounts.includes(sender)) {\n update = accounts.filter((a) => a !== sender);\n } else {\n update = [...accounts, sender];\n }\n return { reactions: { ...message.reactions, [reaction]: update } };\n}, []);\n\nuseEffect(() => {\n const messageListener = (event) => {\n if (\n (activeChatRef.current.type === 'direct_message' &&\n event.sender === activeChatRef.current.id) ||\n (activeChatRef.current.type === 'channel' &&\n event.receiver.type === 'channel' &&\n event.receiver.name === activeChatRef.current.name)\n ) {\n curbApi\n .fetchKey({ chat: activeChatRef.current })\n .then((key) => {\n const decryptedMessages = curbApi.decryptMessages([event], key);\n const newChannelMessages = [];\n const openedThreadMessages = [];\n //record of keys which are id of parent messages and number of new messages that represents addition on threadsCount\n const notOpenedThreadsMessagesSet = {};\n //for each decrypted message check if it is in channel or in thread\n decryptedMessages.map((message) => {\n //thread message\n if (message.parent_message) {\n if (message.parent_message === openThreadRef.current.id) {\n //currently opened thread message\n openedThreadMessages.push(message);\n }\n //update threads count\n if (message.parent_message in notOpenedThreadsMessagesSet) {\n notOpenedThreadsMessagesSet[message.parent_message]++;\n } else {\n // If key doesn't exist, initialize with 1\n notOpenedThreadsMessagesSet[message.parent_message] = 1;\n }\n } else {\n //channel message\n newChannelMessages.push(message);\n }\n });\n\n //update messages count in closed threads\n Object.entries(notOpenedThreadsMessagesSet).forEach(\n ([key, value]) => {\n const parentUpdate = [\n {\n id: key,\n descriptor: {\n updateFunction: (message) => ({\n threadCount: message.threadCount + value,\n threadLastTimestamp: Date.now(),\n }),\n },\n },\n ];\n setUpdatedMessages(parentUpdate);\n },\n );\n\n //update messages in opened thread\n if (openedThreadMessages.length > 0) {\n console.log('update messages in opened thread');\n setOpenThread({\n ...openThreadRef.current,\n threadCount: openThreadRef.current.threadCount + 1,\n });\n setIncomingThreadMessages(openedThreadMessages);\n }\n\n if (newChannelMessages.length > 0) {\n //update messages outside thread\n setIncomingMessages(newChannelMessages);\n }\n })\n .catch((err) => {\n console.log('Failing to decrypt message', err);\n });\n }\n };\n const reactionListener = (event) => {\n const { message_id, reaction, sender } = event;\n const updateFunction = (message) =>\n computeReaction(message, reaction, sender);\n setUpdatedMessages([{ id: message_id, descriptor: { updateFunction } }]);\n };\n\n const editsListener = (event) => {\n console.log('edited message', event);\n if (\n (activeChatRef.current.type === 'direct_message' &&\n event.sender === activeChatRef.current.id) ||\n (activeChatRef.current.type === 'channel' &&\n event.receiver.type === 'channel' &&\n event.receiver.name === activeChatRef.current.name)\n ) {\n curbApi\n .fetchKey({ chat: activeChatRef.current })\n .then((key) => {\n const updates = curbApi.deleteMessage([event], key).map((m) => ({\n id: m.id,\n descriptor: {\n updatedFields: { ...m },\n },\n }));\n if (event.parent_message === openThreadRef.current.id) {\n console.log('updating thread messages', updates);\n setUpdatedThreadMessages(updates);\n } else {\n console.log('updating messages', updates);\n // TODO update thread parent if required\n setUpdatedMessages(updates);\n }\n })\n .catch((err) => {\n console.log('Failing to decrypt message', err);\n });\n }\n };\n\n const deleteListener = (event) => {\n if (\n (activeChatRef.current.type === 'direct_message' &&\n event.sender === activeChatRef.current.id) ||\n (activeChatRef.current.type === 'channel' &&\n event.receiver.type === 'channel' &&\n event.receiver.name === activeChatRef.current.name)\n ) {\n handleDeleteMessage(event);\n }\n };\n\n wsApi.notifications\n .on('ChannelMessage', messageListener)\n .on('MessageReaction', reactionListener)\n .on('EditedMessage', editsListener)\n .on('DeletedMessage', deleteListener);\n return () => {\n wsApi.notifications\n .off('ChannelMessage', messageListener)\n .off('MessageReaction', reactionListener)\n .off('EditedMessage', editsListener)\n .off('DeletedMessage', deleteListener);\n };\n}, []);\n\nconst readMessage = useCallback(\n (message) => {\n if (message?.id && message?.sender !== accountId) {\n // we don't report our own messages\n curbApi.readMessage({\n chat: activeChatRef.current,\n messageId: message.id,\n });\n }\n },\n [curbApi, accountId],\n);\n\nconst handleReaction = useCallback(\n (message, reaction) => {\n const updates = [\n {\n id: message.id,\n descriptor: {\n updateFunction: (message) =>\n computeReaction(message, reaction, accountId),\n },\n },\n ];\n setUpdatedMessages(updates);\n setUpdatedThreadMessages(updates);\n curbApi.toggleReaction({ messageId: message.id, reaction });\n },\n [curbApi],\n);\n\nconst convertCidToUrl = (file) =>\n file\n ? { ...file, ipfs_cid: `https://ipfs.near.social/ipfs/${file.cid}` }\n : null;\n\nconst createTemporalMessage = (text, img, uploadedFile, parentMessage) => {\n const images = img?.file ? [convertCidToUrl(img.file)] : [];\n const files = uploadedFile?.file ? [convertCidToUrl(uploadedFile.file)] : [];\n const temporalId = curbApi.utils.toHexString(Crypto.randomBytes(16));\n\n return {\n id: temporalId,\n nonce: curbApi.utils.toHexString(Crypto.randomBytes(16)),\n reactions: [],\n images,\n files,\n sender: accountId,\n text,\n timestamp: Date.now(),\n temporalId,\n parent_message: parentMessage?.id,\n };\n};\n\nconst submitMessage = (chat, message) =>\n new Promise((resolve, reject) => {\n curbApi.sendMessage(\n {\n message: message.text,\n chat,\n images: message.images,\n files: message.files,\n threadId: message.parent_message ? message.parent_message : undefined,\n },\n (err, result) => {\n if (result) {\n resolve(result.result.id);\n } else {\n reject(err);\n }\n },\n );\n });\n\nconst createSendMessageHandler =\n (chat, parentMessage) => (text, img, uploadedFile) => {\n const temporalMessage = createTemporalMessage(\n text,\n img,\n uploadedFile,\n parentMessage,\n );\n\n const incomingCall = parentMessage\n ? setIncomingThreadMessages\n : setIncomingMessages;\n incomingCall([temporalMessage]);\n\n submitMessage(chat, temporalMessage)\n .then((realMessageId) => {\n const update = [\n {\n id: temporalMessage.id,\n descriptor: {\n updatedFields: { id: realMessageId, temporalId: undefined },\n },\n },\n ];\n\n if (parentMessage) {\n const parentUpdate = [\n {\n id: parentMessage.id,\n descriptor: {\n updateFunction: (message) => ({\n threadCount: message.threadCount + 1,\n threadLastTimestamp: Date.now(),\n }),\n },\n },\n ];\n setUpdatedThreadMessages(update);\n setUpdatedMessages(parentUpdate);\n } else {\n setUpdatedMessages(update);\n }\n })\n .catch((err) => {\n console.log(err);\n });\n };\n\nconst sendMessage = useMemo(\n () => createSendMessageHandler(activeChat),\n [activeChat],\n);\nconst sendThreadMessage = useMemo(\n () => createSendMessageHandler(activeChat, openThread),\n [activeChat, openThread],\n);\n\nconst getIconFromCache = useCallback((accountId) => {\n const fallbackImage = 'https://i.imgur.com/e8buxpa.png';\n return new Promise((resolve, _reject) => {\n const profile = Social.getr(`${accountId}/profile`);\n const baseImageObject = profile.image;\n if (\n !profile &&\n !baseImageObject.url &&\n !baseImageObject.ipfs_cid &&\n !baseImageObject.nft\n ) {\n resolve(fallbackImage);\n } else {\n resolve(\n `https://i.near.social/magic/thumbnail/https://near.social/magic/img/account/${accountId}`,\n );\n }\n });\n}, []);\n\nconst updateDeletedMessage = ({ id, parent_message }) => {\n const update = [{ id, descriptor: { updatedFields: { deleted: true } } }];\n if (parent_message) {\n const parentUpdate = [\n {\n id: parent_message,\n descriptor: {\n updateFunction: (message) => ({\n threadCount: message.threadCount - 1,\n }),\n },\n },\n ];\n setUpdatedThreadMessages(update);\n setUpdatedMessages(parentUpdate);\n } else {\n if (id === openThreadRef?.current?.id) {\n setOpenThread(undefined);\n }\n setUpdatedMessages(update);\n }\n};\n\nconst handleDeleteMessage = useCallback(\n (message) => {\n updateDeletedMessage(message);\n curbApi.deleteMessage({ message, chat: activeChatRef.current });\n },\n [curbApi, activeChat],\n);\n\nconst handleEditMode = useCallback((message) => {\n console.log('edit mode', message);\n const update = [\n {\n id: message.id,\n descriptor: {\n updatedFields: { editMode: !message.editMode },\n },\n },\n ];\n setUpdatedMessages(update);\n}, []);\n\nconst handleEditedMessage = useCallback(\n (message) => {\n curbApi.editMessage({ message, chat: activeChat });\n const update = [\n {\n id: message.id,\n descriptor: {\n updatedFields: {\n text: message.text,\n editMode: false,\n },\n },\n },\n ];\n setUpdatedMessages(update);\n },\n [activeChat, curbApi],\n);\n\nconst handleEditedThreadMessage = useCallback(\n (message) => {\n curbApi.editMessage({ message, chat: activeChat });\n const update = [\n {\n id: message.id,\n descriptor: {\n updatedFields: {\n text: message.text,\n editMode: false,\n },\n },\n },\n ];\n setUpdatedThreadMessages(update);\n },\n [activeChat, curbApi],\n);\n\nconst selectThread = useCallback((message) => {\n console.log('select thread', message);\n setOpenThread(message);\n // reset thread updates\n setIncomingThreadMessages([]);\n setUpdatedThreadMessages([]);\n}, []);\n\nreturn (\n <ChatContainer>\n <>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.ChatDisplaySplit`}\n props={{\n componentOwnerId,\n readMessage,\n handleReaction,\n openThread,\n setOpenThread: selectThread,\n activeChat,\n accountId,\n curbApi,\n incomingMessages,\n updatedMessages,\n resetImage: props.resetImage,\n sendMessage,\n getIconFromCache,\n isThread: false,\n isReadOnly: activeChat.readOnly,\n toggleEmojiSelector,\n channelMeta,\n channelUserList,\n openMobileReactions,\n setOpenMobileReactions,\n onMessageDeletion: handleDeleteMessage,\n onEditModeRequested: handleEditMode,\n onEditModeCancelled: handleEditMode,\n onMessageUpdated: handleEditedMessage,\n }}\n />\n </>\n {openThread.id && (\n <ThreadWrapper>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.ChatDisplaySplit`}\n props={{\n componentOwnerId,\n readMessage,\n handleReaction,\n openThread,\n setOpenThread: selectThread,\n activeChat,\n accountId,\n curbApi,\n incomingMessages: incomingThreadMessages,\n updatedMessages: updatedThreadMessages,\n resetImage: props.resetImage,\n sendMessage: sendThreadMessage,\n getIconFromCache,\n isThread: true,\n isReadOnly: activeChat.readOnly,\n toggleEmojiSelector,\n channelMeta,\n channelUserList,\n openMobileReactions,\n setOpenMobileReactions,\n onMessageDeletion: handleDeleteMessage,\n onEditModeRequested: handleEditMode,\n onEditModeCancelled: handleEditMode,\n onMessageUpdated: handleEditedThreadMessage,\n }}\n />\n </ThreadWrapper>\n )}\n {isEmojiSelectorVisible && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.EmojiSelector.EmojiSelectorPopup`}\n props={{\n OnEmojiSelected: (emoji) => {\n handleReaction(messageWithEmojiSelector, emoji);\n setIsEmojiSelectorVisible(false);\n },\n onClose: () => setIsEmojiSelectorVisible(false),\n componentOwnerId,\n }}\n key=\"chat-emojis-component\"\n />\n )}\n </ChatContainer>\n);\n" }, "Calimero.Curb.SideSelector.UserItem": { "": "const UserListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n :hover {\n color: #ffffff;\n }\n :hover {\n background-color: #25252a;\n }\n cursor: pointer;\n ${({ selected }) =>\n selected\n ? 'color: #fff; background-color: #25252a;'\n : 'color: #777583; background-color: #0E0E10;'}\n`;\n\nconst UserInfoContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst NameContainer = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst handleClick = useCallback(() => {\n props.onDMSelected(user);\n}, [user, props.onDMSelected]);\n\nconst user = props.user;\nreturn (\n <UserListItem selected={props.selected} onClick={handleClick}>\n <UserInfoContainer>\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n active: user.active,\n componentOwnerId: props.componentOwnerId,\n }}\n />\n <NameContainer>{`${user.id}`}</NameContainer>\n </UserInfoContainer>\n {user.unreadMessages.count > 0 && (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.SideSelector.UnreadMessagesBadge`}\n props={{\n messageCount: user.unreadMessages.count,\n backgroundColor: '#777583',\n }}\n />\n )}\n </UserListItem>\n);\n" }, "Calimero.DocsChain.Logo.DocsChainLogo": { "": "const LogoContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n ${({ justify }) => justify && 'justify-content: center;'}\n`;\n\nconst TextContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-size: 20.923px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-right: 0.875rem;\n`;\n\nreturn (\n <LogoContainer>\n <svg\n width=\"28\"\n height=\"28\"\n viewBox=\"0 0 28 28\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <rect width=\"28\" height=\"28\" rx=\"6\" fill=\"#4E95FF\" />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M24.1568 13.9999C24.1568 8.69352 19.8554 4.39209 14.549 4.39209C9.2426 4.39209 4.94117 8.69352 4.94117 13.9999C4.93884 15.7503 5.41672 17.4677 6.32278 18.9653L5.46576 21.8784C5.41655 22.0456 5.41331 22.223 5.45639 22.392C5.49946 22.5609 5.58725 22.7151 5.71053 22.8384C5.83381 22.9617 5.98802 23.0495 6.15696 23.0926C6.3259 23.1356 6.50333 23.1324 6.67058 23.0832L9.58368 22.2262C11.0811 23.1326 12.7986 23.6105 14.549 23.6078C15.4677 23.6078 16.3564 23.4788 17.1977 23.238C17.1509 22.8993 17.1267 22.5534 17.1267 22.2017C17.1267 18.2267 20.2197 14.974 24.1303 14.719C24.1479 14.4816 24.1568 14.2418 24.1568 13.9999ZM10.1672 12.9809C10.4375 12.7106 10.804 12.5588 11.1863 12.5588C11.5685 12.5588 11.9351 12.7106 12.2053 12.9809C12.4756 13.2511 12.6274 13.6177 12.6274 13.9999C12.6274 14.3822 12.4756 14.7487 12.2053 15.019C11.9351 15.2893 11.5685 15.4411 11.1863 15.4411C10.804 15.4411 10.4375 15.2893 10.1672 15.019C9.89693 14.7487 9.74509 14.3822 9.74509 13.9999C9.74509 13.6177 9.89693 13.2511 10.1672 12.9809ZM16.8927 12.9809C17.163 12.7106 17.5295 12.5588 17.9118 12.5588C18.294 12.5588 18.6605 12.7106 18.9308 12.9809C19.2011 13.2511 19.3529 13.6177 19.3529 13.9999C19.3529 14.3822 19.2011 14.7487 18.9308 15.019C18.6605 15.2893 18.294 15.4411 17.9118 15.4411C17.5295 15.4411 17.163 15.2893 16.8927 15.019C16.6224 14.7487 16.4706 14.3822 16.4706 13.9999C16.4706 13.6177 16.6224 13.2511 16.8927 12.9809Z\"\n fill=\"#111111\"\n />\n </svg>\n <TextContainer>DocsChain</TextContainer>\n </LogoContainer>\n);\n" }, "Calimero.Common.Login.index": { "": "const {\n loginApi,\n accountId,\n componentOwnerId,\n onValidLogin,\n logo,\n onAppJoin,\n refresh,\n} = props;\n\nconst [hasBootstrapped, setHasBootstrapped] = useState(false);\nconst [hasValidKey, setHasValidKey] = useState(false);\nconst [loading, setLoading] = useState(false);\n\nconst checkMembership = (accountId) => {\n return loginApi\n .getMembers()\n .then((memberList) =>\n memberList.map((member) => member.id).includes(accountId),\n );\n};\n\nconst bootstrap = () => {\n setLoading(true);\n\n loginApi\n .validateKey()\n .then((isValidKey) => {\n if (!isValidKey) {\n console.log('Invalid key');\n setHasValidKey(false);\n return Promise.reject('Invalid key');\n }\n\n setHasValidKey(true);\n console.log('Valid key');\n return checkMembership(accountId);\n })\n .then((isAlreadyMember) => {\n if (isAlreadyMember) {\n console.log('Is member');\n return true;\n }\n\n console.log('Not a member, attempting to join...');\n return loginApi.join().then(() => checkMembership(accountId));\n })\n .then((isNowMember) => {\n if (isNowMember) {\n onValidLogin();\n } else {\n console.log('Failed to join');\n }\n })\n .catch((error) => {\n console.log('Bootstrap error:', error);\n })\n .finally(() => {\n setLoading(false);\n setHasBootstrapped(true);\n });\n};\n\nconst join = () => {\n setLoading(true);\n loginApi\n .join()\n .then((result) => {\n if (result) {\n setIsMember(true);\n onValidLogin();\n }\n })\n .finally(() => {\n setLoading(false);\n });\n};\n\nuseEffect(() => {\n if (accountId && !hasBootstrapped && !loading) {\n bootstrap();\n }\n}, [accountId, hasBootstrapped, loading]);\n\nif (loading) {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.Loading`}\n props={{ logo }}\n />\n );\n}\n\nif (!accountId) {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Login.LoginPopup`}\n props={{ logo }}\n />\n );\n}\n\nif (!hasValidKey) {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Login.JoinPopup`}\n props={{\n logo,\n join: loginApi.login,\n refresh,\n }}\n />\n );\n}\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.AutoRefresh`}\n props={{ logo, refresh }}\n />\n);\n" }, "Calimero.Curb.ProfileIcon.Image": { "": "// Forked from: rubycoptest.testnet/widget/Image\n\nconst accountId = props.accountId;\nconst className = props.className;\nconst style = props.style;\nconst alt = props.alt;\nconst fallbackUrl = props.fallbackUrl;\nconst thumbnail = props.thumbnail;\nconst componentOwnerId = props.componentOwnerId;\n\nconst [imageUrl, setImageUrl] = useState(null);\nconst [nftImage, setNftImage] = useState(null);\nconst profile = Social.getr(`${accountId}/profile`);\n\nfunction toUrl(image) {\n return (\n (image.ipfs_cid\n ? `https://ipfs.near.social/ipfs/${image.ipfs_cid}`\n : image.url) || fallbackUrl\n );\n}\n\nconst thumb = (imageUrl) =>\n thumbnail && imageUrl && !imageUrl.startsWith('data:image/')\n ? `https://i.near.social/${thumbnail}/${imageUrl}`\n : imageUrl;\n\nuseEffect(() => {\n const image = profile.image;\n if (image && image.nft) {\n setNftImage(image);\n } else if (image) {\n const thumbImg = thumb(toUrl(image));\n setImageUrl(thumbImg);\n } else {\n setImageUrl(fallbackUrl);\n }\n}, [accountId, profile.image, fallbackUrl, thumbnail]);\n\nreturn nftImage.nft.contractId && nftImage.nft.tokenId ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ProfileIcon.NftImage`}\n props={{\n className,\n style,\n alt,\n nft: nftImage.nft,\n thumbnail,\n fallbackUrl,\n }}\n />\n) : (\n <img className={className} style={style} src={imageUrl} alt={alt} />\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.TaskDetailsDialog": { "": "const {\n task,\n componentOwnerId,\n onCloseTaskDetails,\n removeTaskFromColumn,\n projectId,\n contract,\n addActionStatus,\n taskColumn,\n columnIndex,\n updateTaskInColumn,\n} = props;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 734px;\n height: fit-content;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Title = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: transparent;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Input = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n flex: 1;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: transparent;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 104px;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n border: none;\n white-space: nowrap;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 68px;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 185px;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst LabelContainer = styled.div`\n width: 100%;\n`;\n\nconst ReporterInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n padding: 1rem;\n`;\n\nconst ReporterId = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n color: #fff;\n`;\n\nconst AssigneeContainer = styled.div`\n margin-left: 16px;\n`;\n\nconst FunctionButton = styled.button`\n ${({ backgroundColor }) =>\n backgroundColor && `background-color: ${backgroundColor};`}\n ${({ color }) => (color ? `color: ${color};` : 'color: #fff;')}\n ${({ disabled }) =>\n disabled ? 'cursor: not-allowed; opacity: 0.5;' : 'cursor: pointer;'}\n :hover {\n ${({ hoverColor }) =>\n hoverColor && !disabled && `background-color: ${hoverColor};`}\n }\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst DialogTitle = styled.p`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 15px;\n font-style: normal;\n font-weight: 700;\n line-height: 100%;\n padding: 0;\n margin: 0;\n`;\n\nconst ButtonsContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 24px;\n`;\n\nconst [editTaskTitle, setEditTaskTitle] = useState(task.title);\nconst [editTaskDescription, setEditTaskDescription] = useState(\n task.description,\n);\nconst [assignee, setAssignee] = useState(task.assignee);\nconst [editTaskTitleMissing, setEditTaskTitleMissing] = useState(false);\nconst [isDeleteTaskVisible, setIsDeleteTaskVisible] = useState(false);\n\nconst onEditTaskTitle = (e) => {\n setEditTaskTitleMissing(false);\n setEditTaskTitle(e.target.value);\n};\n\nconst onEditTaskDescription = (e) => {\n setEditTaskDescription(e.target.value);\n};\n\nconst updateTask = useCallback(() => {\n try {\n const editedTask = {\n ...task,\n title: editTaskTitle,\n description: editTaskDescription,\n assignee: assignee === 'no_assignee' ? null : assignee,\n };\n\n let promises = [];\n\n if (task.title !== editTaskTitle) {\n promises.push(\n Near.fakCalimeroCall(contract, 'change_task_name', {\n project_id: projectId,\n task_id: task.id,\n new_title: editTaskTitle,\n }),\n );\n }\n\n if (task.description !== editTaskDescription) {\n promises.push(\n Near.fakCalimeroCall(contract, 'edit_task_description', {\n project_id: projectId,\n task_id: task.id,\n new_description: editTaskDescription,\n }),\n );\n }\n\n if (task.assignee !== assignee) {\n promises.push(\n Near.fakCalimeroCall(\n contract,\n assignee === 'no_assignee' ? 'remove_assignee' : 'assign_task',\n {\n project_id: projectId,\n task_id: task.id,\n ...(assignee !== 'no_assignee' && { assignee: assignee }),\n },\n ),\n );\n }\n\n if (promises.length > 0) {\n updateTaskInColumn(columnIndex, editedTask);\n Promise.all(promises);\n }\n } catch (e) {\n console.log('updateTask', e);\n }\n onCloseTaskDetails();\n}, [\n task,\n editTaskTitle,\n editTaskDescription,\n projectId,\n contract,\n taskColumn,\n assignee,\n columnIndex,\n]);\n\nconst deleteTask = useCallback(\n (task) => {\n const actionStatusPrefix = 'Delete task';\n let newActionStatus = {\n id: actionStatusPrefix + taskId,\n status: `Deleting task: ${task.id}`,\n };\n try {\n addActionStatus(newActionStatus);\n removeTaskFromColumn(task.id, columnIndex);\n\n Near.fakCalimeroCall(contract, 'delete_task_by_id', {\n project_id: projectId,\n task_id: task.id,\n }).then(() => {\n newActionStatus.status = `Deleted task: ${taskId}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n newActionStatus.status = `Error deleting task: ${taskId}`;\n addActionStatus(newActionStatus);\n }\n setIsDeleteTaskVisible(false);\n onCloseTaskDetails();\n },\n [projectId, contract, addActionStatus, columnIndex],\n);\n\nconst handleUpdateTask = useCallback(() => {\n if (!editTaskTitle || editTaskTitle.trim() === '') {\n setEditTaskTitleMissing(true);\n setTimeout(() => {\n setEditTaskTitleMissing(false);\n setEditTaskTitle(task.title);\n }, 3000);\n return;\n } else if (\n editTaskTitle !== task.title ||\n editTaskDescription !== task.description ||\n assignee !== task.assignee\n ) {\n updateTask();\n }\n}, [\n task,\n editTaskTitle,\n editTaskDescription,\n setEditTaskTitleMissing,\n setEditTaskTitle,\n updateTask,\n assignee,\n]);\n\nreturn (\n <>\n {isDeleteTaskVisible && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ConfirmationPopup`}\n props={{\n onClick: () => deleteTask(task),\n onClose: () => setIsDeleteTaskVisible(false),\n title: 'Are you sure you want to delete the task?',\n }}\n />\n )}\n <OverlayContainer>\n <PopupContainer>\n <Header>\n <DialogTitle>Edit task</DialogTitle>\n <CloseButton onClick={onCloseTaskDetails}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <InputContainer>\n <Label>Title</Label>\n <Title\n onChange={onEditTaskTitle}\n value={editTaskTitle}\n placeholder=\"Add Title\"\n />\n {editTaskTitleMissing && <MissingTitle>Missing title</MissingTitle>}\n </InputContainer>\n </FieldContainer>\n <FieldContainer>\n <InputContainer>\n <Label>Assignee</Label>\n <AssigneeContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.AssigneeDropdown`}\n props={{\n componentOwnerId,\n assignee,\n setAssignee,\n contract,\n projectId,\n }}\n />\n </AssigneeContainer>\n </InputContainer>\n </FieldContainer>\n <LabelContainer>\n <InputContainer>\n <Label>Created by</Label>\n <ReporterInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: task.reporter,\n componentOwnerId,\n }}\n />\n <ReporterId>{task.reporter}</ReporterId>\n </ReporterInfo>\n </InputContainer>\n </LabelContainer>\n <FieldContainer>\n <InputContainer>\n <Label>Description</Label>\n <Input\n onChange={onEditTaskDescription}\n value={editTaskDescription}\n onBlur={updateTaskDescription}\n placeholder=\"Add Description\"\n />\n </InputContainer>\n </FieldContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.Comments`}\n props={{\n componentOwnerId,\n comments: task.comments,\n contract,\n projectId,\n taskId: task.id,\n }}\n />\n <ButtonsContainer>\n <FunctionButton\n backgroundColor=\"#D0FC42\"\n color=\"black\"\n hoverColor=\"#BBE33B\"\n disabled={\n editTaskTitle === task.title &&\n editTaskDescription === task.description &&\n assignee === task.assignee\n }\n onClick={handleUpdateTask}\n >\n Save\n </FunctionButton>\n <FunctionButton\n backgroundColor=\"transparent\"\n hoverColor=\"#F25757\"\n onClick={() => setIsDeleteTaskVisible(true)}\n >\n Delete\n </FunctionButton>\n </ButtonsContainer>\n </PopupContainer>\n </OverlayContainer>\n </>\n);\n" }, "Calimero.TaskChain.SideMenu.SideMenu": { "": "const {\n contract,\n selectedProjectId,\n setSelectedProjectId,\n functionLoader,\n setFunctionLoader,\n componentOwnerId,\n accountId,\n addActionStatus,\n} = props;\n\nconst SideMenuContainer = styled.div`\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n width: 230px;\n padding-left: 0;\n background-color: #0e0e10;\n color: white;\n padding: 3px;\n gap: 16px;\n`;\n\nconst AddBoardsContainer = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n`;\n\nconst AddBoardText = styled.p`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 700;\n line-height: 150%;\n padding: 0;\n margin: 0;\n`;\n\nconst AddBoardButton = styled.button`\n background-color: transparent;\n border: none;\n :focus {\n outline-width: 0px;\n }\n`;\n\nconst AddBoardIcon = styled.div`\n color: #777583;\n :hover {\n color: #ffffff;\n }\n`;\n\nconst createBoardButton = (\n <AddBoardButton>\n <AddBoardIcon>\n <i className=\"bi bi-plus-circle\"></i>\n </AddBoardIcon>\n </AddBoardButton>\n);\n\nconst [selectedUser, setSelectedUser] = useState(undefined);\nconst [users, setUsers] = useState([]);\nconst [boards, setBoards] = useState([]);\nconst [showEditBoardDialog, setShowEditBoardDialog] = useState(false);\nconst [deleteBoardId, setDeleteBoardId] = useState(undefined);\nconst [showDeleteBoardDialog, setShowDeleteBoardDialog] = useState(false);\nconst [createBoardStatus, setCreateBoardStatus] = useState([]);\n\nconst [editedBoard, setEditedBoard] = useState(undefined);\n\nconst addBoard = useCallback(\n (newBoard) => {\n setBoards((previousBoards) => [...previousBoards, newBoard]);\n },\n [setBoards],\n);\n\nconst updateBoardName = useCallback(\n (updatedBoard) => {\n setBoards((previousBoards) =>\n previousBoards.map((board) =>\n board[0] === updatedBoard[0] ? updatedBoard : board,\n ),\n );\n },\n [setBoards],\n);\n\nconst deleteBoard = useCallback(\n (deletedBoardId) => {\n setSelectedProjectId(boards.length > 0 ? boards[0][0] : undefined);\n setBoards((previousBoards) =>\n previousBoards.filter((board) => board[0] !== deletedBoardId),\n );\n },\n [setBoards, setSelectedProjectId, boards],\n);\n\nconst addCreateBoardStatus = useCallback((newStatus) => {\n let isNew = true;\n let newCreateBoardStatuses = createBoardStatus.map(\n (status) => {\n if (status.name === newStatus.name) {\n isNew = false;\n return newStatus;\n } else {\n return status;\n }\n setCreateBoardStatus(newCreateBoardStatuses);\n },\n [createBoardStatus],\n );\n});\n\nuseEffect(() => {\n createBoardStatus.forEach((board, index) => {\n if (board.status === 'Saved' || board.status === 'Error') {\n const timer = setTimeout(() => {\n const newStatus = createBoardStatus.slice();\n newStatus.splice(index, 1);\n setCreateBoardStatus(newStatus);\n getBoards();\n }, 3000);\n return () => clearTimeout(timer);\n }\n });\n}, [createBoardStatus]);\n\nconst isApplicationUser = (accountId, users) => {\n return users.includes(accountId);\n};\n\nconst onChangeShowEditBoardDialog = (open, board) => {\n setShowEditBoardDialog(open);\n setEditedBoard(board);\n};\n\nconst removeBoardMember = useCallback(\n (member) => {\n setBoardMembers(boardMembers.filter((m) => m.id !== member.id));\n },\n [boardMembers, setBoardMembers],\n);\n\nconst getBoards = useCallback(async () => {\n try {\n Near.fakCalimeroCall(contract, 'get_members_projects', {\n member_id: accountId,\n }).then((boards) => {\n setBoards(boards);\n setSelectedProjectId(boards.length > 0 ? boards[0][0] : undefined);\n });\n } catch (e) {\n console.log('getBoards', e);\n }\n}, [contract, accountId]);\n\nconst getUsers = useCallback(async () => {\n try {\n Near.asyncCalimeroView(contract, 'get_application_members', {}).then(\n (usersResponse) => {\n setUsers(usersResponse.map((user) => ({ id: user })));\n if (!isApplicationUser(accountId, usersResponse)) {\n Near.fakCalimeroCall(contract, 'add_application_member', {\n new_member: accountId,\n });\n }\n },\n );\n } catch (e) {\n console.log('getUsers', e);\n }\n}, [contract, accountId]);\n\nuseEffect(() => {\n getBoards();\n getUsers();\n}, [accountId]);\n\nreturn (\n <>\n {showEditBoardDialog && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.EditBoardDialog`}\n props={{\n createBoard,\n onClose: () => onChangeShowEditBoardDialog(false, undefined),\n functionLoader,\n onChangeEditBoardName,\n boardName,\n editedBoard,\n componentOwnerId,\n selectedProjectId,\n users,\n selectedUser,\n setSelectedUser,\n addActionStatus,\n contract,\n updateBoardName,\n deleteBoard,\n onChangeShowEditBoardDialog,\n }}\n />\n )}\n <SideMenuContainer>\n <AddBoardsContainer>\n <AddBoardText>Your boards</AddBoardText>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.CreateBoardDialog`}\n props={{\n addCreateBoardStatus,\n componentOwnerId,\n accountId,\n handleBoards,\n contract,\n createBoardButton,\n addBoard,\n }}\n />\n </AddBoardsContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.BoardList`}\n props={{\n componentOwnerId,\n selectedProjectId,\n setSelectedProjectId,\n boards,\n onEditBoardClick: () => {},\n editBoard: () => {},\n onChangeShowEditBoardDialog,\n createBoardStatus,\n }}\n />\n </SideMenuContainer>\n </>\n);\n" }, "Calimero.Curb.Popups.InputPopup": { "": "const OverlayContainer = styled.div`\n left: 0px;\n right: 0px;\n bottom: 0px;\n top: 0px;\n position: absolute;\n width: 100%;\n height: 100vh;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n align-items: center;\n @media (max-width: 1024px) {\n height: 100vh;\n }\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n border-radius: 8px;\n width: 489px;\n @media (max-width: 1024px) {\n width: 90%;\n }\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #5765f2;\n :hover {\n background-color: #717cf0;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n position: absolute;\n right: 1rem;\n cursor: pointer;\n`;\nconst [inputValue, setInputValue] = useState('');\n\nconst handleInputChange = useCallback((e) => {\n setInputValue(e.target.value);\n}, []);\nconst handleSubmit = () => {\n props.handleClickEvent(inputValue);\n};\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <CloseButton onClick={props.handleClosePopup}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n <Text>{props.title}</Text>\n <Input onChange={handleInputChange} placeholder={props.placeholder} />\n <FunctionButton onClick={handleSubmit}>\n {props.functionLoader ? (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n props.buttonText\n )}\n </FunctionButton>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.DocsChain.ArticleDialog.DocumentHeader": { "": "const contract = props.contract || 'docs.testnet';\nconst componentOwnerId = props.componentOwnerId || 'fran-cali.testnet';\n\nconst Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n pading-left: 35px;\n padding-right: 35px;\n`;\n\nconst AddButton = styled.div`\n width: 24px;\n height: 24px;\n border-radius: 50%;\n display: flex;\n justify-content: center;\n font-size: 1.5rem;\n color: #4e95ff;\n align-items: center;\n border: solid 1px #0e0e10;\n :hover {\n border: solid 1px #fff;\n }\n`;\n\nconst Text = styled.div`\n color: #797978;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n`;\n\nconst IconPlus = styled.div`\n :hover {\n border: solid 1px #fff;\n }\n cursor: pointer;\n width: 24px;\n height: 24px;\n background-color: #4e95ff;\n color: #fff;\n margin-left: 0.5rem;\n display: flex;\n justify-content: center;\n align-items: center;\n border-radius: 50%;\n padding-top: 2px;\n`;\n\nconst Icon = styled.div`\n color: #797978;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n`;\n\nState.init({\n userList: [],\n userDialogOpen: false,\n openUserDialog: () => State.update({ userDialogOpen: true }),\n});\n\nconst setUserList = () =>\n Near.asyncCalimeroView(\n contract,\n 'get_accounts_paged',\n {},\n undefined,\n false,\n ).then((users) => {\n const userList = users.map((user) => user[0]);\n State.update({ userList: userList });\n });\n\nuseEffect(() => {\n setUserList();\n}, [state.userList]);\n\nconst OptionsContainer = styled.div`\n display: flex;\n gap: 1rem;\n align-items: center;\n`;\n\nreturn (\n <Container>\n <div className=\"d-flex gap-2\">\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.UsersButtonGroup`}\n props={{\n componentOwnerId: componentOwnerId,\n channelUserList: state.userList,\n openMemberList: state.openUserDialog,\n }}\n />\n <IconPlus onClick={() => console.log('add user dialog')}>\n <i className=\"bi bi-plus-lg\" />\n </IconPlus>\n </div>\n <OptionsContainer>\n <div className=\"d-flex gap-2\">\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.Common.Button`}\n props={{\n onClick: () => console.log('save'),\n text: 'Save',\n color: '#4E95FF',\n hoverColor: '#267DFF',\n width: '80px',\n }}\n />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.Common.Button`}\n props={{\n onClick: () => console.log('preview'),\n text: 'Preview',\n width: '80px',\n }}\n />\n </div>\n <div className=\"d-flex gap-4\">\n <Text>\n Last edited\n {props.lastEdit ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.TimeFormatting`}\n props={{\n time: (Date.now() - props.lastEdit) / 1000,\n }}\n />\n ) : (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.TimeFormatting`}\n props={{\n time: Date.now() - Date.now(),\n }}\n />\n )}\n </Text>\n <div className=\"d-flex gap-3\">\n <Icon>\n <i className=\"bi bi-share\"></i>\n </Icon>\n <Icon>\n <i className=\"bi bi-trash3\"></i>\n </Icon>\n </div>\n </div>\n </OptionsContainer>\n </Container>\n);\n" }, "Calimero.TaskChain.ActionStatus.StatusContainer": { "": "const { actionStatuses, componentOwnerId, setActionStatusNotVisible } = props;\n\nconst StatusContainer = styled.div`\n position: absolute;\n top: 1rem;\n right: 1rem;\n align-items: center;\n justify-content: center;\n`;\n\nconst newActionStatuses = actionStatuses.filter(\n (actionStatus) => !actionStatus.seen,\n);\n\nreturn (\n newActionStatuses.length > 0 && (\n <StatusContainer>\n {newActionStatuses.map((actionStatus, index) => (\n <Widget\n key={actionStatus.id + index}\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ActionStatus.Status`}\n props={{\n actionStatus,\n setActionStatusNotVisible,\n }}\n />\n ))}\n </StatusContainer>\n )\n);\n" }, "Calimero.TaskChain.ConfirmationPopup": { "": "const title = props.title ?? 'Are you sure?';\nconst deleteButtonText = props.deleteButtonText ?? 'Delete';\nconst closeButtonText = props.closeButtonText ?? 'Close';\nconst onClick = props.onClick;\nconst onClose = props.onClose;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 30;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n width: 60%;\n justify-content: center;\n text-align: center;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n padding-bottom: 10px;\n`;\n\nconst TextContainer = styled.div`\n display: flex;\n justify-content: center;\n width: 100%;\n`;\n\nconst Input = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n`;\n\nconst FunctionButton = styled.button`\n background-color: red;\n :hover {\n opacity: 0.8;\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n background-color: transparent;\n display: flex;\n justify-content: center;\n color: #6b7280;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <TextContainer>\n <Text>{title}</Text>\n </TextContainer>\n <FunctionButton onClick={onClick}>{deleteButtonText}</FunctionButton>\n <CloseButton onClick={onClose}>{closeButtonText}</CloseButton>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.TaskChain.JoinApp": { "": "const { contractName, componentOwnerId } = props;\n\nconst OverlayContainer = styled.div`\n display: flex;\n width: 100%;\n height: 100%;\n background-color: #0e0e10;\n justify-content: center;\n align-items: center;\n padding: 100px;\n`;\n\nconst PopupContainer = styled.div`\n background-color: #1e1f28;\n padding: 1rem;\n border-radius: 8px;\n width: 489px;\n height: 200px;\n`;\n\nconst JoinContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n text: center;\n`;\n\nconst JoinHeader = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst user = context.accountId;\n\nconst joinTaskChain = useCallback(\n () =>\n Near.requestCalimeroFak(contractName)\n .then((res) => {\n console.log('requestCalimeroFak', res);\n })\n .then(() => {\n Near.asyncCalimeroView(\n contractName,\n 'get_application_members',\n {},\n ).then((res) => {\n if (user in res) {\n return Near.asyncCalimeroCall(\n contractName,\n 'add_application_member',\n {\n member: user,\n },\n );\n }\n });\n }),\n [],\n);\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <JoinContainer>\n <JoinHeader>\n <div className=\"pt-3\" />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.TaskChainLogo`}\n props={{\n justify: true,\n }}\n />\n <div className=\"pt-3\" />\n <Text>Join Boards</Text>\n <div className=\"pt-3\" />\n <FunctionButton onClick={joinTaskChain}>Join</FunctionButton>\n </JoinHeader>\n </JoinContainer>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.TaskChain.BoardContainer.Board": { "": "const {\n projectId,\n setSelectedProjectId,\n contract,\n status,\n componentOwnerId,\n addActionStatus,\n columnIndex,\n} = props;\n\nconst TopBar = styled.div`\n width: 100%;\n height: 50px;\n display: flex;\n flex-direction: row;\n`;\n\nconst AddColumnContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n`;\n\nconst HorizontalFlexContainer = styled.div`\n display: flex;\n flex-direction: row;\n overflow-x: scroll;\n overflow-y: scroll;\n height: 100%;\n width: 100%;\n scroll-behavior: smooth;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst AddColumnButton = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n background-color: transparent;\n margin-top: 1.2rem;\n color: #6b7280;\n width: 12rem;\n gap: 4px;\n border-radius: 4px;\n :hover {\n color: #fff;\n }\n cursor: pointer;\n`;\n\nconst AddIcon = styled.div``;\n\nconst AddColumnText = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n margin-left: 0.25rem;\n`;\n\nconst LoadingContainer = styled.div`\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nconst addColumnButton = (\n <AddColumnButton>\n <AddIcon>\n <i className=\"bi bi-plus-circle-fill\"></i>\n </AddIcon>\n <AddColumnText>Add new Column</AddColumnText>\n </AddColumnButton>\n);\n\nconst [columns, setColumns] = useState([]);\nconst [draggingTask, setDraggingTask] = useState(null);\nconst [saveInProgress, setSaveInProgress] = useState(false);\nconst [draggingColumn, setDraggingColumn] = useState(null);\nconst [columnsLoading, setColumnsLoading] = useState(false);\n\nconst addNewColumn = useCallback(\n (newColumn) => {\n setColumns([...columns, newColumn]);\n },\n [columns],\n);\n\nconst removeColumn = useCallback(\n (columnIndex) => {\n setColumns((prevColumns) =>\n prevColumns.filter((_, idx) => idx !== columnIndex),\n );\n },\n [columns, setColumns],\n);\n\nconst addTaskToColumn = useCallback(\n (columnIndex, newTask) => {\n setColumns((previousColumns) => {\n const updatedColumns = [...previousColumns];\n updatedColumns[columnIndex] = {\n ...updatedColumns[columnIndex],\n tasks: [...updatedColumns[columnIndex].tasks, newTask],\n };\n return updatedColumns;\n });\n },\n [setColumns],\n);\n\nconst updateTaskInColumn = useCallback(\n (columnIndex, updatedTask) => {\n setColumns((previousColumns) => {\n const updatedColumns = [...previousColumns];\n updatedColumns[columnIndex] = {\n ...updatedColumns[columnIndex],\n tasks: updatedColumns[columnIndex].tasks.map((task) =>\n task.id === updatedTask.id ? updatedTask : task,\n ),\n };\n return updatedColumns;\n });\n },\n [setColumns],\n);\n\nconst removeTaskFromColumn = useCallback(\n (taskId, columnIndex) => {\n setColumns(\n columns.map((col, idx) =>\n idx === columnIndex\n ? { ...col, tasks: col.tasks.filter((task) => task.id !== taskId) }\n : col,\n ),\n );\n },\n [columns, setColumns],\n);\n\nconst submitComment = (task_id, message) => {\n Near.fakCalimeroCall(contract, 'comment_on_task', {\n project_id,\n task_id,\n message,\n });\n};\n\nconst onTaskDragStart = (id, title, originalStatus, originalIndex) => {\n try {\n setDraggingTask({ id, title, originalStatus, originalIndex });\n } catch (e) {\n console.log('onTaskDragStart', e);\n }\n};\n\nconst onTaskDragStop = (newStatus, index) => {\n const actionStatusPrefix = 'Task drag';\n let newActionStatus = {\n id:\n actionStatusPrefix +\n draggingTask.id +\n draggingTask.originalStatus +\n draggingTask.originalIndex,\n status: `Saving task ${draggingTask.title} to status ${newStatus}`,\n };\n\n try {\n addActionStatus(newActionStatus);\n // Create a deep copy of the statuses\n let statusesCopy = JSON.parse(JSON.stringify(columns));\n\n // Access and remove the task from the original status using the original index\n const originalStatusObj = statusesCopy.find(\n (status) => status.name === draggingTask.originalStatus,\n );\n\n const newStatusIndex = statusesCopy.findIndex(\n (status) => status.name === newStatus,\n );\n\n if (!originalStatusObj) {\n console.log('Original status not found: ', draggingTask.originalStatus);\n return;\n }\n\n const removedTask = originalStatusObj.tasks.splice(\n draggingTask.originalIndex,\n 1,\n )[0];\n\n // If the task was not found, you can choose how to handle it. In this case, we just log an error and return.\n if (!removedTask) {\n console.log('Task not found: ', draggingTask);\n return;\n }\n\n // Add the task to the new status at the given index\n const newStatusObj = statusesCopy.find(\n (status) => status.name === newStatus,\n );\n if (!newStatusObj) {\n console.log('New status not found: ', newStatus);\n return;\n }\n const newIndex = index ?? newStatusObj.tasks.length;\n\n newStatusObj.tasks.splice(newIndex, 0, removedTask);\n\n // Update the state with the modified statuses\n setSaveInProgress(true);\n\n Near.fakCalimeroCall(contract, 'move_task', {\n project_id: projectId,\n task_id: draggingTask.id,\n to_status_index: newStatusIndex,\n position: newIndex,\n }).then(() => {\n newActionStatus.status = `Saved task ${draggingTask.title} to status ${newStatus}`;\n addActionStatus(newActionStatus);\n setSaveInProgress(false);\n console.log('contract updated');\n });\n\n setDraggingTask(null);\n setColumns(statusesCopy);\n } catch (e) {\n newActionStatus.status = `Error saving task ${draggingTask.title} to status ${newStatus}`;\n addActionStatus(newActionStatus);\n console.log('onTaskDragStop', e);\n }\n};\n\nconst onTaskDragOver = () => {\n try {\n } catch (e) {\n console.log('onTaskDragOver', e);\n }\n};\n\nconst onColumnDragStart = (index, name) => {\n try {\n setDraggingColumn({ index, name });\n } catch (e) {\n console.log('onColumnDragStart', e);\n }\n};\n\nconst onColumnDragStop = (newPositionIndex) => {\n if (newPositionIndex === draggingColumn.index || draggingTask) {\n return;\n }\n const actionStatusPrefix = 'Column drag';\n let newActionStatus = {\n id: actionStatusPrefix + draggingColumn.name + draggingColumn.index,\n status: `Saving column ${draggingColumn.name} to new position ${\n newPositionIndex + 1\n }`,\n seen: false,\n };\n\n try {\n addActionStatus(newActionStatus);\n\n let statusDataCopy = swapByName(\n columns,\n draggingColumn.index,\n newPositionIndex,\n );\n setColumns(statusDataCopy);\n\n Near.fakCalimeroCall(contract, 'reorder_statuses', {\n project_id: projectId,\n old_position: draggingColumn.index,\n new_position: newPositionIndex,\n }).then(() => {\n //updating status\n newActionStatus.status = `Saved column ${\n draggingColumn.name\n } to new position ${newPositionIndex + 1}`;\n addActionStatus(newActionStatus);\n });\n } catch (e) {\n //updating status\n newActionStatus.status = `Error saving column ${\n draggingColumn.name\n } to new position ${newPositionIndex + 1}`;\n addActionStatus(newActionStatus);\n }\n};\n\nconst swapByName = (arr, index1, index2) => {\n let temp = arr[index1];\n arr[index1] = arr[index2];\n arr[index2] = temp;\n return arr;\n};\n\nconst createTask = useCallback(\n async (projectId, taskTitle, taskDescription) => {\n try {\n return Near.fakCalimeroCall(contract, 'create_task', {\n project_id: projectId,\n title: taskTitle,\n description: taskDescription,\n labels: [],\n });\n } catch (e) {\n console.log('createTask', e);\n }\n },\n [],\n);\n\nconst getStatuses = useCallback(async (projectId) => {\n setColumnsLoading(true);\n try {\n Near.asyncCalimeroView(contract, 'get_statuses', {\n project_id: projectId,\n }).then((data) => {\n setColumns(data);\n setColumnsLoading(false);\n });\n } catch (e) {\n console.log('getStatuses', e);\n }\n}, []);\n\nuseEffect(() => {\n if (projectId) {\n getStatuses(projectId);\n }\n}, [projectId]);\n\nreturn (\n <>\n {columnsLoading ? (\n <LoadingContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 128 }}\n />\n </LoadingContainer>\n ) : (\n <HorizontalFlexContainer>\n {columns.map((column, columnIndex) => {\n return (\n <div\n key={columnIndex}\n draggable=\"true\"\n droppable=\"true\"\n value={columnIndex}\n onDragStart={() => onColumnDragStart(columnIndex, column.name)}\n onDrop={() => onColumnDragStop(columnIndex)}\n >\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Column`}\n props={{\n columnIndex,\n componentOwnerId,\n title: column.name,\n tasks: column.tasks,\n submitComment: submitComment,\n onTaskDragStop: onTaskDragStop,\n onTaskDragStart: onTaskDragStart,\n onTaskDragOver: onTaskDragOver,\n createStatus,\n createTask,\n projectId,\n contract,\n setColumns,\n columns,\n addActionStatus,\n removeColumn,\n removeTaskFromColumn,\n addTaskToColumn,\n updateTaskInColumn,\n }}\n />\n </div>\n );\n })}\n <AddColumnContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.AddColumnPopup`}\n props={{\n componentOwnerId,\n addNewColumn,\n addColumnButton,\n addColumn,\n addActionStatus,\n contract,\n projectId,\n }}\n />\n </AddColumnContainer>\n </HorizontalFlexContainer>\n )}\n </>\n);\n" }, "Calimero.Common.Popups.AutoRefresh": { "": "const OverlayContainer = styled.div`\n left: 0px;\n right: 0px;\n bottom: 0px;\n top: 0px;\n position: fixed;\n z-index: 20;\n display: flex;\n background-color: #0e0e10;\n justify-content: center;\n align-items: center;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n border-radius: 8px;\n width: 489px;\n @media (max-width: 1024px) {\n width: 90%;\n }\n`;\n\nconst LoadingContainer = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;zz\n text: center;\n`;\n\nconst LoadingHeader = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: center;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n @media (max-width: 1024px) {\n font-size: 20px;\n }\n`;\n\nsetTimeout(() => {\n console.log('autorefresh');\n props.refresh();\n}, 3000);\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <LoadingContainer>\n <LoadingHeader>\n {props.logo}\n <Text>Loading...</Text>\n </LoadingHeader>\n </LoadingContainer>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.Curb.EventEmitter": { "": "const render = props.render;\n\nfunction EventEmitter() {\n let store = new Map();\n\n let register = (persist, append) => (event, listener, isExclusive) => {\n isExclusive = !!isExclusive;\n if (typeof listener !== 'function') throw 'listener is not a function';\n let listeners = store.get(event) || store.set(event, []).get(event);\n if (\n listeners.length &&\n (isExclusive || listeners.some((entry) => entry.isExclusive))\n )\n throw \"Can't add listener to exclusive event\";\n listeners[append ? 'push' : 'unshift']({\n persist,\n listener,\n isExclusive,\n });\n };\n\n return {\n on: register(true, false),\n once: register(false, false),\n prependListener: register(true, true),\n prependOnceListener: register(false, true),\n off: (event, listener) => {\n if (typeof listener !== 'function') throw 'listener is not a function';\n let idx,\n listeners = store.get(event);\n if (\n listeners &&\n -1 < (idx = listeners.findIndex((entry) => entry.listener === listener))\n ) {\n listeners.splice(idx, 1);\n if (!listeners.length) store.delete(event);\n }\n },\n emit: (event, data) => {\n let listeners = store.get(event);\n if (listeners) {\n listeners = listeners.filter((entry) => {\n entry.listener(data);\n return entry.persist;\n });\n if (!listeners.length) store.delete(event);\n else store.set(event, listeners);\n }\n },\n };\n}\n\nreturn render({ EventEmitter });\n" }, "Calimero.Curb.ChannelsContainer": { "": "const curbApi = props.curbApi;\nconst componentOwnerId = props.componentOwnerId;\nconst onChatSelected = props.onChatSelected;\nconst activeChat = props.activeChat;\nconst isSidebarOpen = props.isSidebarOpen;\nconst setIsSidebarOpen = props.setIsSidebarOpen;\nconst isWsConnectionActive = props.isWsConnectionActive;\nconst wsApi = props.wsApi;\nconst enableCommunities = props.enableCommunities;\nconst handleContractChange = props.handleContractChange;\nconst selectedCommunity = props.selectedCommunity;\n\nconst communitiesList = props.communities;\n\nconst [channels, setChannels] = useState([]);\nconst [users, setUsers] = useState([]);\nconst [communities, setCommunities] = useState([]);\nconst [cachedUsers, setCachedUsers] = useState([]);\n\nconst usersRef = useRef(cachedUsers);\n\nconst generator = () =>\n Promise.all([\n curbApi.getChannels(),\n curbApi.getUnreadMessages(),\n ...(enableCommunities ? [curbApi.getCommunities(communitiesList)] : []),\n ]);\n\nconst [cachedChannels, cachedUnread, cachedCommunities] = useCache(\n generator,\n 'channel_container',\n { subscribe: true },\n);\n\nuseEffect(() => {\n usersRef.current = cachedUsers;\n}, [cachedUsers]);\n\nconst peerStatusChangeCb = (newStatus) => (event) => {\n const updateUserStatus = (userId, isActive) => {\n const user = usersRef.current.find((u) => u.id === userId);\n if (!user || user.active === newStatus) return;\n\n const updatedUsers = usersRef.current.map((u) =>\n u.id === userId ? { ...u, active: isActive } : u,\n );\n setCachedUsers(updatedUsers);\n };\n\n const { account_id: accountId } = event;\n updateUserStatus(accountId, newStatus === 'online');\n};\n\nuseEffect(() => {\n const peerConnectedCb = peerStatusChangeCb('online');\n const peerDisconnectedCb = peerStatusChangeCb('offline');\n const channelMessageCb = (event) => {\n if (event.receiver.type === 'account') {\n const senderId = event.sender;\n const senderExists = usersRef.current.some((u) => u.id === senderId);\n if (!senderExists) {\n const newUser = {\n id: senderId,\n active: true, // we just received a message from this user, so they are online\n type: 'direct_message',\n };\n setCachedUsers((prevUsers) => [...prevUsers, newUser]);\n }\n }\n };\n\n wsApi.notifications.on('PeerConnected', peerConnectedCb);\n wsApi.notifications.on('PeerDisconnected', peerDisconnectedCb);\n wsApi.notifications.on('ChannelMessage', channelMessageCb);\n return () => {\n wsApi.notifications.off('PeerConnected', peerConnectedCb);\n wsApi.notifications.off('PeerDisconnected', peerDisconnectedCb);\n wsApi.notifications.off('ChannelMessage', channelMessageCb);\n };\n}, []);\n\nuseEffect(() => {\n if (isWsConnectionActive) {\n curbApi\n .getDMs()\n .then((accounts) => curbApi.getAccountsStatus(accounts))\n .then((result) =>\n Object.entries(result.status).map(([accountId, status]) => ({\n id: accountId,\n active: status === 'online',\n type: 'direct_message',\n })),\n )\n .then((users) => {\n setCachedUsers(users);\n });\n }\n}, [isWsConnectionActive]);\n\nuseEffect(() => {\n const usersWN = (cachedUsers ?? []).map((u) => ({\n ...u,\n unreadMessages: cachedUnread.chats[u.id] || 0,\n }));\n\n const channelsWN = (cachedChannels ?? []).map((c) => ({\n ...c,\n unreadMessages: cachedUnread.channels[c.name] || 0,\n }));\n\n setUsers(usersWN);\n setChannels(channelsWN);\n enableCommunities && setCommunities(cachedCommunities);\n}, [cachedUsers, cachedChannels, cachedUnread, cachedCommunities]);\n\nconst selectChannel = useCallback(\n (chatSelected) => {\n const chat = { type: chatSelected.type };\n if (chatSelected.type === 'channel') {\n chat.name = chatSelected.name;\n chat.readOnly = chatSelected.readOnly;\n } else {\n // we add a new DM, it will need to fetch the status of the other user\n if (!users.some((user) => user.id === chatSelected.id)) {\n setCachedUsers((prevUsers) => [...prevUsers, chatSelected]);\n }\n chat.id = chatSelected.id;\n }\n onChatSelected(chat);\n },\n [users],\n);\n\nreturn (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.SideSelector.SideSelector`}\n props={{\n curbApi,\n componentOwnerId,\n onChatSelected: selectChannel,\n activeChat,\n users,\n channels,\n isSidebarOpen,\n setIsSidebarOpen,\n communities,\n enableCommunities,\n handleContractChange,\n selectedCommunity,\n }}\n />\n);\n" }, "Calimero.DocsChain.Sidebar.AllArticlesList": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst Container = styled.div`\n background-color: #0e0e10;\n width: 317px;\n`;\n\nconst Item = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n &:hover {\n color: #ffffff;\n }\n a {\n text-decoration: none;\n color: inherit;\n\n &:hover {\n color: #ffffff;\n }\n }\n :hover {\n background-color: #25252a;\n }\n cursor: pointer;\n ${({ selected }) =>\n selected\n ? 'color: #fff; background-color: #25252a;'\n : 'color: #777583; background-color: #0E0E10;'}\n`;\n\nconst TextMedium = styled.div`\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n padding-top: 1px;\n`;\n\nconst NameContainer = styled.a`\n display: flex;\n justify-content: start;\n align-items: center;\n gap: 12px;\n width: 100%;\n text-decoration: none;\n color: #777583;\n &:hover,\n &:active,\n &:visited {\n text-decoration: none;\n color: #777583;\n }\n`;\n\nconst CircleIcon = styled.div`\n border-radius: 50%;\n width: 16px;\n height: 16px;\n background-color: #4e95ff;\n`;\n\nconst getDateLastEdit = (timestamp) => {\n const date = new Date(Number(timestamp) / 1e6);\n const dateString = `${date.toLocaleDateString()} / ${date.toLocaleTimeString()}`;\n return dateString;\n};\n\nconst allArticles = Near.calimeroView(\n contract,\n 'get_article_ids_paged',\n {},\n).map((articleId) => {\n return {\n id: articleId,\n data: Near.calimeroView(contract, 'get_article', { article_id: articleId }),\n };\n});\n\nreturn (\n <Container>\n {(allArticles ?? []).map((article) => (\n <Item key={article.id} selected={props.selectedArticleId === article.id}>\n <NameContainer\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.ArticleView?articleId=${article.id}&blockHeight=${article.data.blockHeight}&lastEditor=${article.data.author})`,\n )}\n >\n {article.body.icon ? <p>{article.body.icon}</p> : <CircleIcon />}\n <TextMedium>\n {article.data.body.split('\\n')[0].replace('# ', '')}\n </TextMedium>\n </NameContainer>\n </Item>\n ))}\n </Container>\n);\n" }, "Calimero.Curb.SideSelector.ChannelsHeader": { "": "const Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n color: #777583;\n :hover {\n color: #ffffff;\n }\n @media (max-width: 1024px) {\n width: 100%;\n padding-top: 1.25rem;\n }\n`;\n\nconst TextBold = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n`;\nconst IconPlusContainer = styled.div`\n display: flex;\n cursor: pointer;\n justify-content: center;\n align-items: center;\n font-size: 1.25rem;\n`;\n\nconst isValidChannelName = useCallback((value) => {\n const regex = /^[^#!\\s]{3,19}$/;\n let error = null;\n const isValid = regex.test(value);\n\n if (value.match(/[!#.\\s]/)) {\n error = 'Channel name must not contain special charters or links.';\n } else if (value.length < 3) {\n error = 'Channel name is too short. It should be at least 3 characters.';\n } else if (value.length > 19) {\n error = 'Channel name is too long. It should be at most 19 characters.';\n } else {\n error = 'Invalid channel name.';\n }\n\n return { isValid, error };\n}, []);\n\nconst CreateChannelPopup = (props) => (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.Popups.CreateChannelPopup`}\n props={{\n componentOwnerId: props.componentOwnerId,\n title: 'Create new Channel',\n placeholder: '# channel name',\n buttonText: 'Create',\n colors: {\n base: '#5765f2',\n hover: '#717cf0',\n disabled: '#3B487A',\n },\n toggle: (\n <IconPlusContainer>\n <i className=\"bi bi-plus-circle\" />\n </IconPlusContainer>\n ),\n createChannel: (channelName, isPublic, isReadOnly) =>\n props.curbApi.createGroup(channelName, isPublic, isReadOnly, accountId),\n channelNameValidator: isValidChannelName,\n }}\n />\n);\n\nreturn (\n <Container>\n <TextBold>{props.title}</TextBold>\n <CreateChannelPopup\n curbApi={props.curbApi}\n componentOwnerId={props.componentOwnerId}\n />\n </Container>\n);\n" }, "Calimero.Curb.ProfileIcon.NftImage": { "": "const {\n className: propClassName,\n style,\n alt,\n nft: propNft,\n thumbnail,\n fallbackUrl,\n} = props;\n\nconst [state, setState] = useState({\n contractId: propNft?.contractId,\n tokenId: propNft?.tokenId,\n loadingUrl:\n 'https://ipfs.near.social/ipfs/bafkreidoxgv2w7kmzurdnmflegkthgzaclgwpiccgztpkfdkfzb4265zuu',\n imageUrl: null,\n oldUrl: null,\n});\n\nconst nftMetadata =\n propNft.contractMetadata ?? Near.view(state.contractId, 'nft_metadata');\nconst tokenMetadata =\n propNft.tokenMetadata ??\n Near.view(state.contractId, 'nft_token', {\n token_id: state.tokenId,\n }).metadata;\n\nlet imageUrl = null;\n\nif (nftMetadata && tokenMetadata) {\n let tokenMedia = tokenMetadata.media || '';\n\n imageUrl =\n tokenMedia.startsWith('https://') ||\n tokenMedia.startsWith('http://') ||\n tokenMedia.startsWith('data:image')\n ? tokenMedia\n : nftMetadata.base_uri\n ? `${nftMetadata.base_uri}/${tokenMedia}`\n : tokenMedia.startsWith('Qm') || tokenMedia.startsWith('ba')\n ? `https://ipfs.near.social/ipfs/${tokenMedia}`\n : tokenMedia;\n\n if (!tokenMedia && tokenMetadata.reference) {\n if (\n nftMetadata.base_uri === 'https://arweave.net' &&\n !tokenMetadata.reference.startsWith('https://')\n ) {\n const res = fetch(`${nftMetadata.base_uri}/${tokenMetadata.reference}`);\n imageUrl = res.body.media;\n } else if (\n tokenMetadata.reference.startsWith('https://') ||\n tokenMetadata.reference.startsWith('http://')\n ) {\n const res = fetch(tokenMetadata.reference);\n imageUrl = JSON.parse(res.body).media;\n } else if (tokenMetadata.reference.startsWith('ar://')) {\n const res = fetch(\n `${'https://arweave.net'}/${tokenMetadata.reference.split('//')[1]}`,\n );\n imageUrl = JSON.parse(res.body).media;\n }\n }\n\n if (!imageUrl) {\n imageUrl = false;\n }\n setState((prevState) => ({\n ...prevState,\n imageUrl: imageUrl,\n }));\n}\n\nconst replaceIpfs = useCallback(\n (imageUrl) => {\n const rex =\n /^(?:https?:\\/\\/)(?:[^\\/]+\\/ipfs\\/)?(Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,})(?:\\.[^\\/]+)?(\\/.*)?$/g;\n\n if (state.oldUrl !== imageUrl && imageUrl) {\n const match = rex.exec(imageUrl);\n if (match) {\n const newImageUrl = `https://ipfs.near.social/ipfs/${match[1]}${\n match[2] || ''\n }`;\n if (newImageUrl !== imageUrl) {\n setState((prevState) => ({\n ...prevState,\n oldUrl: imageUrl,\n imageUrl: newImageUrl,\n }));\n return;\n }\n }\n }\n if (state.imageUrl !== false) {\n setState((prevState) => ({\n ...prevState,\n imageUrl: false,\n }));\n }\n },\n [state],\n);\n\nconst thumb = (imageUrl) =>\n thumbnail && imageUrl && !imageUrl.startsWith('data:image/')\n ? `https://i.near.social/${thumbnail}/${imageUrl}`\n : imageUrl;\n\nconst img = state.imageUrl !== null ? state.imageUrl : imageUrl;\nconst src = img !== false ? img : fallbackUrl;\n\nreturn (\n <img\n className={propClassName || 'img-fluid'}\n style={style}\n src={src !== null ? thumb(src) : state.loadingUrl}\n alt={alt}\n onError={() => replaceIpfs(img)}\n />\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.TaskCard": { "": "const {\n task,\n status,\n index,\n onViewTaskDetails,\n onTaskDragStop,\n onTaskDragStart,\n createStatus,\n} = props;\n\nconst TaskCardContainer = styled.div`\n display: flex;\n flex-direction: column;\n padding: 16px 16px 2px 16px;\n border-radius: 4px;\n width: 15rem;\n align-items: flex-start;\n justify-content: center;\n background-color: #1e1f28;\n color: white;\n ${({ enabled }) => (enabled ? 'cursor: pointer;' : 'cursor: not-allowed')}\n`;\n\nconst TaskTitle = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n padding-top: 0.25rem;\n width: 100%;\n`;\n\nconst Text = styled.div`\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 100%;\n`;\n\nconst ImageContainer = styled.div`\n align-items: center;\n justify-content: center;\n height: 100px;\n overflow: hidden;\n display: flex;\n`;\n\nconst TaskFooter = styled.div`\n display: flex;\n justify-content: space-between;\n color: #6b7280;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n width: 100%;\n`;\n\nconst Label = styled.div`\n height: 8px;\n border-radius: 200px;\n width: 42px;\n ${({ backgroundColor }) =>\n backgroundColor && `background-color: ${backgroundColor};`}\n`;\n\nconst LabelContainer = styled.div`\n gap: 4px;\n padding-bottom: 4px;\n display: flex;\n`;\n\nconst AssigneeIcon = styled.div`\n display: flex;\n gap: 2px;\n`;\n\nconst TaskImage = ({ imageSrc }) => (\n <Widget src={imageSrc} props={{ style: { width: '32px', height: '32px' } }} />\n);\n\nconst taskImage = task.files?.[0] ?? null;\n\nconst savingStatus = createStatus.find((s) => s.title === task.title);\n\nreturn (\n <>\n <TaskCardContainer\n draggable\n onDragStart={() => {\n if (task.id) {\n onTaskDragStart(task.id, task.title, props.status, index);\n }\n }}\n onDrop={() => {\n if (task.id) {\n onTaskDragStop(props.status, index);\n }\n }}\n onClick={onClick}\n onClick={() => {\n if (task.id) {\n onViewTaskDetails(task);\n }\n }}\n enabled={task.id}\n >\n {savingStatus && <p>{savingStatus.status}</p>}\n {task.labels.length > 0 && (\n <LabelContainer>\n {task.labels.map((label, index) => (\n <Label key={index} backgroundColor={label.color} />\n ))}\n </LabelContainer>\n )}\n {taskImage && (\n <ImageContainer>\n <TaskImage imageSrc={taskImage} />\n </ImageContainer>\n )}\n <TaskTitle>\n <Text>{task.title}</Text>\n </TaskTitle>\n <TaskFooter>\n <p>{task.id}</p>\n {task.assignee && (\n <AssigneeIcon>\n <i className=\"bi bi-person-fill\"></i>\n <p>{task.assignee}</p>\n </AssigneeIcon>\n )}\n </TaskFooter>\n </TaskCardContainer>\n </>\n);\n" }, "Calimero.Curb.SideSelector.ChannelList": { "": "const ChannelListContainer = styled.div`\n background-color: #0e0e10;\n @media (max-width: 1024px) {\n width: 100%;\n }\n`;\n\nconst ChannelListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n cursor: pointer;\n @media (min-width: 1024px) {\n &:hover {\n background-color: #25252a !important;\n color: #fff !important;\n fill: #fff !important;\n }\n }\n @media (max-width: 1024px) {\n &:active {\n background-color: #25252a !important;\n color: #fff !important;\n fill: #fff !important;\n }\n }\n`;\n\nconst TextMedium = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n`;\n\nconst NameContainer = styled.div`\n display: flex;\n gap: 8px;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst IconWrapper = styled.div`\n height: 24px;\n width: 24px;\n display: flex;\n justify-content: center;\n align-items: center;\n`;\n\nreturn (\n <ChannelListContainer>\n {props.channels.map((channel) => (\n <ChannelListItem\n key={channel.name}\n selected={props.selectedChannelId === channel.name}\n onClick={() => props.selectChannel(channel)}\n style={{\n backgroundColor:\n props.selectedChannelId === channel.name ? '#25252a' : '#0E0E10',\n color:\n channel.unreadMessages.count > 0 ||\n props.selectedChannelId === channel.name\n ? '#fff'\n : '#777583',\n fill:\n channel.unreadMessages.count > 0 ||\n props.selectedChannelId === channel.name\n ? '#fff'\n : '#777583',\n }}\n >\n <div>\n <NameContainer>\n <IconWrapper>\n {channel.channelType === 'Private' ? (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M8 1C8.53043 1 9.03914 1.21071 9.41421 1.58579C9.78929 1.96086 10 2.46957 10 3V7H6V3C6 2.46957 6.21071 1.96086 6.58579 1.58579C6.96086 1.21071 7.46957 1 8 1ZM11 7V3C11 2.20435 10.6839 1.44129 10.1213 0.87868C9.55871 0.31607 8.79565 0 8 0C7.20435 0 6.44129 0.31607 5.87868 0.87868C5.31607 1.44129 5 2.20435 5 3V7C4.46957 7 3.96086 7.21071 3.58579 7.58579C3.21071 7.96086 3 8.46957 3 9V14C3 14.5304 3.21071 15.0391 3.58579 15.4142C3.96086 15.7893 4.46957 16 5 16H11C11.5304 16 12.0391 15.7893 12.4142 15.4142C12.7893 15.0391 13 14.5304 13 14V9C13 8.46957 12.7893 7.96086 12.4142 7.58579C12.0391 7.21071 11.5304 7 11 7Z\" />\n </svg>\n ) : (\n <svg\n width=\"13\"\n height=\"17\"\n viewBox=\"0 0 13 17\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M6.585 15.972C6.57134 16.0614 6.56383 16.1516 6.5625 16.242C6.5625 16.6995 6.8775 17.004 7.3125 17.004C7.7115 17.004 8.0505 16.746 8.145 16.2885L8.976 12.234H10.782C11.4135 12.234 11.7075 11.883 11.7075 11.4135C11.7075 10.9455 11.4255 10.6185 10.782 10.6185H9.3045L10.0785 6.83249H11.976C12.621 6.83249 12.903 6.49199 12.903 6.01199C12.903 5.54249 12.621 5.22599 11.976 5.22599H10.407L11.121 1.76999C11.1354 1.68874 11.1434 1.60649 11.145 1.52399C11.1462 1.42202 11.127 1.32083 11.0885 1.22638C11.0501 1.13192 10.9931 1.04612 10.921 0.974005C10.8489 0.90189 10.7631 0.844923 10.6686 0.806453C10.5742 0.767984 10.473 0.748788 10.371 0.749995C10.1822 0.746392 9.99804 0.80888 9.8504 0.926656C9.70277 1.04443 9.60094 1.21009 9.5625 1.39499L8.778 5.22599H5.4255L6.141 1.76999C6.153 1.70999 6.1635 1.59299 6.1635 1.52399C6.16433 1.42123 6.14452 1.31935 6.10526 1.22438C6.06599 1.12941 6.00807 1.04329 5.93491 0.971112C5.86176 0.898937 5.77486 0.842178 5.67937 0.804196C5.58388 0.766214 5.48174 0.747784 5.379 0.749995C5.19215 0.748908 5.0107 0.812572 4.86549 0.930161C4.72028 1.04775 4.62028 1.212 4.5825 1.39499L3.795 5.22599H2.121C1.476 5.22599 1.1955 5.55599 1.1955 6.02399C1.1955 6.49199 1.476 6.83249 2.121 6.83249H3.48L2.7075 10.617H0.9135C0.282 10.617 0 10.9455 0 11.4135C0 11.883 0.282 12.234 0.915 12.234H2.379L1.605 15.972C1.593 16.032 1.5825 16.1595 1.5825 16.242C1.5825 16.6995 1.8975 17.004 2.3325 17.004C2.73 17.004 3.0705 16.746 3.1635 16.2885L3.996 12.234H7.359L6.5865 15.972H6.585ZM5.085 6.80849H8.484L7.7115 10.653H4.2885L5.0865 6.80849H5.085Z\" />\n </svg>\n )}\n </IconWrapper>\n\n <TextMedium>{channel.name}</TextMedium>\n </NameContainer>\n </div>\n {channel.unreadMessages.mentions > 0 && (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.SideSelector.UnreadMessagesBadge`}\n props={{\n messageCount: channel.unreadMessages.mentions,\n backgroundColor: '#FF5E5E',\n }}\n />\n )}\n </ChannelListItem>\n ))}\n </ChannelListContainer>\n);\n" }, "Calimero.Curb.Navbar.UsersButtonGroup": { "": "const AvatarContainer = styled.div`\n padding-left: 1rem;\n display: flex;\n flex-direction: row;\n cursor: pointer;\n`;\n\nconst ProfileIconContainerGroup = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n ${({ id }) => id && `background-color: ${colors[id]};`}\n ${({ counter }) =>\n counter ? 'background-color: #25252A; color: #6E6E78;' : 'color: #FFF;'}\n text-align: center;\n /* Body/Small */\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-left: -8px;\n border: solid 1px #0e0e10;\n :hover {\n border: solid 1px #fff;\n }\n`;\n\nreturn (\n <AvatarContainer onClick={props.openMemberList}>\n {props.channelUserList.slice(0, 3).map((user, id) => {\n return (\n <div key={id}>\n <ProfileIconContainerGroup>\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n showStatus: false,\n componentOwnerId: props.componentOwnerId,\n }}\n />\n </ProfileIconContainerGroup>\n </div>\n );\n })}\n {props.channelUserList.length > 3 && (\n <ProfileIconContainerGroup counter={true}>\n {props.channelUserList.length}\n </ProfileIconContainerGroup>\n )}\n </AvatarContainer>\n);\n" }, "Calimero.Curb.Settings.DetailsContainer": { "": "const channelName = props.channelName;\nconst initialTabIndex = props.selectedTabIndex ? 0 : 1;\nconst onSwitch = props.onSwitch;\nconst userCount = props.userList.length;\nconst userList = props.userList;\nconst componentOwnerId = props.componentOwnerId;\nconst channelMeta = props.channelMeta;\nconst handleLeaveChannel = props.handleLeaveChannel;\nconst addMember = props.addMember;\nconst promoteModerator = props.promoteModerator;\nconst removeUserFromChannel = props.removeUserFromChannel;\nconst curbApi = props.curbApi;\n\nconst [selectedTabIndex, setSelectedTabIndex] = useState(initialTabIndex);\nconst [optionsOpen, setOptionsOpen] = useState(-1);\nconst [nonInvitedUserList, setNonInvitedUserList] = useState([]);\nconst [selectedUser, setSelectedUser] = useState(undefined);\n\nconst getNonInvitedUsers = useCallback(\n (namePrefix, channelName) => {\n if (namePrefix) {\n curbApi.getFilteredMembers(namePrefix, channelName).then((users) => {\n setNonInvitedUserList(users ?? []);\n });\n }\n },\n [curbApi, userList],\n);\n\nconst ChannelTitle = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst ChannelName = () => {\n return (\n <ChannelTitle>\n <i className=\"bi bi-hash\"></i>\n {channelName}\n </ChannelTitle>\n );\n};\n\nreturn (\n <>\n <ChannelName channelName={channelName} />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Settings.TabSwitch`}\n props={{\n selectedTabIndex,\n setSelectedTabIndex,\n userCount,\n }}\n />\n {selectedTabIndex === 0 && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Settings.AboutDetails`}\n props={{\n dateCreated: channelMeta.createdAt,\n manager: channelMeta.createdBy,\n handleLeaveChannel,\n }}\n />\n )}\n {selectedTabIndex === 1 && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Settings.MemberDetails`}\n props={{\n componentOwnerId,\n userList,\n addMember,\n channelName,\n setOptionsOpen,\n optionsOpen,\n promoteModerator,\n removeUserFromChannel,\n channelOwner: channelMeta.createdBy,\n getNonInvitedUsers,\n nonInvitedUserList,\n selectedUser,\n setSelectedUser,\n }}\n />\n )}\n </>\n);\n" }, "Calimero.Curb.SideSelector.DMHeader": { "": "const Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n color: #777583;\n :hover {\n color: #ffffff;\n }\n @media (max-width: 1024px) {\n width: 100%;\n }\n`;\n\nconst TextBold = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n`;\nconst IconPlusContainer = styled.div`\n display: flex;\n cursor: pointer;\n justify-content: center;\n align-items: center;\n font-size: 1.25rem;\n`;\n\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\nconst createDM = props.createDM;\n\nconst isValidNearAccount = useCallback((value) => {\n const regex = /^[a-z\\d]+[-_]*[a-z\\d]+[-_]*[a-z\\d]+\\.(near|testnet)$/;\n let isValid = false;\n let error = '';\n\n if (!regex.test(value)) {\n isValid = false;\n error = \"Invite users whose wallets end with '.near' for access\";\n } else {\n isValid = true;\n error = '';\n }\n return { isValid, error };\n}, []);\n\nconst CreateDMPopup = ({ componentOwnerId, createDM, curbApi }) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Popups.StartDMPopup`}\n props={{\n componentOwnerId,\n title: 'Start new message',\n placeholder: 'Send to wallet address',\n buttonText: 'Next',\n colors: {\n base: '#5765f2',\n hover: '#717cf0',\n disabled: '#3B487A',\n },\n toggle: (\n <IconPlusContainer>\n <i className=\"bi bi-plus-circle\" />\n </IconPlusContainer>\n ),\n onAccountSelected: createDM,\n fetchAccounts: (prefix) => {\n return curbApi.fetchAccounts({ prefix, limit: 20 });\n },\n validator: isValidNearAccount,\n functionLoader: createDM,\n }}\n />\n);\n\nreturn (\n <Container>\n <TextBold>{'Direct Messages'}</TextBold>\n <CreateDMPopup\n componentOwnerId={props.componentOwnerId}\n createDM={props.createDM}\n curbApi={props.curbApi}\n />\n </Container>\n);\n" }, "Calimero.TaskChain.TaskChainLogo": { "": "const LogoContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n ${({ justify }) => justify && 'justify-content: center;'}\n`;\n\nconst TaskChainNameContainer = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-size: 20.923px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-right: 0.875rem;\n`;\n\nconst OrganizationNameContainer = () => {\n return <TaskChainNameContainer>Boards</TaskChainNameContainer>;\n};\n\nreturn (\n <LogoContainer>\n <svg\n width=\"28\"\n height=\"28\"\n viewBox=\"0 0 28 28\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <rect width=\"28\" height=\"28\" fill=\"#1E1E1E\" />\n <g id=\"Frame 20\" clipPath=\"url(#clip0_0_1)\">\n <rect\n width=\"1440\"\n height=\"900\"\n transform=\"translate(-76 -26)\"\n fill=\"#0E0E10\"\n />\n <g id=\"Group 2\">\n <g id=\"Group 1\">\n <rect\n id=\"Rectangle 3\"\n width=\"28\"\n height=\"28\"\n rx=\"6\"\n fill=\"#D0FC42\"\n />\n <path\n id=\"Subtract\"\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M24.1568 13.9999C24.1568 8.69352 19.8554 4.39209 14.549 4.39209C9.2426 4.39209 4.94117 8.69352 4.94117 13.9999C4.93884 15.7503 5.41672 17.4677 6.32278 18.9653L5.46576 21.8784C5.41655 22.0456 5.41331 22.223 5.45639 22.392C5.49946 22.5609 5.58725 22.7151 5.71053 22.8384C5.83381 22.9617 5.98802 23.0495 6.15696 23.0926C6.3259 23.1356 6.50333 23.1324 6.67058 23.0832L9.58368 22.2262C11.0811 23.1326 12.7986 23.6105 14.549 23.6078C15.4677 23.6078 16.3564 23.4788 17.1977 23.238C17.1509 22.8993 17.1267 22.5534 17.1267 22.2017C17.1267 18.2267 20.2197 14.974 24.1303 14.719C24.1479 14.4816 24.1568 14.2418 24.1568 13.9999ZM10.1672 12.9809C10.4375 12.7106 10.804 12.5588 11.1863 12.5588C11.5685 12.5588 11.9351 12.7106 12.2053 12.9809C12.4756 13.2511 12.6274 13.6177 12.6274 13.9999C12.6274 14.3822 12.4756 14.7487 12.2053 15.019C11.9351 15.2893 11.5685 15.4411 11.1863 15.4411C10.804 15.4411 10.4375 15.2893 10.1672 15.019C9.89693 14.7487 9.74509 14.3822 9.74509 13.9999C9.74509 13.6177 9.89693 13.2511 10.1672 12.9809ZM16.8927 12.9809C17.163 12.7106 17.5295 12.5588 17.9118 12.5588C18.294 12.5588 18.6605 12.7106 18.9308 12.9809C19.2011 13.2511 19.3529 13.6177 19.3529 13.9999C19.3529 14.3822 19.2011 14.7487 18.9308 15.019C18.6605 15.2893 18.294 15.4411 17.9118 15.4411C17.5295 15.4411 17.163 15.2893 16.8927 15.019C16.6224 14.7487 16.4706 14.3822 16.4706 13.9999C16.4706 13.6177 16.6224 13.2511 16.8927 12.9809Z\"\n fill=\"#111111\"\n />\n </g>\n </g>\n </g>\n <defs>\n <clipPath id=\"clip0_0_1\">\n <rect\n width=\"1440\"\n height=\"900\"\n fill=\"white\"\n transform=\"translate(-76 -26)\"\n />\n </clipPath>\n </defs>\n </svg>\n <OrganizationNameContainer />\n </LogoContainer>\n);\n" }, "Calimero.Curb.Settings.TabSwitch": { "": "const selectedTabIndex = props.selectedTabIndex ?? 0;\nconst setSelectedTabIndex = props.setSelectedTabIndex;\nconst userCount = props.userCount ?? 0;\n\nconst Popup = styled.div`\n display: flex;\n align-items: center;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n padding-top: 1rem;\n border-bottom: solid 1px #282933;\n margin-bottom: 1rem;\n`;\n\nconst SwitchOption = styled.div`\n display: flex;\n column-gap: 0.5rem;\n padding-right: 1rem;\n ${({ leftPadding }) => leftPadding && 'padding-left: 1rem;'}\n cursor: pointer;\n ${({ selected }) => (selected ? 'color: #5765F2' : 'color: #fff;')}\n`;\n\nconst items = [\n {\n name: 'About',\n icon: 'bi bi-info-circle-fill',\n },\n {\n name: `Members ${userCount}`,\n icon: 'bi bi-people-fill',\n },\n];\n\nreturn (\n <Popup>\n {items.map((item, index) => (\n <SwitchOption\n selected={selectedTabIndex === index}\n onClick={() => setSelectedTabIndex(index)}\n >\n <i className={item.icon}></i>\n <p>{item.name}</p>\n </SwitchOption>\n ))}\n </Popup>\n);\n" }, "Calimero.TaskChain.SideMenu.BoardItem": { "": "const BoardListItem = styled.div`\n display: flex;\n flex-direction: row;\n height: 40px;\n width: 100%;\n justify-content: space-between;\n padding-left: 16px;\n padding-right: 16px;\n align-items: center;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n :hover {\n text-decoration: none;\n background-color: #777583;\n color: #ffffff;\n }\n border-radius: 4px;\n gap: 4px;\n ${({ selected }) => (selected ? 'background-color: #25252A;' : '')};\n ${({ selected }) => (selected ? 'color: #FFFFFF;' : 'color: #777583;')};\n ${({ disabled }) => (disabled ? 'cursor: not-allowed;' : 'cursor: pointer;')};\n`;\n\nconst BoardListItemCircle = styled.div`\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background-color: #d0fc42;\n`;\n\nconst BoardListItemName = styled.div`\n display: flex;\n flex-direction: row;\n gap: 8px;\n align-items: center;\n`;\n\nconst GearIcon = styled.a`\n color: #ffffff;\n :hover {\n color: #0e0e10;\n }\n`;\n\nconst Text = styled.p`\n padding: 0;\n margin: 0;\n max-width: 100px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst SuccessIcon = styled.div`\n color: #00ff66;\n`;\n\nconst ErrorIcon = styled.div`\n color: #dc3545;\n`;\n\nreturn (\n <BoardListItem\n key={props.id + props.name}\n id={props.id}\n selected={props.selectedProjectId === props.id}\n onClick={() => {\n if (props.id) {\n props.onClick();\n }\n }}\n onMouseEnter={props.onMouseEnter}\n onMouseLeave={props.onMouseLeave}\n disabled={!props.id}\n >\n <BoardListItemName>\n <BoardListItemCircle />\n <Text>{props.name}</Text>\n </BoardListItemName>\n {props.boardStatus.status ? (\n props.boardStatus.status === 'Saving' ? (\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.TaskChain.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : props.boardStatus.status === 'Saved' ? (\n <SuccessIcon>\n <i className=\"bi bi-check\"></i>\n </SuccessIcon>\n ) : props.boardStatus.status === 'Error' ? (\n <ErrorIcon>\n <i className=\"bi bi-x-circle\"></i>\n </ErrorIcon>\n ) : null\n ) : (\n props.hoverId === props.id && (\n <GearIcon onClick={props.onSettingsClick}>\n <i className=\"bi bi-gear\"></i>\n </GearIcon>\n )\n )}\n </BoardListItem>\n);\n" }, "Calimero.TaskChain.BoardContainer.AddColumnDialog": { "": "const addColumn = props.addColumn;\nconst setAddColumnDialogOpen = props.setAddColumnDialogOpen;\nconst functionLoader = props.functionLoader;\nconst onChangeColumnName = props.onChangeColumnName;\nconst columnName = props.columnName;\nconst componentOwnerId = props.componentOwnerId;\nconst addColumnNameMissing = props.addColumnNameMissing;\nconst setColumnName = props.setColumnName;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Title = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: end;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 14px;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <Header>\n <CloseButton\n onClick={() => {\n setAddColumnDialogOpen(false);\n setColumnName('');\n }}\n >\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <Title\n onChange={onChangeColumnName}\n value={columnName}\n placeholder=\"Add Name\"\n />\n {addColumnNameMissing && <MissingTitle>Missing name</MissingTitle>}\n </FieldContainer>\n <FunctionButton onClick={addColumn}>\n {functionLoader ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n 'Add'\n )}\n </FunctionButton>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.TaskChain.SideMenu.AddMemberDialog": { "": "const createMember = props.createMember;\nconst open = props.open;\nconst onChangeShowAddMemberDialog = props.onChangeShowAddMemberDialog;\nconst functionLoader = props.functionLoader;\nconst componentOwnerId = props.componentOwnerId;\nconst addBoardNameMissing = props.addBoardNameMissing;\n\nconst OverlayContainer = styled.div`\n left: 12px;\n right: 12px;\n bottom: 0px;\n top: 40px;\n position: absolute;\n z-index: 20;\n display: flex;\n background-color: rgba(0, 0, 0, 0.5);\n justify-content: center;\n padding-top: 100px;\n`;\n\nconst PopupContainer = styled.div`\n position: relative;\n background-color: #1d1d21;\n padding: 1rem;\n width: 489px;\n height: fit-content;\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Title = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 20px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Input = styled.input`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n flex: 1;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n ::placeholder {\n color: #d0fc42;\n }\n border: none;\n`;\n\nconst Label = styled.label`\n color: #777583;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n text-align: left;\n width: 30%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n background-color: transparent;\n :focus {\n outline-color: #d0fc42;\n outline-style: solid;\n outline-width: 1px;\n }\n border: none;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: end;\n align-items: center;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex-direction: row;\n align-items: center;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n margin-top: 16px;\n margin-bottom: 16;\n`;\n\nconst FieldContainer = styled.div`\n position: relative;\n width: 100%;\n`;\n\nconst MissingText = styled.div`\n position: absolute;\n right: 36%;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst MissingTitle = styled.div`\n position: absolute;\n left: 14px;\n top: 75%;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-weight: 400;\n line-height: 24px;\n letter-spacing: 0em;\n color: red;\n`;\n\nconst FunctionButton = styled.button`\n background-color: #d0fc42;\n :hover {\n opacity: 0.8;\n }\n color: #0e0e10;\n border-radius: 4px;\n margin-top: 1rem;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nreturn (\n <OverlayContainer>\n <PopupContainer>\n <Header>\n <CloseButton onClick={() => onChangeShowAddBoardDialog(false)}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseButton>\n </Header>\n <Divider />\n <FieldContainer>\n <Title\n onChange={onChangeBoardName}\n value={boardName}\n placeholder=\"Add Name\"\n />\n {addBoardNameMissing && <MissingTitle>Missing name</MissingTitle>}\n </FieldContainer>\n <FunctionButton onClick={createBoard}>\n {functionLoader ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n 'Create'\n )}\n </FunctionButton>\n </PopupContainer>\n </OverlayContainer>\n);\n" }, "Calimero.TaskChain.BoardContainer.Task.AssigneeDropdown": { "": "const { componentOwnerId, assignee, setAssignee, projectId, contract } = props;\n\nconst SelectTrigger = styled('Select.Trigger')`\n display: flex;\n flex-direction: row;\n align-items: center;\n height: 31px;\n background-color: #373d43;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-weight: 400;\n line-height: 21px;\n letter-spacing: 0em;\n border: none;\n border-radis: 0.5rem;\n gap: 0.25rem;\n :focus {\n outline-color: transparent;\n outline-style: solid;\n outline-width: 1px;\n }\n`;\n\nconst SelectGroup = styled('Select.Group')`\n display: flex;\n flex-direction: column;\n padding: 16px;\n background-color: #0e0e10;\n color: #777583;\n width: 328px;\n border-radius: 0.5rem;\n`;\n\nconst SelectContent = styled('Select.Content')`\n z-index: 100;\n`;\n\nconst SelectItem = styled('Select.Item')`\n display: flex;\n column-gap: 0.5rem;\n :hover {\n background-color: #d0fc42;\n }\n border: none;\n :focus {\n outline-color: transparent;\n outline-style: solid;\n outline-width: 1px;\n }\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.25rem;\n`;\n\nconst SelectIcon = styled('Select.Icon')`\n color: #fff;\n transition: transform 3s ease-in-out;\n transform: rotate(${({ open }) => (open ? '180deg' : '0deg')});\n`;\n\nconst ButtonsContainer = styled.div`\n display: flex;\n flex-direction: row;\n gap: 8px;\n`;\n\nconst SelectedMember = styled.div`\n display: flex;\n column-gap: 0.5rem;\n padding: 0.5rem;\n`;\n\nconst Header = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 1rem;\n`;\n\nconst CloseButton = styled.div`\n background-color: transparent;\n :hover {\n color: #fff;\n }\n`;\n\nconst Text = styled.div`\n padding: 0;\n margin: 0;\n`;\n\nconst ResetButton = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 0.3rem;\n background-color: transparent;\n color: #777583;\n :hover {\n color: #fff;\n }\n`;\n\nconst placeholder = 'Assign a person';\nconst label = 'Assign to task';\nconst NO_ASSIGNEE = 'no_assignee';\n\nconst accountId = context.accountId;\n\nconst [members, setMembers] = useState([]);\nconst [isOpen, setIsOpen] = useState(false);\n\nconst toggleOpen = () => {\n setIsOpen((prevIsOpen) => !prevIsOpen);\n};\n\nconst getMembers = useCallback(async (projectId) => {\n try {\n Near.asyncCalimeroView(contract, 'get_members', {\n project_id: projectId,\n }).then((members) => {\n setMembers(\n members\n .map((member) => ({ id: member }))\n .filter((member) => member.id !== accountId),\n );\n });\n } catch (e) {\n console.log('getMembers', e);\n }\n}, []);\n\nuseEffect(() => {\n if (projectId) {\n getMembers(projectId);\n }\n}, [projectId]);\n\nreturn (\n <Select.Root\n value={assignee}\n onValueChange={setAssignee}\n placeholder={placeholder}\n open={isOpen}\n onOpenChange={toggleOpen}\n >\n <ButtonsContainer>\n <SelectTrigger>\n <Select.Value value={assignee}>\n {assignee !== NO_ASSIGNEE && assignee !== null ? (\n <SelectedMember>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: assignee,\n componentOwnerId,\n }}\n />\n <Text>{assignee}</Text>\n </SelectedMember>\n ) : (\n placeholder\n )}\n </Select.Value>\n <SelectIcon open={isOpen}>\n <i className=\"bi bi-caret-down-fill\"></i>\n </SelectIcon>\n </SelectTrigger>\n {assignee !== NO_ASSIGNEE && assignee !== null && (\n <ResetButton\n type=\"button\"\n onClick={(e) => {\n setAssignee(NO_ASSIGNEE);\n }}\n >\n <i className=\"bi bi-x-circle-fill\"></i>\n Remove\n </ResetButton>\n )}\n </ButtonsContainer>\n <SelectContent position=\"popper\" sideOffset={10}>\n <Select.Viewport>\n <SelectGroup>\n <Header>\n <Select.Label>{label}</Select.Label>\n <CloseButton type=\"button\" onClick={() => setIsOpen(false)}>\n <i className=\"bi bi-x\"></i>\n </CloseButton>\n </Header>\n <SelectItem\n id={accountId}\n key={accountId}\n value={accountId}\n ref=\"forwardedRef\"\n >\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: accountId,\n componentOwnerId,\n }}\n />\n <Select.ItemText>Assign to me</Select.ItemText>\n <Select.ItemIndicator>\n <i className=\"bi bi-check\"></i>\n </Select.ItemIndicator>\n </SelectItem>\n {members?.map((member, id) => (\n <SelectItem id={id} key={id} value={member.id} ref=\"forwardedRef\">\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: member.id,\n componentOwnerId,\n }}\n />\n <Select.ItemText>{member.id}</Select.ItemText>\n </SelectItem>\n ))}\n </SelectGroup>\n </Select.Viewport>\n </SelectContent>\n </Select.Root>\n);\n" }, "Calimero.DocsChain.ArticleView": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst PageContainer = styled.div`\n width: 100%;\n height: 100vh;\n padding-left: 60px;\n padding-right: 60px;\n background-color: #0e0e10;\n color: #ffffff;\n`;\n\nState.init({\n articleId: props.articleId,\n editArticle: false,\n});\n\nState.update({\n ...state,\n article: Near.calimeroView(contract, 'get_article', {\n article_id: props.articleId,\n }),\n});\n\nconsole.log(state.article);\n\nconst saveArticle = () => {\n if (!state.editorInput) {\n return;\n }\n const params = {\n article_id: state.articleId,\n body: state.editorInput,\n };\n Near.fakCalimeroCall(contract, 'post_article', params);\n};\n\nreturn (\n <PageContainer>\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Navbar.HorizontalNavbar'}\n config={redirectConfig}\n />\n <div className=\"d-flex\">\n <Widget\n src={'calimero.near/widget/Calimero.DocsChain.Sidebar.DocsSidebar'}\n config={redirectConfig}\n props={{\n onOpenCreatePage: state.onOpenCreatePage,\n createPageOpen: state.createPage.open,\n }}\n />\n <div>\n <h1>Article: {state.articleId}</h1>\n {/* === BUTTON - EDIT ARTICLE === */}\n {\n <button\n onClick={() => {\n State.update({\n ...state,\n editArticle: true,\n editorInput: state.article.body,\n });\n }}\n >\n Edit Article\n </button>\n }\n {/* === BUTTON - SAVE ARTICLE === */}\n {state.editArticle && (\n <>\n <button\n type=\"button\"\n className=\"btn btn-success\"\n onClick={() => {\n saveArticle();\n }}\n >\n Save Article{' '}\n </button>\n\n <button\n type=\"button\"\n className=\"btn btn-danger\"\n onClick={() => {\n State.update({\n ...state,\n editArticle: false,\n editorInput: undefined,\n });\n }}\n >\n Cancel{' '}\n </button>\n </>\n )}\n\n {/* === BUTTON - VIEW HISTORY === */}\n <span className=\"ps-4\">\n <button\n onClick={() => {\n State.update({\n ...state,\n viewHistory: !state.viewHistory,\n });\n }}\n >\n View History\n </button>\n </span>\n {/* === EDIT ARTICLE === */}\n {state.editArticle && (\n <>\n <div className=\"d-flex gap-2\" style={{ minHeight: '300px' }}>\n <div className=\"w-50\">\n <Widget\n src=\"calimero.near/widget/Calimero.DocsChain.MarkdownEditorIframe\"\n config={redirectConfig}\n props={{\n initialText: state.article.body,\n onChange: (newBody) =>\n State.update({\n editorInput: newBody,\n }),\n }}\n />\n </div>\n <div className=\"w-50\">\n <Widget\n src=\"calimero.near/widget/Calimero.DocsChain.SocialMarkdown\"\n config={redirectConfig}\n props={{ text: state.editorInput }}\n />\n </div>\n </div>\n </>\n )}\n {/* MARKDOWN and TAGS list when user doesn't edit article */}\n {!state.editArticle && (\n <>\n <Markdown text={state.editorInput || state.article.body} />\n </>\n )}\n {/* === VIEW HISTORY === */}\n {state.viewHistory && (\n <div className=\"mt-3 ps-5\">Not implemented yet</div>\n )}\n {/* === FOOTER === */}\n <Widget\n src={`calimero.near/widget/Calimero.DocsChain.ArticleFooter`}\n config={redirectConfig}\n props={{\n author: state.article.author,\n lastEditor: state.article.author, // expand contract to allow editors\n timeLastEdit: state.article.timestamp,\n version: state.article.version,\n }}\n />\n </div>\n </div>\n </PageContainer>\n);\n" }, "Calimero.Curb.Popups.CreateChannelPopup": { "": "const Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n position: relative;\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n outline: none;\n border: none;\n border-radius: 4px;\n background-color: #0e0e10;\n`;\n\nconst customStyle = {\n border: '1px solid #dc3545',\n outline: 'none',\n};\n\nconst FunctionButton = styled.button`\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.base};`};\n :hover {\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.hover};`};\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n position: absolute;\n right: 1rem;\n cursor: pointer;\n`;\n\nconst {\n title,\n toggle,\n placeholder,\n createChannel,\n buttonText,\n componentOwnerId,\n channelNameValidator,\n colors,\n} = props;\n\nconst BaseModal = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.BaseModal`}\n props={{\n ...props,\n componentOwnerId,\n }}\n />\n);\n\nconst [isOpen, setIsOpen] = useState(false);\nconst [isProcessing, setIsProcessing] = useState(false);\nconst [inputValue, setInputValue] = useState('');\nconst [validInput, setValidInput] = useState(false);\nconst [errorMessage, setErrorMessage] = useState('');\nconst [visibility, setVisibility] = useState('public');\nconst [readOnly, setReadOnly] = useState('no');\n\nconst runProcess = () => {\n setIsProcessing(true);\n createChannel(inputValue, visibility === 'private', readOnly === 'yes').then(\n (receipt) => {\n if (receipt === undefined) {\n setIsProcessing(false);\n setValidInput(false);\n setErrorMessage('Error creating channel.');\n } else {\n setIsProcessing(false);\n setIsOpen(false);\n }\n },\n );\n};\n\nconst onOpenChange = (isOpen) => {\n if (isProcessing && !isOpen) {\n return;\n }\n setIsOpen(isOpen);\n};\n\nconst handleClosePopup = () => {\n if (isProcessing) return;\n setIsOpen(false);\n};\nconst isInvalid =\n inputValue && channelNameValidator && !validInput && errorMessage;\n\nconst ErrorWrapper = styled.div`\n color: #dc3545;\n /* Body/Small */\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-top: 6px;\n`;\n\nconst EmptyMessageContainer = styled.div`\n height: 27px;\n`;\n\nconst IconSvg = styled.svg`\n position: absolute;\n top: 50%;\n right: 13px;\n`;\n\nconst ExclamationIcon = () => (\n <IconSvg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"#dc3545\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M8.99951 2.74918C5.54773 2.74918 2.74951 5.5474 2.74951 8.99918C2.74951 12.451 5.54773 15.2492 8.99951 15.2492C12.4513 15.2492 15.2495 12.451 15.2495 8.99918C15.2495 5.5474 12.4513 2.74918 8.99951 2.74918ZM1.74951 8.99918C1.74951 4.99511 4.99545 1.74918 8.99951 1.74918C13.0036 1.74918 16.2495 4.99511 16.2495 8.99918C16.2495 13.0032 13.0036 16.2492 8.99951 16.2492C4.99545 16.2492 1.74951 13.0032 1.74951 8.99918ZM8.334 5.058C8.42856 4.95669 8.56093 4.89918 8.69951 4.89918H9.29951C9.4381 4.89918 9.57046 4.95669 9.66503 5.058C9.75959 5.15931 9.80786 5.29532 9.79833 5.43358L9.49833 9.78358C9.48025 10.0457 9.2623 10.2492 8.99951 10.2492C8.73672 10.2492 8.51878 10.0457 8.5007 9.78358L8.2007 5.43358C8.19116 5.29532 8.23944 5.15931 8.334 5.058ZM9.89951 12.2992C9.89951 12.7962 9.49657 13.1992 8.99951 13.1992C8.50246 13.1992 8.09951 12.7962 8.09951 12.2992C8.09951 11.8021 8.50246 11.3992 8.99951 11.3992C9.49657 11.3992 9.89951 11.8021 9.89951 12.2992Z\"\n fill=\"#DC3545\"\n />\n </IconSvg>\n);\n\nconst InputWrapper = styled.div`\n position: relative;\n`;\n\nconst popupContent = (\n <>\n <CloseButton onClick={handleClosePopup}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n <Text>{title}</Text>\n <InputWrapper>\n <Input\n onChange={(e) => {\n setInputValue(e.target.value);\n if (channelNameValidator) {\n const { isValid, error } = channelNameValidator(e.target.value);\n setValidInput(isValid);\n setErrorMessage(error ? error : '');\n }\n }}\n value={inputValue}\n placeholder={placeholder}\n disabled={isProcessing}\n style={isInvalid ? customStyle : {}}\n />\n {isInvalid && <ExclamationIcon />}\n </InputWrapper>\n {isInvalid ? (\n <ErrorWrapper>{errorMessage}</ErrorWrapper>\n ) : (\n <EmptyMessageContainer />\n )}\n <div className=\"mb-3\">\n <label className=\"form-label\">Visibility:</label>\n <div className=\"d-flex\">\n <div className=\"form-check form-check-inline\">\n <input\n className=\"form-check-input\"\n type=\"radio\"\n name=\"visibility\"\n id=\"public\"\n value=\"public\"\n checked={visibility === 'public'}\n onChange={() => setVisibility('public')}\n disabled={isSubmitting}\n />\n <label className=\"form-check-label\" htmlFor=\"public\">\n Public\n </label>\n </div>\n <div className=\"form-check form-check-inline\">\n <input\n className=\"form-check-input\"\n type=\"radio\"\n name=\"visibility\"\n id=\"private\"\n value=\"private\"\n checked={visibility === 'private'}\n onChange={() => setVisibility('private')}\n disabled={isSubmitting}\n />\n <label className=\"form-check-label\" htmlFor=\"private\">\n Private\n </label>\n </div>\n </div>\n </div>\n\n <div className=\"mb-3\">\n <label className=\"form-label\">Read-only:</label>\n <div className=\"d-flex\">\n <div className=\"form-check form-check-inline\">\n <input\n className=\"form-check-input\"\n type=\"radio\"\n name=\"readOnly\"\n id=\"yes\"\n value=\"yes\"\n checked={readOnly === 'yes'}\n onChange={() => setReadOnly('yes')}\n disabled={isSubmitting}\n />\n <label className=\"form-check-label\" htmlFor=\"yes\">\n Yes\n </label>\n </div>\n <div className=\"form-check form-check-inline\">\n <input\n className=\"form-check-input\"\n type=\"radio\"\n name=\"readOnly\"\n id=\"no\"\n value=\"no\"\n checked={readOnly === 'no'}\n onChange={() => setReadOnly('no')}\n disabled={isSubmitting}\n />\n <label className=\"form-check-label\" htmlFor=\"no\">\n No\n </label>\n </div>\n </div>\n </div>\n\n <FunctionButton\n onClick={runProcess}\n disabled={inputValue ? isInvalid : true}\n >\n {isProcessing ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n buttonText\n )}\n </FunctionButton>\n </>\n);\n\nreturn (\n <BaseModal\n toggle={toggle}\n content={popupContent}\n open={isOpen}\n onOpenChange={onOpenChange}\n />\n);\n" }, "Calimero.Curb.Navbar.DetailsDropdown": { "": "const activeChat = props.activeChat;\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\n\nconst DropdownSelector = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n cursor: pointer;\n padding-left: 0.875rem;\n padding-top: 0.5rem;\n`;\n\nconst SelectedChannelName = styled.h4`\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%;\n @media (max-width: 1024px) {\n font-size: 18px;\n font-weight: 400;\n }\n`;\n\nconst ChevronIcon = styled.i`\n font-size: 1rem;\n @media (max-width: 1024px) {\n display: none;\n }\n cursor: pointer;\n color: #777583;\n`;\n\nconst MobileCogIcon = styled.i`\n display: none;\n @media (max-width: 1024px) {\n display: block;\n }\n cursor: pointer;\n color: #777583;\n font-size: 0.8rem;\n padding-bottom: 4px;\n`;\n\nif (activeChat.type === 'channel') {\n const toggle = (\n <DropdownSelector>\n <SelectedChannelName>{activeChat.name}</SelectedChannelName>\n <>\n <ChevronIcon className=\"bi bi-gear-fill\" />\n <MobileCogIcon className=\"bi bi-info-circle-fill\" />\n </>\n </DropdownSelector>\n );\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Popups.ChannelDetailsPopup`}\n props={{\n toggle,\n chat: activeChat,\n curbApi,\n componentOwnerId,\n }}\n />\n );\n}\nconst title =\n activeChat.type === 'direct_message' ? activeChat.id : activeChat.name;\nreturn <SelectedChannelName>{title}</SelectedChannelName>;\n" }, "Calimero.Curb.Main": { "": "const initialContract = props.contract || 'chat-staging.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId || 'calimero.testnet';\nconst encryptionUrl =\n props.encryptionUrl ||\n 'https://cali-encryption.euw3.staging.gcp.calimero.network/key';\nconst meroshipUrl =\n props.meroshipUrl ||\n 'wss://api.staging.calimero.network/api/v1/shards/ws-protocol-63-calimero-testnet/meroship/ws';\nconst accountId = context.accountId;\nconst enableCommunities = props.enableCommunities ?? false;\nconst onAppJoined = props.onAppJoined || (() => {});\nconst onAppJoin = props.onAppJoin || (() => {});\nconst communities = props.communities || [];\nconst getCalimeroToken = props.getCalimeroToken || (() => {});\nconst refresh = props.refresh || (() => {});\nconst debug = props.debug || false;\n\nconst getSelectedCommunity = useCallback(() => {\n return Storage.innerGet('community').then((value) => {\n if (selectedCommunity === undefined) {\n value = initialContract;\n Storage.set('community', value);\n setContract(value);\n } else {\n setContract(value);\n }\n });\n}, [initialContract]);\n\nconst [contract, setContract] = useState(null);\nconst [loggedIn, setLoggedIn] = useState(false);\nconst [initialChat, setInitialChat] = useState(null);\n\nconst handleContractChange = useCallback((contract) => {\n changeContract(contract, null);\n}, []);\n\nconst updateInitialChat = useCallback((session) => {\n Storage.set('lastSession', JSON.stringify(session));\n}, []);\n\nfunction changeContract(contract, session) {\n Storage.set('community', contract);\n setContract(contract);\n setLoggedIn(false);\n updateInitialChat(session);\n}\n\nconst getUrlSession = () => {\n const urlDerivedChatParams = extractQueryParams();\n let derivedSession = null;\n if (\n urlDerivedChatParams &&\n urlDerivedChatParams.type &&\n urlDerivedChatParams.target &&\n urlDerivedChatParams.contractName\n ) {\n if (!communities.includes(urlDerivedChatParams.contractName)) {\n console.log('Error unknown contract id on push notif.');\n return;\n }\n\n if (urlDerivedChatParams.type === 'channel') {\n derivedSession = {\n type: urlDerivedChatParams.type,\n name: urlDerivedChatParams.target,\n };\n } else if (urlDerivedChatParams.type === 'dm') {\n derivedSession = {\n type: 'direct_message',\n id: urlDerivedChatParams.target,\n };\n }\n changeContract(urlDerivedChatParams.contractName, derivedSession);\n }\n return derivedSession;\n};\n\nconst getInitialChat = useCallback(async () => {\n const derivedSession = getUrlSession();\n if (derivedSession) {\n setInitialChat(derivedSession);\n return;\n }\n Storage.innerGet('lastSession').then((session) => {\n setInitialChat(session ? JSON.parse(session) : null);\n });\n}, []);\n\nuseEffect(() => {\n getSelectedCommunity();\n getInitialChat();\n}, []);\n\nif (!contract) {\n return 'Loading';\n}\n\nconst PageContainer = styled.div`\n height: 100%;\n width: 100%;\n`;\n\nconst loginApi = useMemo(\n () => ({\n join: () => {\n onAppJoin();\n Near.fakCalimeroCall(contract, 'join');\n },\n login: () => {\n onAppJoin();\n Near.requestCalimeroFak(contract);\n },\n validateKey: () => Near.hasValidCalimeroFak(contract),\n getMembers: () => Near.asyncCalimeroView(contract, 'get_members'),\n }),\n [contract],\n);\n\nconst Logo = () => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.CurbLogo`}\n props={{\n justify: true,\n }}\n />\n);\n\n// Ping to prepare the new key, needs to be simplified\nconst ping = () => Near.fakCalimeroCall(contract, 'ping');\n\nconst App = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Logger`}\n props={{\n render: ({ Logger }) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.EventEmitter`}\n props={{\n render: ({ EventEmitter }) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.WebSocketManager`}\n props={{\n deps: { Logger, EventEmitter },\n debug,\n accountId: props.accountId,\n wsAddress: meroshipUrl,\n getAuthToken: getCalimeroToken,\n render: ({ wsApi }) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.AppContainer`}\n props={{\n ...props,\n wsApi,\n deps: { Logger, EventEmitter },\n }}\n />\n ),\n }}\n />\n ),\n }}\n />\n ),\n }}\n />\n);\n\nreturn (\n <PageContainer>\n {!loggedIn || !accountId ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Login.index`}\n props={{\n loginApi,\n accountId: context.accountId,\n componentOwnerId,\n onValidLogin: () => {\n onAppJoined(accountId);\n // todo! migrate to meroship\n ping().then(() => {\n setLoggedIn(true);\n });\n },\n logo: <Logo />,\n onAppJoin,\n refresh,\n }}\n />\n ) : (\n <App\n componentOwnerId={componentOwnerId}\n contract={contract}\n encryptionUrl={encryptionUrl}\n accountId={context.accountId}\n enableCommunities={enableCommunities}\n handleContractChange={handleContractChange}\n initialChat={initialChat}\n updateInitialChat={updateInitialChat}\n communities={communities}\n />\n )}\n </PageContainer>\n);\n" }, "Calimero.DocsChain.Sidebar.SettingsList": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst Container = styled.div`\n background-color: #0e0e10;\n width: 317px;\n`;\n\nconst Item = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n &:hover {\n color: #ffffff;\n }\n a {\n text-decoration: none;\n color: inherit;\n\n &:hover {\n color: #ffffff;\n }\n }\n :hover {\n background-color: #25252a;\n }\n cursor: pointer;\n ${({ selected }) =>\n selected\n ? 'color: #fff; background-color: #25252a;'\n : 'color: #777583; background-color: #0E0E10;'}\n`;\n\nconst TextMedium = styled.div`\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n padding-top: 1px;\n`;\n\nconst NameContainer = styled.a`\n display: flex;\n justify-content: start;\n align-items: center;\n gap: 12px;\n width: 100%;\n height: 24px;\n`;\n\nconst settingsList = [\n {\n id: 'members',\n title: 'Members',\n href: 'Members.Main',\n icon: 'bi bi-people',\n },\n];\n\nreturn (\n <Container>\n {settingsList.map((item) => (\n <Item key={item.id} selected={props.selectedId === item.id}>\n <NameContainer\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.${item.href}`,\n )}\n >\n <i\n className={`${item.icon} w-5 h-5`}\n style={{ fontSize: '1.5rem' }}\n ></i>\n <TextMedium>{item.title}</TextMedium>\n </NameContainer>\n </Item>\n ))}\n </Container>\n);\n" }, "Calimero.Curb.CommunitiesContainer": { "": "const componentOwnerId = props.componentOwnerId;\n\nconst Container = styled.div`\n display: flex;\n flex-direction: column;\n height: 100%;\n align-items: center;\n width: 100%;\n height: 100%;\n background-color: black;\n padding: 31px 60px 31px 31px;\n gap: 43px;\n`;\n\nconst [communities, setCommunities] = useState([]);\nconst [filteredCommunities, setFilteredCommunities] = useState([]);\nconst [isSearched, setIsSearched] = useState(false);\nconst [searchedTerm, setSearchedTerm] = useState('');\n\nconst handleSearch = useCallback(\n (searchTerm) => {\n const filtered = communities.filter((community) =>\n community.name.toLowerCase().startsWith(searchTerm.toLowerCase()),\n );\n setFilteredCommunities(filtered);\n setIsSearched(true);\n setSearchedTerm(searchTerm);\n },\n [setFilteredCommunities, communities],\n);\n\nconst resetSearch = useCallback(() => {\n setIsSearched(false);\n setSearchedTerm('');\n setFilteredCommunities([]);\n}, [setIsSearched]);\n\nreturn (\n <Container>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.SearchContainer`}\n props={{\n componentOwnerId,\n handleSearch,\n isSearched,\n searchedTerm,\n searchResultCount: filteredCommunities.length,\n resetSearch,\n }}\n />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.CommunityList`}\n props={{\n componentOwnerId,\n communities,\n filteredCommunities,\n isSearched,\n }}\n />\n </Container>\n);\n" }, "Calimero.DocsChain.ArticleDialog.Dialog": { "": "const componentOwnerId = props.componentOwnerId || 'fran-cali.testnet';\n\nconst Container = styled.div`\n background-color: #0e0e10;\n color: #fff;\n width: 100%;\n height: 100vh;\n`;\n\nconst Header = styled.div`\n padding-top: 12px;\n`;\n\nconst InputConainer = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: center;\n padding-top: 50px;\n padding-left: 200px;\n padding-right: 200px;\n`;\n\nconst TextInputContainer = styled.div`\n padding-top: 16px;\n display: flex;\n flex-direction: column;\n`;\n\nconst TitleArea = styled.textarea`\n width: 100%;\n border: none;\n background: transparent;\n outline: none;\n width: 100%;\n padding: 0;\n margin: 0;\n font-size: 24px;\n line-height: 120%;\n font-weight: 500;\n color: #fff;\n resize: none;\n overflow-y: hidden;\n height: auto;\n min-height: 50px;\n ::placeholder {\n color: #777583;\n }\n`;\n\nconst TextArea = styled.textarea`\n width: 100%;\n background: #111;\n background: transparent;\n border: none;\n outline: none;\n width: 100%;\n padding: 0;\n margin: 0;\n font-size: 16px;\n line-height: 150%;\n font-weight: 400;\n color: #fff;\n resize: none;\n overflow-y: hidden;\n height: auto;\n min-height: 50px;\n ::placeholder {\n color: #777583;\n }\n`;\n\nconst titleRef = useRef(null);\nconst markdownTextRef = useRef(null);\nconst [title, setTitle] = useState('');\nconst [markdownText, setMarkdownText] = useState('');\n\nconst InputText = ({ textRef, text, setText, isTitle }) => {\n const handleInputChange = (event) => {\n setText(event.target.value);\n if (textRef.current.textLength === 0) {\n textRef.current.style.height = '50px';\n }\n if (textRef.current.scrollHeight > textRef.current.clientHeight) {\n const currentHeight = parseInt(\n textRef.current.style.height.replace('px', ''),\n 10,\n );\n textRef.current.style.height = `${currentHeight + 50}px`;\n }\n };\n return (\n <>\n {isTitle ? (\n <TitleArea\n placeholder=\"Untitled\"\n value={text}\n ref={textRef}\n onChange={handleInputChange}\n style={{ height: '50px' }}\n />\n ) : (\n <TextArea\n placeholder=\"Write your document..\"\n value={text}\n ref={textRef}\n onChange={handleInputChange}\n style={{ height: '50px' }}\n />\n )}\n </>\n );\n};\n\nconst publishDocument = () => {\n console.log(title);\n console.log(markdownText);\n};\n\nreturn (\n <Container>\n <Header>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.ArticleDialog.DocumentHeader`}\n />\n </Header>\n <InputConainer>\n <div className=\"d-flex gap-2\">\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.Common.Button`}\n props={{\n onClick: () => console.log('add'),\n text: 'Add Icon',\n icon: 'bi bi-plus-circle-fill',\n }}\n />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.DocsChain.Common.Button`}\n props={{\n onClick: () => console.log('markdown'),\n text: 'Markdown help',\n icon: 'bi bi-question-circle-fill',\n }}\n />\n </div>\n <TextInputContainer>\n <InputText\n textRef={titleRef}\n text={title}\n setText={setTitle}\n isTitle={true}\n />\n <InputText\n textRef={markdownTextRef}\n text={markdownText}\n setText={setMarkdownText}\n isTitle={false}\n />\n </TextInputContainer>\n </InputConainer>\n </Container>\n);\n" }, "Calimero.Common.Popups.InputPopup": { "": "const Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n`;\n\nconst customStyle = {\n border: '1px solid #dc3545',\n outline: 'none',\n};\n\nconst FunctionButton = styled.button`\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.base};`};\n :hover {\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.hover};`};\n }\n color: #fff;\n border-radius: 4px;\n margin-top: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n position: absolute;\n right: 1rem;\n cursor: pointer;\n`;\n\nconst UserList = styled.div`\n position: absolute;\n left: 0px;\n top: 7rem;\n overflow-y: scroll;\n max-height: 150px;\n width: 100%;\n background-color: #1d1d21;\n border-radius: 4px;\n padding: 8px;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst UserListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n border-radius: 0.5rem;\n cursor: pointer;\n :hover {\n background-color: #25252a;\n }\n`;\n\nconst UserInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst UserText = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst ErrorWrapper = styled.div`\n color: #dc3545;\n /* Body/Small */\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-top: 6px;\n`;\n\nconst RulesWrapper = styled.div`\n color: #6c757d;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-top: 6px;\n`;\n\nconst EmptyMessageContainer = styled.div`\n height: 27px;\n`;\n\nconst IconSvg = styled.svg`\n position: absolute;\n top: 50%;\n right: 13px;\n`;\n\nconst ExclamationIcon = () => (\n <IconSvg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"#dc3545\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M8.99951 2.74918C5.54773 2.74918 2.74951 5.5474 2.74951 8.99918C2.74951 12.451 5.54773 15.2492 8.99951 15.2492C12.4513 15.2492 15.2495 12.451 15.2495 8.99918C15.2495 5.5474 12.4513 2.74918 8.99951 2.74918ZM1.74951 8.99918C1.74951 4.99511 4.99545 1.74918 8.99951 1.74918C13.0036 1.74918 16.2495 4.99511 16.2495 8.99918C16.2495 13.0032 13.0036 16.2492 8.99951 16.2492C4.99545 16.2492 1.74951 13.0032 1.74951 8.99918ZM8.334 5.058C8.42856 4.95669 8.56093 4.89918 8.69951 4.89918H9.29951C9.4381 4.89918 9.57046 4.95669 9.66503 5.058C9.75959 5.15931 9.80786 5.29532 9.79833 5.43358L9.49833 9.78358C9.48025 10.0457 9.2623 10.2492 8.99951 10.2492C8.73672 10.2492 8.51878 10.0457 8.5007 9.78358L8.2007 5.43358C8.19116 5.29532 8.23944 5.15931 8.334 5.058ZM9.89951 12.2992C9.89951 12.7962 9.49657 13.1992 8.99951 13.1992C8.50246 13.1992 8.09951 12.7962 8.09951 12.2992C8.09951 11.8021 8.50246 11.3992 8.99951 11.3992C9.49657 11.3992 9.89951 11.8021 9.89951 12.2992Z\"\n fill=\"#DC3545\"\n />\n </IconSvg>\n);\n\nconst InputWrapper = styled.div`\n position: relative;\n`;\n\nconst {\n title,\n toggle,\n placeholder,\n functionLoader,\n buttonText,\n componentOwnerId,\n validator,\n colors,\n isChild,\n autocomplete,\n nonInvitedUserList,\n} = props;\n\nconst BaseModal = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.BaseModal`}\n props={{\n ...props,\n componentOwnerId,\n }}\n />\n);\n\nconst [isOpen, setIsOpen] = useState(false);\nconst [isProcessing, setIsProcessing] = useState(false);\nconst [inputValue, setInputValue] = useState('');\nconst [validInput, setValidInput] = useState(false);\nconst [errorMessage, setErrorMessage] = useState('');\nconst [chatUsersNotInMembers, setChatUsersNotInMembers] = useState([]);\nconst [showAutocomplete, setShowAutocomplete] = useState(false);\n\nconst runProcess = () => {\n setIsProcessing(true);\n functionLoader(inputValue).then((receipt) => {\n if (receipt === undefined) {\n setValidInput(false);\n setErrorMessage('This user does not exist.');\n setIsProcessing(false);\n } else {\n setIsProcessing(false);\n setIsOpen(false);\n }\n });\n};\n\nconst onOpenChange = (isOpen) => {\n if (isProcessing && !isOpen) {\n return;\n }\n setIsOpen(isOpen);\n};\n\nconst handleClosePopup = () => {\n if (isProcessing) return;\n setIsOpen(false);\n};\n\nconst isInvalid = inputValue && validator && !validInput && errorMessage;\n\nconst AutocompleteContainer = ({ value, inviteUsers, selectUser }) => {\n return (\n <>\n {inviteUsers.length && (\n <UserList>\n {inviteUsers.map(\n (user, id) =>\n user.id !== value && (\n <UserListItem key={id} onClick={() => selectUser(user.id)}>\n <UserInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n active: user.active,\n componentOwnerId,\n }}\n />\n <UserText>{user.id}</UserText>\n </UserInfo>\n </UserListItem>\n ),\n )}\n </UserList>\n )}\n </>\n );\n};\n\nconst selectUser = (userId) => {\n setInputValue(userId);\n setValidInput(validator(userId));\n setShowAutocomplete(false);\n};\n\nconst popupContent = (\n <>\n <CloseButton onClick={handleClosePopup}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n <Text>{title}</Text>\n <InputWrapper>\n <Input\n onChange={(e) => {\n setInputValue(e.target.value);\n if (validator) {\n const { isValid, error } = validator(e.target.value);\n setValidInput(isValid);\n setErrorMessage(error ? error : '');\n }\n if (e.target.value) {\n setShowAutocomplete(true);\n } else {\n setShowAutocomplete(false);\n }\n }}\n value={inputValue}\n placeholder={placeholder}\n style={isInvalid ? customStyle : {}}\n />\n {isInvalid && <ExclamationIcon />}\n </InputWrapper>\n {isInvalid && errorMessage ? (\n <ErrorWrapper>{errorMessage}</ErrorWrapper>\n ) : (\n <RulesWrapper>\n Invite users whose wallets end with '.near' for access\n </RulesWrapper>\n )}\n {autocomplete &&\n inputValue &&\n nonInvitedUserList.length > 0 &&\n showAutocomplete && (\n <AutocompleteContainer\n value={inputValue}\n inviteUsers={nonInvitedUserList}\n selectUser={selectUser}\n />\n )}\n <FunctionButton\n onClick={runProcess}\n disabled={inputValue ? isInvalid : true}\n >\n {isProcessing ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n buttonText\n )}\n </FunctionButton>\n </>\n);\n\nreturn (\n <BaseModal\n toggle={toggle}\n content={popupContent}\n open={isOpen}\n onOpenChange={onOpenChange}\n isChild={isChild}\n />\n);\n" }, "Calimero.DocsChain.Sidebar.Header": { "": "const contract = props.contract || 'docschain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId ?? 'calimero.testnet';\nconst redirectConfig =\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? {}\n : { redirect: (url) => url.replace('calimero.near', componentOwnerId) };\nconst transformUrl = (url) =>\n !componentOwnerId || componentOwnerId === 'calimero.near'\n ? 'calimero.near'\n : url.replace('calimero.near', componentOwnerId);\n\nconst Container = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n background-color: #0e0e10;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n color: #777583;\n &:hover {\n color: #ffffff;\n }\n a {\n text-decoration: none;\n color: inherit;\n\n &:hover {\n color: #ffffff;\n }\n }\n width: 317px;\n`;\n\nconst TextBold = styled.div`\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n`;\n\nconst IconPlusContainer = styled.div`\n display: flex;\n cursor: pointer;\n justify-content: center;\n align-items: center;\n font-size: 1.25rem;\n`;\n\nreturn (\n <Container>\n <TextBold>{props.title}</TextBold>\n {props.onChange && (\n <IconPlusContainer onClick={props.onChange}>\n <a\n href={transformUrl(\n `#/calimero.near/widget/Calimero.DocsChain.${props.componentName}`,\n )}\n >\n <i className=\"bi bi-plus-circle\" />\n </a>\n </IconPlusContainer>\n )}\n </Container>\n);\n" }, "Calimero.Curb.Popups.ChannelDetailsPopup": { "": "const componentOwnerId = props.componentOwnerId;\nconst chat = props.chat;\nconst curbApi = props.curbApi;\nconst toggle = props.toggle;\nconst initialTab = props.aboutSelected ?? true;\n\nconst [isOpen, setIsOpen] = useState(false);\nconst [channelMeta, setChannelMeta] = useState({});\nconst [channelUserList, setChannelUserList] = useState([]);\n\nuseEffect(() => {\n curbApi.getChannelMeta(chat.name).then(setChannelMeta);\n}, []);\n\nconst generator = () => curbApi.getChannelMembers(chat.name);\nconst cachedMembers = useCache(generator, 'channel_members', {\n subscribe: true,\n});\n\nuseEffect(() => {\n if (cachedMembers) {\n setChannelUserList(cachedMembers);\n }\n}, [cachedMembers]);\n\nconst popupContent = (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Settings.DetailsContainer`}\n props={{\n componentOwnerId,\n channelName: chat.type === 'channel' ? chat.name : chat.id,\n aboutSelected,\n onSwitch: () => setAboutSelected(!aboutSelected),\n channelMeta,\n handleLeaveChannel: () => curbApi.leaveChannel(chat.name),\n userList: channelUserList,\n addMember: curbApi.inviteUser,\n promoteModerator: (accountId, isMod) =>\n curbApi.promoteModerator(accountId, chat.name, isMod),\n removeUserFromChannel: (accountId) =>\n curbApi.removeUserFromChannel(accountId, chat.name),\n curbApi,\n }}\n />\n);\n\nconst BaseModal = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.BaseModal`}\n props={{\n ...props,\n componentOwnerId,\n }}\n />\n);\n\nreturn (\n <BaseModal\n toggle={toggle}\n content={popupContent}\n open={isOpen}\n onOpenChange={setIsOpen}\n />\n);\n" }, "Calimero.TaskChain.BoardContainer.Column": { "": "const {\n tasks,\n title,\n submitComment,\n onTaskDragStop,\n onTaskDragStart,\n onTaskDragOver,\n functionLoader,\n componentOwnerId,\n createTask,\n projectId,\n contract,\n columns,\n setColumns,\n addActionStatus,\n removeColumn,\n columnIndex,\n removeTaskFromColumn,\n addTaskToColumn,\n updateTaskInColumn,\n} = props;\n\nconst ColumnContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n width: 17.5rem;\n margin-left: 2em;\n margin-right: 2em;\n background-color: transparent;\n cursor: pointer;\n :hover {\n background-color: #11121a;\n }\n padding: 1rem;\n`;\n\nconst ColumnDetailsIcon = styled.div`\n color: transparent;\n`;\n\nconst ColumnLabel = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 700;\n line-height: 150%;\n padding-top: 4px;\n padding-bottom: 4px;\n gap: 0.5rem;\n cursor: pointer;\n width: 15rem;\n :hover {\n ${ColumnDetailsIcon} {\n color: #ffffff;\n }\n }\n`;\n\nconst TasksContainer = styled.div`\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding-bottom: 16px;\n padding-left: 5px;\n max-height: 83%;\n overflow-y: scroll;\n scroll-behavior: smooth;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst AddTaskContainer = styled.div`\n display: flex;\n flex-direction: column;\n align-items: center;\n`;\n\nconst ColumnLabelText = styled.div`\n display: flex;\n justify-content: start;\n align-items: start;\n`;\n\nconst AddIcon = styled.div`\n color: #777583;\n`;\n\nconst AddTaskButton = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n background-color: #1e1f28;\n color: #ffffff;\n width: 240px;\n padding: 16px;\n gap: 4px;\n border-radius: 4px;\n :hover {\n ${AddIcon} {\n color: #ffffff;\n }\n }\n`;\n\nconst AddTaskText = styled.div`\n display: flex;\n justify-content: center;\n align-items: center;\n margin-left: 0.25rem;\n`;\n\nconst Title = styled.div`\n padding: 0;\n margin: 0;\n max-width: 100%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\nconst createTaskButton = (\n <AddTaskButton>\n <AddIcon>\n <i className=\"bi bi-plus-circle-fill\"></i>\n </AddIcon>\n <AddTaskText>Add new Card</AddTaskText>\n </AddTaskButton>\n);\n\nconst columnDetailsButton = (\n <ColumnDetailsIcon>\n <i className=\"bi bi-pen-fill\"></i>\n </ColumnDetailsIcon>\n);\n\nconst [createStatus, setCreateStatus] = useState([]);\nconst [columnName, setColumnName] = useState(title);\nconst [selectedTaskDetails, setSelectedTaskDetails] = useState(null);\nconst [isTaskDetailsVisible, setIsTaskDetailsVisible] = useState(false);\n\nconst cleanCreateTaskStatus = (title) => {\n const filteredStatuses = createStatus.filter((s) => s.title !== title);\n setCreateStatus(filteredStatuses);\n\n Near.asyncCalimeroView(contract, 'get_statuses', {\n project_id: projectId,\n }).then((columns) => {\n setColumns(columns);\n });\n};\n\nconst onViewTaskDetails = (task) => {\n setSelectedTaskDetails(task);\n setIsTaskDetailsVisible(true);\n};\n\nconst onCloseTaskDetails = () => {\n setSelectedTaskDetails(null);\n setIsTaskDetailsVisible(false);\n};\n\nconst addCreateTaskStatus = useCallback((newStatus) => {\n let isNew = true;\n let newCreateTaskStatuses = createStatus.map((status) => {\n if (status.title === newStatus.title) {\n isNew = false;\n return newStatus;\n } else {\n return status;\n }\n });\n if (isNew) {\n newCreateTaskStatuses = [...newCreateTaskStatuses, newStatus];\n }\n setCreateStatus(newCreateTaskStatuses);\n}, []);\n\nreturn (\n <>\n {isTaskDetailsVisible && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.TaskDetailsDialog`}\n props={{\n task: selectedTaskDetails,\n componentOwnerId,\n onCloseTaskDetails,\n removeTaskFromColumn,\n projectId,\n contract,\n addActionStatus,\n taskColumn: columnName,\n handleColumns,\n columnIndex,\n updateTaskInColumn,\n }}\n />\n )}\n <ColumnContainer\n droppable\n onDragOver={(e) => {\n onTaskDragOver();\n }}\n onDrop={() => onTaskDragStop(columnName)}\n >\n <ColumnLabel>\n <Title>{columnName}</Title>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.ColumnDetailsPopup`}\n props={{\n componentOwnerId,\n columnIndex,\n columnName,\n setColumnName,\n removeColumn,\n contract,\n projectId,\n addActionStatus,\n containsTasks: tasks.length > 0,\n columnDetailsButton,\n }}\n />\n </ColumnLabel>\n <TasksContainer>\n {tasks.map((task, i) => {\n return (\n <div key={task.id}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Task.TaskCard`}\n props={{\n task: task,\n status: columnName,\n index: i,\n onTaskDragStart: onTaskDragStart,\n onTaskDragStop: onTaskDragStop,\n createStatus,\n onViewTaskDetails: onViewTaskDetails,\n }}\n />\n </div>\n );\n })}\n </TasksContainer>\n <AddTaskContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.Popups.CreateTaskPopup`}\n props={{\n createTaskButton,\n componentOwnerId,\n addCreateTaskStatus,\n cleanCreateTaskStatus,\n contract,\n handleColumns,\n projectId,\n columnTitle: columnName,\n addTaskToColumn,\n columnIndex,\n }}\n />\n </AddTaskContainer>\n </ColumnContainer>\n </>\n);\n" }, "Calimero.TaskChain.SideMenu.BoardList": { "": "const {\n setSelectedProjectId,\n selectedProjectId,\n boards,\n onChangeShowEditBoardDialog,\n createBoardStatus,\n componentOwnerId,\n} = props;\n\nconst BoardList = styled.div`\n justify-content: center;\n width: 100%;\n max-height: 85%;\n padding: 10px;\n overflow-y: scroll;\n scroll-behavior: smooth;\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst [hoverId, setHoverId] = useState(-1);\n\nreturn (\n <BoardList>\n {(boards || []).map((board) => {\n const boardStatus = props.createBoardStatus.find(\n (b) => b.name === board[1],\n );\n return (\n <Widget\n key={board[0]}\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.BoardItem`}\n props={{\n id: board[0],\n name: board[1],\n onClick: () => setSelectedProjectId(board[0]),\n onMouseEnter: () => setHoverId(board[0]),\n onMouseLeave: () => setHoverId(-1),\n onSettingsClick: () => onChangeShowEditBoardDialog(true, board),\n hoverId,\n selectedProjectId,\n boardStatus,\n componentOwnerId,\n }}\n />\n );\n })}\n </BoardList>\n);\n" }, "Calimero.TaskChain.SideMenu.EditBoardOptions": { "": "const {\n onChangeShowDeleteBoardDialog,\n editedBoardId,\n addMemberInputOpen,\n componentOwnerId,\n setSelectedUser,\n setMemberInputOpen,\n selectedUser,\n boardMembers,\n selectedOption,\n users,\n addMember,\n addMemberStatus,\n onSelectRemovingMember,\n} = props;\n\nswitch (selectedOption) {\n case 1:\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.BoardOptions.BoardDetails`}\n props={{\n options: [\n {\n buttonText: 'Delete board',\n onClick: () => onChangeShowDeleteBoardDialog(true),\n },\n ],\n }}\n />\n );\n case 2:\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.BoardOptions.BoardMembers`}\n props={{\n addMemberInputOpen,\n componentOwnerId,\n setSelectedUser,\n setMemberInputOpen,\n selectedUser,\n boardMembers,\n users,\n addMember,\n addMemberStatus,\n onSelectRemovingMember,\n }}\n />\n );\n}\n" }, "Calimero.Gateway.Dropdown.Dropdown": { "": "const backgroundColor = props.backgroundColor ?? '#1E1F28';\nconst width = props.width ?? '14.5625rem';\nconst borderColor = props.border ?? '#9c9da326';\nconst expanded = props.expanded ?? false;\nconst apps = props.apps ?? [];\nconst onAppClick = props.onAppClick ?? null;\nconst componentOwnerId = props.componentOwnerId;\n\nconst applicationList = {\n id: 'applicationList',\n name: 'Application List',\n icon: (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"100%\"\n height=\"100%\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M1 2C1 1.73478 1.10536 1.48043 1.29289 1.29289C1.48043 1.10536 1.73478 1 2 1H4C4.26522 1 4.51957 1.10536 4.70711 1.29289C4.89464 1.48043 5 1.73478 5 2V4C5 4.26522 4.89464 4.51957 4.70711 4.70711C4.51957 4.89464 4.26522 5 4 5H2C1.73478 5 1.48043 4.89464 1.29289 4.70711C1.10536 4.51957 1 4.26522 1 4V2ZM6 2C6 1.73478 6.10536 1.48043 6.29289 1.29289C6.48043 1.10536 6.73478 1 7 1H9C9.26522 1 9.51957 1.10536 9.70711 1.29289C9.89464 1.48043 10 1.73478 10 2V4C10 4.26522 9.89464 4.51957 9.70711 4.70711C9.51957 4.89464 9.26522 5 9 5H7C6.73478 5 6.48043 4.89464 6.29289 4.70711C6.10536 4.51957 6 4.26522 6 4V2ZM11 2C11 1.73478 11.1054 1.48043 11.2929 1.29289C11.4804 1.10536 11.7348 1 12 1H14C14.2652 1 14.5196 1.10536 14.7071 1.29289C14.8946 1.48043 15 1.73478 15 2V4C15 4.26522 14.8946 4.51957 14.7071 4.70711C14.5196 4.89464 14.2652 5 14 5H12C11.7348 5 11.4804 4.89464 11.2929 4.70711C11.1054 4.51957 11 4.26522 11 4V2ZM1 7C1 6.73478 1.10536 6.48043 1.29289 6.29289C1.48043 6.10536 1.73478 6 2 6H4C4.26522 6 4.51957 6.10536 4.70711 6.29289C4.89464 6.48043 5 6.73478 5 7V9C5 9.26522 4.89464 9.51957 4.70711 9.70711C4.51957 9.89464 4.26522 10 4 10H2C1.73478 10 1.48043 9.89464 1.29289 9.70711C1.10536 9.51957 1 9.26522 1 9V7ZM6 7C6 6.73478 6.10536 6.48043 6.29289 6.29289C6.48043 6.10536 6.73478 6 7 6H9C9.26522 6 9.51957 6.10536 9.70711 6.29289C9.89464 6.48043 10 6.73478 10 7V9C10 9.26522 9.89464 9.51957 9.70711 9.70711C9.51957 9.89464 9.26522 10 9 10H7C6.73478 10 6.48043 9.89464 6.29289 9.70711C6.10536 9.51957 6 9.26522 6 9V7ZM11 7C11 6.73478 11.1054 6.48043 11.2929 6.29289C11.4804 6.10536 11.7348 6 12 6H14C14.2652 6 14.5196 6.10536 14.7071 6.29289C14.8946 6.48043 15 6.73478 15 7V9C15 9.26522 14.8946 9.51957 14.7071 9.70711C14.5196 9.89464 14.2652 10 14 10H12C11.7348 10 11.4804 9.89464 11.2929 9.70711C11.1054 9.51957 11 9.26522 11 9V7ZM1 12C1 11.7348 1.10536 11.4804 1.29289 11.2929C1.48043 11.1054 1.73478 11 2 11H4C4.26522 11 4.51957 11.1054 4.70711 11.2929C4.89464 11.4804 5 11.7348 5 12V14C5 14.2652 4.89464 14.5196 4.70711 14.7071C4.51957 14.8946 4.26522 15 4 15H2C1.73478 15 1.48043 14.8946 1.29289 14.7071C1.10536 14.5196 1 14.2652 1 14V12ZM6 12C6 11.7348 6.10536 11.4804 6.29289 11.2929C6.48043 11.1054 6.73478 11 7 11H9C9.26522 11 9.51957 11.1054 9.70711 11.2929C9.89464 11.4804 10 11.7348 10 12V14C10 14.2652 9.89464 14.5196 9.70711 14.7071C9.51957 14.8946 9.26522 15 9 15H7C6.73478 15 6.48043 14.8946 6.29289 14.7071C6.10536 14.5196 6 14.2652 6 14V12ZM11 12C11 11.7348 11.1054 11.4804 11.2929 11.2929C11.4804 11.1054 11.7348 11 12 11H14C14.2652 11 14.5196 11.1054 14.7071 11.2929C14.8946 11.4804 15 11.7348 15 12V14C15 14.2652 14.8946 14.5196 14.7071 14.7071C14.5196 14.8946 14.2652 15 14 15H12C11.7348 15 11.4804 14.8946 11.2929 14.7071C11.1054 14.5196 11 14.2652 11 14V12Z\"\n fill=\"white\"\n />\n </svg>\n ),\n};\n\nState.init({\n expanded: expanded,\n selectedApp: props.default ?? applicationList,\n});\n\nconst arrowUp = (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M14.7071 12.7071C14.3166 13.0976 13.6834 13.0976 13.2929 12.7071L10 9.41421L6.70712 12.7071C6.3166 13.0976 5.68343 13.0976 5.29291 12.7071C4.90238 12.3166 4.90238 11.6834 5.29291 11.2929L9.2929 7.29289C9.68342 6.90237 10.3166 6.90237 10.7071 7.29289L14.7071 11.2929C15.0976 11.6834 15.0976 12.3166 14.7071 12.7071Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nconst arrowDown = (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M7.29289 14.7071C6.90237 14.3166 6.90237 13.6834 7.29289 13.2929L10.5858 10L7.29289 6.70712C6.90237 6.3166 6.90237 5.68343 7.29289 5.29291C7.68342 4.90238 8.31658 4.90238 8.70711 5.29291L12.7071 9.2929C13.0976 9.68343 13.0976 10.3166 12.7071 10.7071L8.70711 14.7071C8.31658 15.0976 7.68342 15.0976 7.29289 14.7071Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nconst Dropdown = styled.div`\n display: ${(props) => (props.expanded ? 'block' : 'none')};\n width: 100%;\n border-top: 1px solid ${borderColor};\n opacity: ${(props) => (props.expanded ? 1 : 0)};\n max-height: ${(props) => (props.expanded ? '1000px' : '0')};\n transition:\n max-height 0.3s ease-in-out,\n opacity 0.3s ease-in-out; /* Apply transitions */\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #9c9da326;\n`;\n\nconst DropdownContainer = styled.div`\n width: ${width};\n overflow: hidden;\n border-radius: 0.25rem;\n background-color: ${backgroundColor};\n`;\n\nconst SelectedApp = styled.div`\n background-color: ${backgroundColor};\n width: 100%;\n cursor: pointer;\n align-items: center;\n display: inline-flex;\n flex-direction: row;\n justify-content: space-between;\n`;\n\nconst Arrow = styled.div`\n padding: 1rem;\n`;\n\nconst handleWidgetClick = (app) => {\n State.update({\n expanded: false,\n selectedApp: app,\n });\n onAppClick(app);\n};\n\nconst toggleDropdown = () => {\n State.update({\n expanded: !state.expanded,\n });\n};\n\nreturn (\n <DropdownContainer>\n <SelectedApp onClick={toggleDropdown}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Gateway.Dropdown.Element`}\n props={{\n app: state.selectedApp,\n backgroundColor: backgroundColor,\n componentOwnerId: componentOwnerId,\n }}\n />\n {apps.length > 0 && <Arrow>{state.expanded ? arrowUp : arrowDown}</Arrow>}\n </SelectedApp>\n <Dropdown expanded={state.expanded}>\n {apps.map((app, index) => {\n return (\n <div key={app.id}>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Gateway.Dropdown.Element`}\n props={{\n app: app,\n onClick: () => handleWidgetClick(app),\n backgroundColor: backgroundColor,\n componentOwnerId: componentOwnerId,\n }}\n />\n {index !== apps.length - 1 && <Divider />}{' '}\n {/* Render divider for all but the last element */}\n </div>\n );\n })}\n </Dropdown>\n </DropdownContainer>\n);\n" }, "Calimero.TaskChain.ProfileIcon.UserProfileIcon": { "": "const ProfileIconContainer = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n ${({ width }) => width && `width: ${width};`}\n ${({ height }) => height && `height: ${width};`}\n`;\n\nconst ActiveStatusCricle = styled.div`\n position: absolute;\n bottom: -2px;\n right: -2px;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n ${({ active }) =>\n active ? 'background-color: #00FF66;' : 'background-color: #777583;'}\n border: 1px solid #1A1A1D;\n`;\nconst accountId = props.accountId;\nconst width = props.width ?? '24px';\nconst height = props.height ?? '24px';\n\nreturn (\n <ProfileIconContainer width={width} height={height}>\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.Image`}\n props={{\n accountId,\n alt: `profile-icon-${accountId}`,\n className: 'rounded-circle',\n style: { width: width, height: height, objectFit: 'cover' },\n thumbnail: 'thumbnail',\n fallbackUrl: 'https://i.imgur.com/e8buxpa.png',\n }}\n />\n </ProfileIconContainer>\n);\n" }, "Calimero.TaskChain.ProfileIcon.Image": { "": "// Forked from: rubycoptest.testnet/widget/Image\n\nconst accountId = props.accountId;\nconst className = props.className;\nconst style = props.style;\nconst alt = props.alt;\nconst fallbackUrl = props.fallbackUrl;\nconst thumbnail = props.thumbnail;\nconst componentOwnerId = props.componentOwnerId;\n\nconst [imageUrl, setImageUrl] = useState(\n 'https://ipfs.near.social/ipfs/bafkreidoxgv2w7kmzurdnmflegkthgzaclgwpiccgztpkfdkfzb4265zuu',\n);\n\nfunction toUrl(image) {\n return (\n (image.ipfs_cid\n ? `https://ipfs.near.social/ipfs/${image.ipfs_cid}`\n : image.url) || fallbackUrl\n );\n}\n\nconst thumb = (imageUrl) =>\n thumbnail && imageUrl && !imageUrl.startsWith('data:image/')\n ? `https://i.near.social/${thumbnail}/${imageUrl}`\n : imageUrl;\n\nuseEffect(() => {\n const profile = Social.getr(`${accountId}/profile`);\n const image = profile.image;\n if (image) {\n const imageUrl = thumb(toUrl(image));\n setImageUrl(imageUrl);\n } else {\n setImageUrl(fallbackUrl);\n }\n}, [accountId, fallbackUrl, thumbnail]);\n\nreturn <img className={className} style={style} src={imageUrl} alt={alt} />;\n" }, "Calimero.TaskChain.UserDropdown": { "": "const users = props.users;\nconst componentOwnerId = props.componentOwnerId;\nconst onClick = props.onClick;\nconst onClose = props.onClose;\nconst onAdd = props.onAdd;\nconst onSelect = props.onSelect;\nconst selectedUser = props.selectedUser;\nconst addMemberStatus = props.addMemberStatus;\n\nconst UserListItem = styled.div`\n display: flex;\n justify-content: space-between;\n align-items: center;\n color: #777583;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n padding-right: 1rem;\n cursor: pointer;\n :hover {\n background-color: #d0fc42;\n }\n background-color: #1d1d21;\n`;\n\nconst Container = styled.div`\n z-index: 30;\n outline-color: #777583;\n outline-style: solid;\n outline-width: 1px;\n ${({ selected }) =>\n selected\n ? 'border-radius: 4px 4px 4px 4px;'\n : 'border-radius: 4px 4px 0px 0px;'}\n width: inherit;\n font-family: Helvetica Neue;\n`;\n\nconst UserList = styled.div`\n overflow-y: scroll;\n max-height: 200px;\n position: absolute;\n z-index: 30;\n outline-color: #777583;\n outline-style: solid;\n outline-width: 1px;\n border-radius: 0px 0px 4px 4px;\n width: inherit;\n scroll-behavior: smooth;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-track {\n background-color: #1d1d21;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst UserInfo = styled.div`\n display: flex;\n column-gap: 0.5rem;\n`;\n\nconst UserId = styled.div`\n display: flex;\n justify-content: start;\n align-items: center;\n width: 100%;\n`;\n\nconst AddUserInput = styled.div`\n color: #fff;\n display: flex;\n border-radius: 4px;\n height: 40px;\n padding-top: 8px;\n padding-bottom: 8px;\n padding-left: 16px;\n padding-right: 16px;\n border: none;\n width: 100%;\n background-color: #1d1d21;\n`;\n\nconst CloseUserDropdownButton = styled.div`\n color: #6b7280;\n :hover {\n color: #d0fc42;\n }\n cursor: pointer;\n position: absolute;\n right: 10px;\n top: 10px;\n`;\n\nreturn (\n <Container selected={selectedUser || users.length === 0}>\n <AddUserInput>\n {users.length > 0 ? selectedUser?.id : 'No users to add'}\n </AddUserInput>\n {!selectedUser && users.length > 0 && (\n <UserList>\n {users.map((user) => (\n <>\n <UserListItem key={user.id} onClick={() => onClick(user)}>\n <UserInfo>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ProfileIcon.UserProfileIcon`}\n props={{\n accountId: user.id,\n componentOwnerId,\n }}\n />\n <UserId>{user.id}</UserId>\n </UserInfo>\n </UserListItem>\n </>\n ))}\n </UserList>\n )}\n <CloseUserDropdownButton onClick={onClose}>\n <i className=\"bi bi-x-circle\"></i>\n </CloseUserDropdownButton>\n </Container>\n);\n" }, "Calimero.Curb.SideSelector.SideSelector": { "": "const users = props.users;\nconst channels = props.channels;\nconst componentOwnerId = props.componentOwnerId;\nconst activeChat = props.activeChat;\nconst onChatSelected = props.onChatSelected;\nconst curbApi = props.curbApi;\nconst isSidebarOpen = props.isSidebarOpen;\nconst communities = props.communities;\nconst enableCommunities = props.enableCommunities;\nconst handleContractChange = props.handleContractChange;\nconst selectedCommunity = props.selectedCommunity;\n\nconst HorizontalSeparatorLine = styled.div`\n background-color: '#BF4F74';\n height: 1px;\n background-color: #282933;\n margin-top: 1rem;\n margin-bottom: 1rem;\n @media (max-width: 1024px) {\n width: 100%;\n }\n`;\n\nconst SideMenu = styled.div`\n background-color: #0e0e10;\n padding-top: 1rem;\n width: 318px;\n overflow-y: scroll;\n height: calc(100vh - 169px);\n @media (max-width: 1024px) {\n display: none;\n }\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst SideMenuMobile = styled.div`\n display: none;\n background-color: #0e0e10;\n padding-top: 1rem;\n overflow-y: scroll;\n height: 100vh;\n @media (max-width: 1024px) {\n display: block;\n position: relative;\n z-index: 10;\n padding-top: 64px;\n width: 100%;\n padding-bottom: 30px;\n }\n scrollbar-color: black black;\n ::-webkit-scrollbar {\n width: 6px;\n }\n ::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n * {\n scrollbar-color: black black;\n }\n html::-webkit-scrollbar {\n width: 12px;\n }\n html::-webkit-scrollbar-thumb {\n background-color: black;\n border-radius: 6px;\n }\n html::-webkit-scrollbar-thumb:hover {\n background-color: black;\n }\n`;\n\nconst CommunityDropdownContainer = styled.div`\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0.25rem;\n`;\n\nconst SideMenuContent = () => {\n return (\n <>\n {enableCommunities && communities && (\n <>\n <CommunityDropdownContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Communities.CommunityDropdown`}\n props={{\n componentOwnerId,\n communities,\n handleContractChange,\n selectedCommunity,\n }}\n />\n </CommunityDropdownContainer>\n </>\n )}\n {enableCommunities && communities && <HorizontalSeparatorLine />}\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.SideSelector.ChannelsHeader`}\n props={{\n title: 'Channel',\n componentOwnerId,\n curbApi,\n }}\n />\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.SideSelector.ChannelList`}\n props={{\n channels,\n selectChannel: onChatSelected,\n selectedChannelId:\n activeChat.type === 'channel' ? activeChat.name : null,\n componentOwnerId,\n }}\n />\n <HorizontalSeparatorLine />\n <Widget\n src={`${props.componentOwnerId}/widget/Calimero.Curb.SideSelector.DMSideSelector`}\n props={{\n componentOwnerId,\n curbApi,\n users,\n onDMSelected: onChatSelected,\n selectedDM:\n activeChat.type === 'direct_message' ? activeChat.id : null,\n createDM: (value) =>\n new Promise((resolve) => {\n onChatSelected({\n id: value,\n type: 'direct_message',\n });\n resolve();\n }),\n }}\n />\n </>\n );\n};\nreturn (\n <>\n {isSidebarOpen && (\n <SideMenuMobile>\n <SideMenuContent />\n </SideMenuMobile>\n )}\n <SideMenu>\n <SideMenuContent />\n </SideMenu>\n </>\n);\n" }, "Calimero.Curb.Popups.StartDMPopup": { "": "const SuggestionsDropdown = styled.div`\n max-height: 200px;\n overflow-y: auto;\n border-radius: 4px;\n background-color: #0e0e10;\n position: absolute;\n top: 100%;\n width: 100%;\n z-index: 10;\n`;\n\nconst SuggestionItem = styled.div`\n padding: 8px 16px;\n color: #fff;\n cursor: pointer;\n &:hover {\n background-color: #1f1f21;\n }\n`;\n\nconst Text = styled.div`\n display: flex;\n column-gap: 0.5rem;\n align-items: center;\n color: #fff;\n font-family: Helvetica Neue;\n font-size: 24px;\n font-style: normal;\n font-weight: 500;\n line-height: 120%\n margin-bottom: 1rem;\n`;\n\nconst Input = styled.input`\n color: #fff;\n width: 100%;\n height: 40px;\n padding: 8px 60px 8px 16px;\n margin-top: 1rem;\n border-radius: 4px;\n background-color: #0e0e10;\n border: none;\n`;\n\nconst customStyle = {\n border: '1px solid #dc3545',\n outline: 'none',\n};\n\nconst FunctionButton = styled.button`\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.base};`};\n :hover {\n background-color: ${({ disabled }) =>\n disabled ? `${colors.disabled};` : `${colors.hover};`};\n }\n color: #fff;\n border-radius: 4px;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n border: none;\n width: 100%;\n`;\n\nconst CloseButton = styled.div`\n color: #fff;\n :hover {\n color: #5765f2;\n }\n position: absolute;\n right: 1rem;\n cursor: pointer;\n`;\n\nconst ErrorWrapper = styled.div`\n color: #dc3545;\n /* Body/Small */\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%; /* 21px */\n margin-top: 6px;\n`;\n\nconst EmptyMessageContainer = styled.div`\n height: 27px;\n`;\n\nconst IconSvg = styled.svg`\n position: absolute;\n top: 50%;\n right: 13px;\n`;\n\nconst ExclamationIcon = () => (\n <IconSvg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"#dc3545\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M8.99951 2.74918C5.54773 2.74918 2.74951 5.5474 2.74951 8.99918C2.74951 12.451 5.54773 15.2492 8.99951 15.2492C12.4513 15.2492 15.2495 12.451 15.2495 8.99918C15.2495 5.5474 12.4513 2.74918 8.99951 2.74918ZM1.74951 8.99918C1.74951 4.99511 4.99545 1.74918 8.99951 1.74918C13.0036 1.74918 16.2495 4.99511 16.2495 8.99918C16.2495 13.0032 13.0036 16.2492 8.99951 16.2492C4.99545 16.2492 1.74951 13.0032 1.74951 8.99918ZM8.334 5.058C8.42856 4.95669 8.56093 4.89918 8.69951 4.89918H9.29951C9.4381 4.89918 9.57046 4.95669 9.66503 5.058C9.75959 5.15931 9.80786 5.29532 9.79833 5.43358L9.49833 9.78358C9.48025 10.0457 9.2623 10.2492 8.99951 10.2492C8.73672 10.2492 8.51878 10.0457 8.5007 9.78358L8.2007 5.43358C8.19116 5.29532 8.23944 5.15931 8.334 5.058ZM9.89951 12.2992C9.89951 12.7962 9.49657 13.1992 8.99951 13.1992C8.50246 13.1992 8.09951 12.7962 8.09951 12.2992C8.09951 11.8021 8.50246 11.3992 8.99951 11.3992C9.49657 11.3992 9.89951 11.8021 9.89951 12.2992Z\"\n fill=\"#DC3545\"\n />\n </IconSvg>\n);\n\nconst InputWrapper = styled.div`\n position: relative;\n`;\n\nconst {\n title,\n toggle,\n placeholder,\n onAccountSelected,\n buttonText,\n componentOwnerId,\n fetchAccounts,\n validator,\n colors,\n functionLoader,\n} = props;\n\nconst BaseModal = (props) => (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.BaseModal`}\n props={{\n ...props,\n componentOwnerId,\n }}\n />\n);\n\nconst [isOpen, setIsOpen] = useState(false);\nconst [isProcessing, setIsProcessing] = useState(false);\nconst [inputValue, setInputValue] = useState('');\nconst [validInput, setValidInput] = useState(false);\nconst [errorMessage, setErrorMessage] = useState('');\nconst [suggestions, setSuggestions] = useState([]);\n\nconst inputRef = useRef('');\n\nconst isInvalid = inputValue && validator && !validInput && errorMessage;\n\nuseEffect(() => {\n inputRef.current = inputValue;\n}, [inputValue]);\n\nconst runProcess = () => {\n setIsProcessing(true);\n functionLoader(inputValue).then((_) => {\n setIsProcessing(false);\n setIsOpen(false);\n });\n};\n\nconst onOpenChange = (isOpen) => {\n if (isProcessing && !isOpen) {\n return;\n }\n setIsOpen(isOpen);\n};\n\nconst debouncedFetchUsers = (value) => {\n setTimeout(\n () =>\n fetchAccounts(value).then((users) => {\n const accounts = users.map((user) => user.id);\n setSuggestions(accounts);\n }),\n 200,\n );\n};\n\nconst handleInputChange = (e) => {\n const value = e.target.value;\n setInputValue(value);\n\n if (validator) {\n const { isValid, error } = validator(value);\n setValidInput(isValid);\n setErrorMessage(error ? error : '');\n }\n\n debouncedFetchUsers(value);\n};\n\nconst handleClosePopup = () => {\n if (isProcessing) return;\n setIsOpen(false);\n};\n\nconst handleSuggestionClick = (suggestion) => {\n onAccountSelected(suggestion);\n setIsOpen(false);\n};\n\nconst popupContent = (\n <>\n <CloseButton onClick={handleClosePopup}>\n <i className=\"bi bi-x-lg\"></i>\n </CloseButton>\n <Text>{title}</Text>\n <InputWrapper>\n <Input\n onChange={handleInputChange}\n value={inputValue}\n placeholder={placeholder}\n style={isInvalid ? customStyle : {}}\n />\n {isInvalid && <ExclamationIcon />}\n </InputWrapper>\n {isInvalid ? (\n <ErrorWrapper>{errorMessage}</ErrorWrapper>\n ) : (\n <EmptyMessageContainer />\n )}\n {suggestions.length > 0 && (\n <SuggestionsDropdown>\n {suggestions.map((suggestion, index) => (\n <SuggestionItem\n key={index}\n onClick={() => handleSuggestionClick(suggestion)}\n >\n {suggestion}\n </SuggestionItem>\n ))}\n </SuggestionsDropdown>\n )}\n <FunctionButton\n onClick={runProcess}\n disabled={inputValue ? isInvalid : true}\n >\n {isProcessing ? (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Loader.Loader`}\n props={{ size: 16 }}\n />\n ) : (\n buttonText\n )}\n </FunctionButton>\n </>\n);\n\nreturn (\n <BaseModal\n toggle={toggle}\n content={popupContent}\n open={isOpen}\n onOpenChange={onOpenChange}\n />\n);\n" }, "Calimero.Curb.NavbarContainer": { "": "const activeChat = props.activeChat;\nconst componentOwnerId = props.componentOwnerId;\nconst curbApi = props.curbApi;\nconst isSidebarOpen = props.isSidebarOpen;\nconst setIsSidebarOpen = props.setIsSidebarOpen;\nconst channelSelected = props.channelSelected;\nconst enableCommunities = props.enableCommunities;\n\nconst [appName, setAppName] = useState('');\n\nuseEffect(() => {\n curbApi.getAppName().then(setAppName);\n}, [curbApi]);\n\nconst ChannelNavbarContainer = (props) => {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.ChannelNavbarContainer`}\n props={props}\n />\n );\n};\n\nconst DMNavbarContainer = (props) => {\n return (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Navbar.CurbNavbar`}\n props={props}\n />\n );\n};\n\nconst props = {\n appName,\n activeChat,\n componentOwnerId,\n curbApi,\n isSidebarOpen,\n setIsSidebarOpen,\n channelSelected:\n activeChat.type === 'channel' ? activeChat.name : activeChat.id,\n enableCommunities,\n};\n\nreturn (\n <>\n {activeChat.type === 'channel' ? (\n <ChannelNavbarContainer {...props} />\n ) : (\n <DMNavbarContainer {...props} />\n )}\n </>\n);\n" }, "Calimero.Curb.Chat.MessageInput": { "": "const {\n componentOwnerId,\n threadReply,\n selectedChat,\n sendMessage,\n openThread,\n isThread,\n isReadOnly,\n isOwner,\n isModerator,\n} = props;\n\nconst Container = styled.div`\n position: absolute;\n bottom: 16px;\n padding-left: 16px;\n padding-right: 16px;\n padding-top: 12px;\n padding-bottom: 12px;\n background-color: #1d1d21;\n display: flex;\n align-items: end;\n @media (min-width: 1025px) {\n gap: 8px;\n border-radius: 4px;\n }\n @media (max-width: 1024px) {\n position: fixed;\n margin: 0 !important;\n left: 0;\n right: 0;\n bottom: 0px;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n gap: 4px;\n margin: 0px;\n padding-left: 8px;\n padding-right: 8px;\n padding-bottom: 12px;\n padding-top: 12px;\n width: 100% !important;\n }\n`;\n\nconst EmojiPopupContainer = styled.div`\n position: absolute;\n bottom: 70px;\n right: 2.5rem;\n`;\n\nconst UploadPopupContainer = styled.div`\n position: absolute;\n bottom: 46px;\n left: 16px;\n @media (max-width: 1024px) {\n left: 8px;\n }\n`;\n\nconst UploadContainer = styled.div`\n background-color: #25252a;\n border-radius: 2px;\n width: fit-content;\n`;\n\nconst [showEmojiPopup, setShowEmojiPopup] = useState(false);\nconst [showUpload, setShowUpload] = useState(false);\nconst [message, setMessage] = useState('');\nconst [showMarkdown, setShowMarkdown] = useState(false);\nconst [uploadedFile, setUploadedFile] = useState(null);\nconst [uploadedImage, setUploadedImage] = useState(null);\nconst [emojiSelectorOpen, setEmojiSelectorOpen] = useState(false);\nconst [error, setError] = useState('');\n\nconst updateShowMarkdown = useCallback(() => {\n if (message) {\n setShowMarkdown(!showMarkdown);\n }\n}, [message, showMarkdown]);\n\nconst handleMessageChange = useCallback((mesage) => {\n setMessage(mesage);\n}, []);\n\nconst resetFile = useCallback(() => {\n setUploadedFile(null);\n setShowUpload(false);\n}, []);\n\nconst resetImage = useCallback(() => {\n setUploadedImage(null);\n setShowUpload(false);\n}, []);\n\nconst resetMessage = useCallback(() => setMessage(''), []);\nconst emptyText = /^(\\s*<p><br><\\/p>\\s*)*$/;\nconst markdownParser = (text) => {\n const toHTML = text.replace(\n /(\\b(https?:\\/\\/[^\\s<]+\\/?)\\b)|^(#####|####|###|##|#) (.*)$|(@everyone)|(@here)|(@[a-z\\d]+[-_]*[a-z\\d]+[-_]*[a-z\\d]+\\.(near|testnet))|<p><br><\\/p>(?=\\s*$)/gim,\n (\n match,\n url,\n url2,\n heading,\n text,\n everyoneMention,\n hereMention,\n validMention,\n ) => {\n if (url || url2) {\n return `<a href=\"${url || url2}\" class=\"url-link\" target=\"_blank\">${\n url || url2\n }</a>`;\n } else if (heading) {\n return text;\n } else if (everyoneMention) {\n return `<span class='mention-everyone'>@everyone</span>`;\n } else if (hereMention) {\n return `<span class='mention-here'>@here</span>`;\n } else if (validMention) {\n return `<span class='mention mention-user-${validMention\n .replace('@', '')\n .replace(/\\./g, '\\\\.')\n .replace(/_/g, '\\\\_')}'>${validMention}</span>`;\n } else {\n return '';\n }\n },\n );\n\n return toHTML;\n};\n\nconst isActive =\n (message && !emptyText.test(markdownParser(message))) ||\n uploadedImage ||\n uploadedFile;\n\nconst handleSendMessage = useCallback(() => {\n if (\n (uploadedFile && !uploadedFile.file.cid) ||\n (uploadedImage && !uploadedImage.file.cid)\n ) {\n return;\n } else if (\n emptyText.test(markdownParser(message)) &&\n !uploadedImage &&\n !uploadedFile\n ) {\n handleMessageChange('');\n } else {\n sendMessage(\n markdownParser(message),\n uploadedImage,\n uploadedFile,\n openThread,\n );\n resetImage();\n resetFile();\n setShowUpload(false);\n setEmojiSelectorOpen(false);\n handleMessageChange('');\n }\n}, [message, uploadedImage, uploadedFile, openThread]);\n\nconst [selectedEmoji, setSelectedEmoji] = useState('');\n\nconst Wrapper = styled.div`\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: start;\n background-color: #111111;\n`;\n\nconst FullWidthWrapper = styled.div`\n width: 100%;\n display: flex;\n flex-direction: column;\n`;\n\nconst IconUploadWrapper = styled.div`\n height: 34px;\n width: 34px;\n padding: 8px;\n display: flex;\n justify-content: center;\n border-radius: 2px;\n align-items: center;\n :hover {\n background-color: #686672;\n fill: #fff;\n }\n cursor: pointer;\n fill: #686672;\n`;\n\nconst IconUpload = ({ onClick }) => (\n <IconUploadWrapper>\n <svg\n onClick={onClick}\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n className=\"bi bi-plus-circle\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z\" />\n <path d=\"M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z\" />\n </svg>\n </IconUploadWrapper>\n);\n\nconst EmojiContainer = styled.div`\n border-radius: 2px;\n margin-bottom: 4px;\n height: 26px;\n width: 26px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 2px;\n cursor: pointer;\n\n .hidden-svg {\n visibility: hidden;\n position: absolute;\n z-index: -10;\n }\n\n .visible-svg {\n visibility: visible;\n }\n\n @media (max-width: 1024px) {\n display: none;\n }\n`;\n\nconst IconEmoji = () => {\n const [hovered, setHovered] = useState(false);\n\n return (\n <EmojiContainer\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill=\"#686672\"\n className={`bi bi-emoji-wink ${hovered ? 'hidden-svg' : 'visible-svg'}`}\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z\" />\n <path d=\"M4.285 9.567a.5.5 0 0 1 .683.183A3.498 3.498 0 0 0 8 11.5a3.498 3.498 0 0 0 3.032-1.75.5.5 0 1 1 .866.5A4.498 4.498 0 0 1 8 12.5a4.498 4.498 0 0 1-3.898-2.25.5.5 0 0 1 .183-.683zM7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5zm1.757-.437a.5.5 0 0 1 .68.194.934.934 0 0 0 .813.493c.339 0 .645-.19.813-.493a.5.5 0 1 1 .874.486A1.934 1.934 0 0 1 10.25 7.75c-.73 0-1.356-.412-1.687-1.007a.5.5 0 0 1 .194-.68z\" />\n </svg>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill=\"#FFDD1D\"\n className={`bi bi-emoji-wink-fill ${\n hovered ? 'visible-svg' : 'hidden-svg'\n }`}\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM7 6.5C7 5.672 6.552 5 6 5s-1 .672-1 1.5S5.448 8 6 8s1-.672 1-1.5zM4.285 9.567a.5.5 0 0 0-.183.683A4.498 4.498 0 0 0 8 12.5a4.5 4.5 0 0 0 3.898-2.25.5.5 0 1 0-.866-.5A3.498 3.498 0 0 1 8 11.5a3.498 3.498 0 0 1-3.032-1.75.5.5 0 0 0-.683-.183zm5.152-3.31a.5.5 0 0 0-.874.486c.33.595.958 1.007 1.687 1.007.73 0 1.356-.412 1.687-1.007a.5.5 0 0 0-.874-.486.934.934 0 0 1-.813.493.934.934 0 0 1-.813-.493z\" />\n </svg>\n </EmojiContainer>\n );\n};\n\nconst IconSendSvg = styled.svg`\n margin-bottom: 8px;\n :hover {\n fill: #4e95ff;\n }\n cursor: pointer;\n`;\nconst IconSend = ({ onClick, isActive }) => (\n <IconSendSvg\n onClick={onClick}\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"18\"\n height=\"18\"\n fill={`${isActive ? '#4E95FF' : '#686672'}`}\n className=\"bi bi-send-fill\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M15.964.686a.5.5 0 0 0-.65-.65L.767 5.855H.766l-.452.18a.5.5 0 0 0-.082.887l.41.26.001.002 4.995 3.178 3.178 4.995.002.002.26.41a.5.5 0 0 0 .886-.083l6-15Zm-1.833 1.89L6.637 10.07l-.215-.338a.5.5 0 0 0-.154-.154l-.338-.215 7.494-7.494 1.178-.471-.47 1.178Z\" />\n </IconSendSvg>\n);\n\nconst Placeholder = styled.div`\n position: absolute;\n z-index: 10;\n bottom: ${({ placeholderPosition }) =>\n placeholderPosition && placeholderPosition};\n left: 68px;\n color: #686672;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n pointer-events: none;\n @media (max-width: 1024px) {\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n bottom: ${({ placeholderPositionMobile }) =>\n placeholderPositionMobile && placeholderPositionMobile};\n left: 56px;\n }\n`;\n\nconst getCustomStyle = (openThread) => {\n const customStyle = {\n width: 'calc(100% - 440px)',\n marginLeft: '2.5rem',\n marginRight: '2.5rem',\n };\n if (openThread && !isThread) {\n customStyle.width = 'calc(60% - 262px)';\n customStyle.marginRight = '1.25rem';\n } else if (!openThread && !isThread) {\n customStyle.width = 'calc(100% - 440px)';\n } else if (openThread && isThread) {\n customStyle.width = 'calc(40% - 212px)';\n customStyle.marginLeft = '0rem';\n customStyle.marginRight = '1.25rem';\n }\n return customStyle;\n};\n\nconst ReadOnlyField = styled.div`\n background-color: #111111;\n height: 2rem;\n border-radius: 4px;\n padding: 4px 8px 4px 8px;\n font-family: Helvetica Neue;\n font-size: 16px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n color: #797978;\n flex: 1;\n @media (max-width: 1024px) {\n font-size: 14px;\n display: flex;\n align-items: center;\n }\n`;\n\nconst FileUrl = styled.a`\n cursor: pointer;\n text-decoration: none;\n color: #ffdd1d;\n cursor: pointer;\n :hover {\n color: #ffdd1d;\n text-decoration: underline;\n }\n :visited {\n color: #d0fc42;\n }\n`;\n\nlet canWriteMessage = false;\nif (isReadOnly) {\n if (isModerator || isOwner) {\n canWriteMessage = true;\n } else {\n canWriteMessage = false;\n }\n} else {\n canWriteMessage = true;\n}\n\nlet placeholderPosition = '16px';\nlet placeholderPositionMobile = '16px';\nif (uploadedFile.file.cid) {\n placeholderPosition = '61px';\n placeholderPositionMobile = '51px';\n} else if (uploadedImage.file.cid) {\n placeholderPosition = '86px';\n placeholderPositionMobile = '80px';\n}\n\nconst ImageIconSvg = () => (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"#fff\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M15.75 12.75C15.75 13.7446 15.3549 14.6984 14.6517 15.4017C13.9484 16.1049 12.9946 16.5 12 16.5C11.0054 16.5 10.0516 16.1049 9.34835 15.4017C8.64509 14.6984 8.25 13.7446 8.25 12.75C8.25 11.7554 8.64509 10.8016 9.34835 10.0983C10.0516 9.39509 11.0054 9 12 9C12.9946 9 13.9484 9.39509 14.6517 10.0983C15.3549 10.8016 15.75 11.7554 15.75 12.75Z\"\n fill=\"white\"\n />\n <path\n d=\"M3 6C2.20435 6 1.44129 6.31607 0.87868 6.87868C0.316071 7.44129 0 8.20435 0 9V18C0 18.7956 0.316071 19.5587 0.87868 20.1213C1.44129 20.6839 2.20435 21 3 21H21C21.7956 21 22.5587 20.6839 23.1213 20.1213C23.6839 19.5587 24 18.7956 24 18V9C24 8.20435 23.6839 7.44129 23.1213 6.87868C22.5587 6.31607 21.7956 6 21 6H19.242C18.4464 5.99983 17.6835 5.68365 17.121 5.121L15.879 3.879C15.3165 3.31635 14.5536 3.00017 13.758 3H10.242C9.44641 3.00017 8.68348 3.31635 8.121 3.879L6.879 5.121C6.31652 5.68365 5.55358 5.99983 4.758 6H3ZM3.75 9C3.55109 9 3.36032 8.92098 3.21967 8.78033C3.07902 8.63968 3 8.44891 3 8.25C3 8.05109 3.07902 7.86032 3.21967 7.71967C3.36032 7.57902 3.55109 7.5 3.75 7.5C3.94891 7.5 4.13968 7.57902 4.28033 7.71967C4.42098 7.86032 4.5 8.05109 4.5 8.25C4.5 8.44891 4.42098 8.63968 4.28033 8.78033C4.13968 8.92098 3.94891 9 3.75 9ZM17.25 12.75C17.25 14.1424 16.6969 15.4777 15.7123 16.4623C14.7277 17.4469 13.3924 18 12 18C10.6076 18 9.27226 17.4469 8.28769 16.4623C7.30312 15.4777 6.75 14.1424 6.75 12.75C6.75 11.3576 7.30312 10.0223 8.28769 9.03769C9.27226 8.05312 10.6076 7.5 12 7.5C13.3924 7.5 14.7277 8.05312 15.7123 9.03769C16.6969 10.0223 17.25 11.3576 17.25 12.75Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nconst FileIconSvg = () => (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"#fff\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g clipPath=\"url(#clip0_952_64107)\">\n <path\n d=\"M13.9395 0H6C5.20435 0 4.44129 0.316071 3.87868 0.87868C3.31607 1.44129 3 2.20435 3 3V21C3 21.7956 3.31607 22.5587 3.87868 23.1213C4.44129 23.6839 5.20435 24 6 24H18C18.7956 24 19.5587 23.6839 20.1213 23.1213C20.6839 22.5587 21 21.7956 21 21V7.0605C20.9999 6.66271 20.8418 6.28124 20.5605 6L15 0.4395C14.7188 0.158176 14.3373 8.49561e-05 13.9395 0V0ZM14.25 5.25V2.25L18.75 6.75H15.75C15.3522 6.75 14.9706 6.59196 14.6893 6.31066C14.408 6.02936 14.25 5.64782 14.25 5.25ZM9.75 8.25V9.201L10.5735 8.7255C10.6588 8.67548 10.7532 8.64283 10.8512 8.62943C10.9492 8.61603 11.0489 8.62214 11.1445 8.64743C11.2401 8.67271 11.3298 8.71665 11.4084 8.77674C11.487 8.83682 11.5529 8.91185 11.6023 8.9975C11.6518 9.08316 11.6838 9.17776 11.6966 9.27584C11.7093 9.37393 11.7025 9.47357 11.6766 9.56902C11.6507 9.66447 11.6062 9.75386 11.5456 9.83203C11.4849 9.9102 11.4095 9.97561 11.3235 10.0245L10.5 10.5L11.3235 10.9755C11.4095 11.0244 11.4849 11.0898 11.5456 11.168C11.6062 11.2461 11.6507 11.3355 11.6766 11.431C11.7025 11.5264 11.7093 11.6261 11.6966 11.7242C11.6838 11.8222 11.6518 11.9168 11.6023 12.0025C11.5529 12.0882 11.487 12.1632 11.4084 12.2233C11.3298 12.2833 11.2401 12.3273 11.1445 12.3526C11.0489 12.3779 10.9492 12.384 10.8512 12.3706C10.7532 12.3572 10.6588 12.3245 10.5735 12.2745L9.75 11.799V12.75C9.75 12.9489 9.67098 13.1397 9.53033 13.2803C9.38968 13.421 9.19891 13.5 9 13.5C8.80109 13.5 8.61032 13.421 8.46967 13.2803C8.32902 13.1397 8.25 12.9489 8.25 12.75V11.799L7.4265 12.2745C7.34117 12.3245 7.24679 12.3572 7.14879 12.3706C7.0508 12.384 6.95111 12.3779 6.85549 12.3526C6.75987 12.3273 6.67019 12.2833 6.59162 12.2233C6.51304 12.1632 6.44713 12.0882 6.39768 12.0025C6.34822 11.9168 6.3162 11.8222 6.30345 11.7242C6.2907 11.6261 6.29748 11.5264 6.32339 11.431C6.34931 11.3355 6.39385 11.2461 6.45445 11.168C6.51505 11.0898 6.59052 11.0244 6.6765 10.9755L7.5 10.5L6.6765 10.0245C6.50565 9.92434 6.38134 9.76066 6.33072 9.56919C6.2801 9.37772 6.30727 9.174 6.40629 9.00248C6.50532 8.83096 6.66817 8.70558 6.8593 8.65369C7.05043 8.6018 7.25433 8.62761 7.4265 8.7255L8.25 9.201V8.25C8.25 8.05109 8.32902 7.86032 8.46967 7.71967C8.61032 7.57902 8.80109 7.5 9 7.5C9.19891 7.5 9.38968 7.57902 9.53033 7.71967C9.67098 7.86032 9.75 8.05109 9.75 8.25ZM6.75 15H14.25C14.4489 15 14.6397 15.079 14.7803 15.2197C14.921 15.3603 15 15.5511 15 15.75C15 15.9489 14.921 16.1397 14.7803 16.2803C14.6397 16.421 14.4489 16.5 14.25 16.5H6.75C6.55109 16.5 6.36032 16.421 6.21967 16.2803C6.07902 16.1397 6 15.9489 6 15.75C6 15.5511 6.07902 15.3603 6.21967 15.2197C6.36032 15.079 6.55109 15 6.75 15ZM6.75 18H14.25C14.4489 18 14.6397 18.079 14.7803 18.2197C14.921 18.3603 15 18.5511 15 18.75C15 18.9489 14.921 19.1397 14.7803 19.2803C14.6397 19.421 14.4489 19.5 14.25 19.5H6.75C6.55109 19.5 6.36032 19.421 6.21967 19.2803C6.07902 19.1397 6 18.9489 6 18.75C6 18.5511 6.07902 18.3603 6.21967 18.2197C6.36032 18.079 6.55109 18 6.75 18Z\"\n fill=\"white\"\n />\n </g>\n <defs>\n <clipPath id=\"clip0_952_64107\">\n <rect width=\"24\" height=\"24\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n);\nconst ErrorMessage = styled.div`\n color: red;\n`;\n\nconst ErrorContainer = styled.div`\n position: relative;\n top: 0;\n padding-top: 4px;\n padding-bottom: 4px;\n padding-left: 8px;\n display: flex;\n width: 206px;\n color: #dc3545;\n font-family: Helvetica Neue;\n font-size: 14px;\n font-style: normal;\n font-weight: 400;\n line-height: 150%;\n background-color: #25252a;\n border-radius: 2px;\n`;\nreturn (\n <>\n {canWriteMessage && (\n <Container\n style={getCustomStyle(openThread, isThread)}\n key={openThread.id}\n >\n {/* <IconUpload\n onClick={() => {\n setError(\"\");\n if (!uploadedFile.file.cid && !uploadedImage.file.cid) {\n setEmojiSelectorOpen(false);\n setShowUpload(!showUpload);\n }\n }}\n /> */}\n <Wrapper>\n <FullWidthWrapper>\n <MarkdownEditor\n setValue={setMessage}\n value={message}\n onChange={handleMessageChange}\n selectedEmoji={selectedEmoji}\n resetSelectedEmoji={() => setSelectedEmoji('')}\n handleMessageSent={handleSendMessage}\n />\n </FullWidthWrapper>\n {(!message || emptyText.test(markdownParser(message))) && (\n <Placeholder placeholderPosition={placeholderPosition}>\n {openThread && isThread\n ? `Reply in thread`\n : `Type message in ${selectedChat}`}\n </Placeholder>\n )}\n\n {uploadedFile.file.cid && (\n <>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.MessageFileField`}\n props={{\n file: uploadedFile.file,\n resetFile,\n }}\n />\n </>\n )}\n {uploadedImage.file.cid && (\n <>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.MessageImageField`}\n props={{\n file: uploadedImage.file,\n resetImage,\n }}\n />\n </>\n )}\n </Wrapper>\n <div onClick={() => setEmojiSelectorOpen(!emojiSelectorOpen)}>\n <IconEmoji />\n </div>\n <IconSend\n onClick={() => {\n if (isActive) {\n handleSendMessage();\n }\n }}\n isActive={isActive}\n />\n {emojiSelectorOpen && (\n <EmojiPopupContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.EmojiSelector.EmojiSelector`}\n props={{\n OnEmojiSelected: (emoji) => setSelectedEmoji(emoji),\n }}\n key={'message-input-emoji-component'}\n />\n </EmojiPopupContainer>\n )}\n {showUpload && !uploadedFile.file.cid && !uploadedImage.file.cid && (\n <UploadPopupContainer>\n {error && <ErrorContainer>{error}</ErrorContainer>}\n <UploadContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.UploadComponent`}\n props={{\n uploadedFile: uploadedImage,\n setUploadedFile: setUploadedImage,\n type: ['image/jpeg', 'image/png', 'image/gif'],\n icon: <ImageIconSvg />,\n text: 'Upload Image',\n setError: setError,\n }}\n key=\"images-component\"\n />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Curb.Chat.UploadComponent`}\n props={{\n uploadedFile: uploadedFile,\n setUploadedFile: setUploadedFile,\n type: ['*/*'],\n icon: <FileIconSvg />,\n text: 'Upload File',\n setError: setError,\n }}\n key=\"files-component\"\n />\n </UploadContainer>\n </UploadPopupContainer>\n )}\n </Container>\n )}\n {!canWriteMessage && (\n <Container\n style={getCustomStyle(openThread, isThread)}\n key={openThread.id}\n >\n <ReadOnlyField>\n You don't have permissions to write in this channel\n </ReadOnlyField>\n </Container>\n )}\n </>\n);\n" }, "Calimero.TaskChain.Main": { "": "const contract = props.contract || 'taskchain.ws-protocol-63';\nconst componentOwnerId = props.componentOwnerId || 'calimero.testnet';\nconst accountId = context.accountId;\n\nconst PageContainer = styled.div`\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background-color: #0e0e10;\n padding-left: 60px;\n padding-right: 60px;\n padding-top: 30px;\n padding-bottom: 30px;\n gap: 24px;\n`;\n\nconst TaskChainLogo = styled.a`\n display: flex;\n flex-direction: space-between;\n align-items: center\n width: 100%;\n height: 100%;\n background-color: #0E0E10;\n color: #FFFFFF;\n font-family: Inter;\n font-weight: 700;\n font-size: 20.92px;\n line-height: 31.39px;\n :hover {\n text-decoration: none;\n }\n`;\n\nconst HeaderContainer = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n position: relative;\n`;\n\nconst BoardContainer = styled.div`\n padding-top: 8px;\n display: flex;\n flex-direction: row;\n justify-content: start;\n width: 100%;\n height: 100%;\n`;\n\nconst Divider = styled.div`\n width: 100%;\n height: 1px;\n background-color: #282933;\n`;\n\nconst [selectedProjectId, setSelectedProjectId] = useState(undefined);\nconst [bootStrapping, setBootStrapping] = useState(true);\nconst [loggedIn, setLoggedIn] = useState(false);\nconst [functionLoader, setFunctionLoader] = useState(false);\nconst [actionStatuses, setActionStatuses] = useState([]);\n\nconst addActionStatus = useCallback(\n (newActionStatus) => {\n setActionStatuses([...actionStatuses, newActionStatus]);\n },\n [actionStatuses],\n);\n\nconst setActionStatusNotVisible = (actionStatus) => {\n setActionStatuses(\n actionStatuses.map((status) => {\n if (status.id === actionStatus.id) {\n return { ...actionStatus, seen: true };\n } else {\n return actionStatus;\n }\n }),\n );\n};\n\nconst bootStrapApp = () =>\n Near.hasValidCalimeroFak(contract).then((result) => {\n if (result) {\n setLoggedIn(true);\n setBootStrapping(false);\n } else {\n setLoggedIn(false);\n setBootStrapping(false);\n }\n });\n\nconst Logo = () => (\n <Widget src={`${componentOwnerId}/widget/Calimero.TaskChain.TaskChainLogo`} />\n);\n\nif (!accountId) {\n return <>Please login to continue.</>;\n}\n\nif (bootStrapping) {\n bootStrapApp();\n return (\n <PageContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.Common.Popups.Loading`}\n props={{\n logo: <Logo />,\n }}\n />\n </PageContainer>\n );\n}\n\nif (!loggedIn) {\n return (\n <PageContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.JoinApp`}\n props={{\n contractName: contract,\n componentOwnerId,\n }}\n />\n </PageContainer>\n );\n}\n\nreturn (\n <PageContainer>\n <HeaderContainer>\n <Logo />\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.ActionStatus.StatusContainer`}\n props={{\n componentOwnerId,\n actionStatuses,\n setActionStatusNotVisible,\n }}\n />\n </HeaderContainer>\n <Divider>.</Divider>\n <BoardContainer>\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.SideMenu.SideMenu`}\n props={{\n contract,\n componentOwnerId,\n selectedProjectId,\n setSelectedProjectId,\n functionLoader,\n setFunctionLoader,\n accountId,\n addActionStatus,\n }}\n />\n {selectedProjectId && (\n <Widget\n src={`${componentOwnerId}/widget/Calimero.TaskChain.BoardContainer.Board`}\n props={{\n componentOwnerId,\n contract,\n projectId: selectedProjectId,\n addActionStatus,\n addNewStatus,\n }}\n />\n )}\n </BoardContainer>\n </PageContainer>\n);\n" } } } } }
Empty result
No logs
Receipt:
Predecessor ID:
Gas Burned:
223 Ggas
Tokens Burned:
0 
Transferred 0.17117  to calimero-deployer.near
Empty result
No logs