diff --git a/src/devhub/components/molecule/AccountAutocomplete.jsx b/src/devhub/components/molecule/AccountAutocomplete.jsx index f205550df..725fa4854 100644 --- a/src/devhub/components/molecule/AccountAutocomplete.jsx +++ b/src/devhub/components/molecule/AccountAutocomplete.jsx @@ -1,6 +1,7 @@ if (!context.accountId || !props.term) return <>; let results = []; +const filterAccounts = props.filterAccounts ?? []; // hide certain accounts from the list const profilesData = Social.get("*/profile/name", "final") || {}; const followingData = Social.get( `${context.accountId}/graph/follow/**`, @@ -43,6 +44,9 @@ for (let i = 0; i < profiles.length; i++) { results.sort((a, b) => b.score - a.score); results = results.slice(0, limit); +if (filterAccounts?.length > 0) { + results = results.filter((item) => !filterAccounts?.includes(item.accountId)); +} function onResultClick(id) { props.onSelect && props.onSelect(id); diff --git a/src/devhub/components/molecule/ProfileCard.jsx b/src/devhub/components/molecule/ProfileCard.jsx index fcbda997b..0607ad3e6 100644 --- a/src/devhub/components/molecule/ProfileCard.jsx +++ b/src/devhub/components/molecule/ProfileCard.jsx @@ -27,6 +27,7 @@ const ProfileCard = (props) => { // const hideName = props.hideName; const hideImage = props.hideImage; const iconOnly = props.iconOnly; + const openLinkInNewTab = props.openLinkInNewTab ?? false; const profile = props.profile ?? Social.getr(`${accountId}/profile`); @@ -66,6 +67,8 @@ const ProfileCard = (props) => { ? link : `/${REPL_MOB}/widget/ProfilePage?accountId=${accountId}` } + target={openLinkInNewTab ? "_blank" : ""} + rel="noopener noreferrer" className="link-dark text-truncate d-inline-flex" > {inner} diff --git a/src/devhub/entity/community/Teams.jsx b/src/devhub/entity/community/Teams.jsx index 0114f1f96..c14cb1fd8 100644 --- a/src/devhub/entity/community/Teams.jsx +++ b/src/devhub/entity/community/Teams.jsx @@ -48,7 +48,7 @@ return ( style={{ minHeight: 30 }} >
- Admins + Community Admins
diff --git a/src/devhub/entity/community/configuration/AccessControlConfigurator.jsx b/src/devhub/entity/community/configuration/AccessControlConfigurator.jsx index 1c3b8b563..61ef162d8 100644 --- a/src/devhub/entity/community/configuration/AccessControlConfigurator.jsx +++ b/src/devhub/entity/community/configuration/AccessControlConfigurator.jsx @@ -7,25 +7,168 @@ const CommunityAccessControlSchema = { }, }; -const { data, onSubmit, onCancel, setIsActive, isActive } = props; +const Struct = VM.require("${REPL_DEVHUB}/widget/core.lib.struct"); + +if (!Struct) { + return

Loading modules...

; +} + +const AutoComplete = styled.div` + z-index: 5; + + > div > div { + padding: calc(var(--padding) / 2); + } +`; + +const Wrapper = styled.div` + .container { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.5em; + } + + .admins-item { + display: inline-block; + padding: 0.6em 0.8em; + border-radius: 10px; + border: 1px solid lightgray; + position: relative; + } + + .admins-item .remove { + position: absolute; + right: 5px; + top: 0; + font-size: 18px; + color: grey; + cursor: pointer; + } + + .admins-input { + flex-grow: 1; + border: none; + outline: none; + } -function handleOnSubmit(v) { - if (v.admins) { - v.admins = v.admins.split(",").map((admin) => admin.trim()); + input[type="text"]:disabled { + all: inherit; } - onSubmit(v); + + input::placeholder { + font-size: 16px; + } +`; + +const { data, onSubmit, onCancel, setIsActive, isActive } = props; +const initialValues = Struct.typeMatch(CommunityAccessControlSchema) + ? Struct.pick(data ?? {}, Object.keys(CommunityAccessControlSchema)) + : {}; + +const [admins, setAdmins] = useState(initialValues?.admins ?? []); +const [text, setText] = useState(""); +const [showAccountAutocomplete, setShowAutoAutocomplete] = useState(false); + +function handleKeyDown(e) { + if (e.key !== "Enter") return; + const value = e.target.value; + if (!value.trim()) return; + // Add the value to the admins array + setAdmins([...admins, value]); + setText(""); +} + +const onCancelClick = () => { + setAdmins(initialValues?.admins ?? []); + setIsActive(false); +}; + +const onSubmitClick = () => { + onSubmit({ admins: admins.map((admin) => admin.trim()) }); setIsActive(false); +}; + +function autoCompleteAccountId(id) { + setAdmins([...admins, id]); + setText(""); + setShowAutoAutocomplete(false); } return ( - + +
+ {admins.map((admin, index) => ( +
+ + {/* don't allow removal if only 1 admin is added */} + {admins.length > 1 && isActive && ( + setAdmins(admins.filter((item) => item !== admin))} + > + × + + )} +
+ ))} + { + setShowAutoAutocomplete(true); + setText(v.target.value); + }} + onKeyDown={handleKeyDown} + type="text" + className="admins-input" + placeholder={isActive && "Add Admins here..."} + /> +
+ {showAccountAutocomplete && ( + + setShowAutoAutocomplete(false), + filterAccounts: admins, + }} + /> + + )} + {isActive && ( +
+ + +
+ )} +
); diff --git a/src/devhub/page/community/configuration.jsx b/src/devhub/page/community/configuration.jsx index afee6349e..28f339949 100644 --- a/src/devhub/page/community/configuration.jsx +++ b/src/devhub/page/community/configuration.jsx @@ -107,7 +107,7 @@ return ( "${REPL_DEVHUB}/widget/devhub.entity.community.configuration.ConfigurationSection" } props={{ - title: "Access Control", + title: "Community Admins", hasConfigurePermissions, Configurator: (p) => (