diff --git a/src/devhub/components/molecule/AccountAutocomplete.jsx b/src/devhub/components/molecule/AccountAutocomplete.jsx new file mode 100644 index 000000000..f205550df --- /dev/null +++ b/src/devhub/components/molecule/AccountAutocomplete.jsx @@ -0,0 +1,143 @@ +if (!context.accountId || !props.term) return <>; + +let results = []; +const profilesData = Social.get("*/profile/name", "final") || {}; +const followingData = Social.get( + `${context.accountId}/graph/follow/**`, + "final" +); +if (!profilesData) return <>; +const profiles = Object.entries(profilesData); +const term = (props.term || "").replace(/\W/g, "").toLowerCase(); +const limit = 5; + +for (let i = 0; i < profiles.length; i++) { + let score = 0; + const accountId = profiles[i][0]; + const accountIdSearch = profiles[i][0].replace(/\W/g, "").toLowerCase(); + const nameSearch = (profiles[i][1]?.profile?.name || "") + .replace(/\W/g, "") + .toLowerCase(); + const accountIdSearchIndex = accountIdSearch.indexOf(term); + const nameSearchIndex = nameSearch.indexOf(term); + + if (accountIdSearchIndex > -1 || nameSearchIndex > -1) { + score += 10; + + if (accountIdSearchIndex === 0) { + score += 10; + } + if (nameSearchIndex === 0) { + score += 10; + } + if (followingData[accountId] === "") { + score += 30; + } + + results.push({ + accountId, + score, + }); + } +} + +results.sort((a, b) => b.score - a.score); +results = results.slice(0, limit); + +function onResultClick(id) { + props.onSelect && props.onSelect(id); +} + +const Wrapper = styled.div` + position: relative; + background: #eceef0; + + &::before { + content: ""; + display: block; + position: absolute; + right: 0; + width: 6px; + height: 100%; + background: linear-gradient( + to left, + rgba(236, 238, 240, 1), + rgba(236, 238, 240, 0) + ); + z-index: 10; + } +`; + +const Scroller = styled.div` + position: relative; + display: flex; + padding: 6px; + gap: 6px; + overflow: auto; + scroll-behavior: smooth; + align-items: center; + scrollbar-width: none; + -ms-overflow-style: none; + &::-webkit-scrollbar { + display: none; + } + + > * { + max-width: 175px; + flex-grow: 0; + flex-shrink: 0; + + button { + border: 1px solid #eceef0; + background: #fff !important; + border-radius: 6px; + padding: 3px 6px; + transition: all 200ms; + + &:focus, + &:hover { + border-color: #687076; + } + } + } +`; + +const CloseButton = styled.button` + background: none; + border: none; + display: block; + padding: 12px; + color #687076; + transition: all 200ms; + + &:hover { + color: #000; + } +`; + +if (results.length === 0) return <>; + +return ( + + + + + + + {results.map((result) => { + return ( + + ); + })} + + +); diff --git a/src/devhub/components/molecule/SimpleMDE.jsx b/src/devhub/components/molecule/SimpleMDE.jsx index 0f11aa048..264b1f1e1 100644 --- a/src/devhub/components/molecule/SimpleMDE.jsx +++ b/src/devhub/components/molecule/SimpleMDE.jsx @@ -212,9 +212,7 @@ const code = ` initialText: event.data.content })); isEditorInitialized = true; } else { - console.log(event); if (event.data.handler === 'autocompleteSelected') { - console.log("we're in"); codeMirrorInstance.getDoc().setValue(event.data.content); } } diff --git a/src/devhub/entity/post/Post.jsx b/src/devhub/entity/post/Post.jsx index 274cdd6de..9ca826a7b 100644 --- a/src/devhub/entity/post/Post.jsx +++ b/src/devhub/entity/post/Post.jsx @@ -626,7 +626,7 @@ const tags = post.snapshot.labels ? (
diff --git a/src/devhub/entity/post/PostEditor.jsx b/src/devhub/entity/post/PostEditor.jsx index 489f75fda..af094a82c 100644 --- a/src/devhub/entity/post/PostEditor.jsx +++ b/src/devhub/entity/post/PostEditor.jsx @@ -379,7 +379,7 @@ const callDescriptionDiv = () => { {autocompleteEnabled && state.showAccountAutocomplete && ( word.startsWith("@")) + .map((mention) => mention.slice(1)); + const newMentiones = allMentiones.filter( + (item) => !state.mentionsArray.includes(item) + ); + State.update((lastKnownState) => ({ ...lastKnownState, text: value, - showAccountAutocomplete, + showAccountAutocomplete: newMentiones?.length > 0, + mentionsArray: allMentiones, + mentionInput: newMentiones?.[0] ?? "", })); } function autoCompleteAccountId(id) { - let description = state.description.replace(/[\s]{0,1}@[^\s]*$/, ""); - description = `${description} @${id}`.trim() + " "; + // to make sure we update the @ at correct index + let currentIndex = 0; + const updatedDescription = state.description.replace( + /(?:^|\s)(@[^\s]*)/g, + (match) => { + if (currentIndex === state.mentionsArray.indexOf(state.mentionInput)) { + currentIndex++; + return ` @${id}`; + } else { + currentIndex++; + return match; + } + } + ); + State.update((lastKnownState) => ({ ...lastKnownState, - description, + handler: "autocompleteSelected", + description: updatedDescription, showAccountAutocomplete: false, })); } @@ -51,26 +96,6 @@ const labels = labelStrings.map((s) => { return { name: s }; }); -State.init({ - seekingFunding: false, - - author_id: context.accountId, - // Should be a list of objects with field "name". - labels, - // Should be a list of labels as strings. - // Both of the label structures should be modified together. - labelStrings, - postType: "Idea", - name: props.name ?? "", - description: props.description ?? "", - amount: props.amount ?? "", - token: props.token ?? "USDT", - supervisor: props.supervisor ?? "neardevdao.near", - githubLink: props.githubLink ?? "", - warning: "", - waitForDraftStateRestore: true, -}); - if (state.waitForDraftStateRestore) { const draftstatestring = Storage.privateGet(DRAFT_STATE_STORAGE_KEY); if (draftstatestring != null) { @@ -313,9 +338,9 @@ const descriptionDiv = ( {autocompleteEnabled && state.showAccountAutocomplete && ( State.update({ showAccountAutocomplete: false }), }}