123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- import React, { useEffect, useState } from "react";
- import { Dropdown, Label, Loader, Message, Icon, DropdownItemProps, DropdownOnSearchChangeData, DropdownProps } from "semantic-ui-react";
- import { getFormErrorLabelsProps } from "./errorHandling";
- import * as Yup from "yup";
- import {
- GenericProposalForm,
- GenericFormValues,
- genericFormDefaultOptions,
- genericFormDefaultValues,
- withProposalFormData,
- ProposalFormExportProps,
- ProposalFormContainerProps,
- ProposalFormInnerProps
- } from "./GenericProposalForm";
- import Validation from "../validationSchema";
- import { FormField } from "./FormFields";
- import { withFormContainer } from "./FormContainer";
- import { useTransport } from "../runtime";
- import { usePromise } from "../utils";
- import { Profile } from "@joystream/types/members";
- import PromiseComponent from "../Proposal/PromiseComponent";
- import _ from 'lodash';
- import "./forms.css";
- type FormValues = GenericFormValues & {
- workingGroupLead: any;
- };
- const defaultValues: FormValues = {
- ...genericFormDefaultValues,
- workingGroupLead: ""
- };
- type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form.
- type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
- type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
- type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
- function memberOptionKey(id: number, profile: Profile) {
- return `${id}:${profile.root_account.toString()}`;
- }
- const MEMBERS_QUERY_MIN_LENGTH = 4;
- const MEMBERS_NONE_OPTION: DropdownItemProps = {
- key: '- NONE -',
- text: '- NONE -',
- value: 'none'
- }
- function membersToOptions(members: { id: number, profile: Profile }[]) {
- return [MEMBERS_NONE_OPTION].concat(
- members
- .map(({ id, profile }) => ({
- key: profile.handle,
- text: `${ profile.handle } (id:${ id })`,
- value: memberOptionKey(id, profile),
- image: profile.avatar_uri.toString() ? { avatar: true, src: profile.avatar_uri } : null
- }))
- );
- }
- function filterMembers(options: DropdownItemProps[], query: string) {
- if (query.length < MEMBERS_QUERY_MIN_LENGTH) {
- return [MEMBERS_NONE_OPTION];
- }
- const regexp = new RegExp(_.escapeRegExp(query));
- return options.filter((opt) => regexp.test((opt.text || '').toString()))
- }
- type MemberWithId = { id: number; profile: Profile };
- const SetContentWorkingGroupsLeadForm: React.FunctionComponent<FormInnerProps> = props => {
- const { handleChange, errors, touched, values } = props;
- const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
- // State
- const [ membersOptions, setMembersOptions ] = useState([] as DropdownItemProps[]);
- const [ filteredOptions, setFilteredOptions ] = useState([] as DropdownItemProps[]);
- const [ membersSearchQuery, setMembersSearchQuery ] = useState("");
- // Transport
- const transport = useTransport();
- const [members, /* error */, loading] = usePromise<MemberWithId[]>(
- () => transport.membersExceptCouncil(),
- []
- );
- const [currentLead, clError, clLoading] = usePromise<MemberWithId | null>(
- () => transport.WGLead(),
- null
- );
- // Generate members options array on load
- useEffect(() => {
- if (members.length) {
- setMembersOptions(membersToOptions(members));
- }
- }, [members]);
- // Filter options on search query change (we "pulled-out" this logic here to avoid lags)
- useEffect(() => {
- setFilteredOptions(filterMembers(membersOptions, membersSearchQuery));
- }, [membersSearchQuery]);
- return (
- <PromiseComponent error={clError} loading={clLoading} message="Fetching current lead...">
- <GenericProposalForm
- {...props}
- txMethod="createSetLeadProposal"
- proposalType="SetLead"
- submitParams={[
- props.myMemberId,
- values.title,
- values.rationale,
- "{STAKE}",
- values.workingGroupLead !== MEMBERS_NONE_OPTION.value ? values.workingGroupLead.split(":") : undefined
- ]}
- >
- {loading ? (
- <>
- <Loader active inline style={{ marginRight: "5px" }} /> Fetching members...
- </>
- ) : (<>
- <FormField
- error={errorLabelsProps.workingGroupLead}
- label="New Content Working Group Lead"
- help={
- 'The member you propose to set as a new Content Working Group Lead. ' +
- 'Start typing handle or use "id:[ID]" query.'
- }
- >
- {
- (!values.workingGroupLead || membersSearchQuery.length > 0) &&
- (MEMBERS_QUERY_MIN_LENGTH - membersSearchQuery.length) > 0 && (
- <Label>
- Type at least { MEMBERS_QUERY_MIN_LENGTH - membersSearchQuery.length } more characters
- </Label>
- )
- }
- <Dropdown
- clearable
- // Here we just ignore search query and return all options, since we pulled-out this logic
- // to our component to avoid lags
- search={ (options: DropdownItemProps[], query:string ) => options }
- // On search change we update it in our state
- onSearchChange={ (e: React.SyntheticEvent, data: DropdownOnSearchChangeData) => {
- setMembersSearchQuery(data.searchQuery);
- } }
- name="workingGroupLead"
- placeholder={ "Start typing member handle or \"id:[ID]\" query..." }
- fluid
- selection
- options={filteredOptions}
- onChange={
- (e: React.ChangeEvent<any>, data: DropdownProps) => {
- // Fix TypeScript issue
- const originalHandler = handleChange as (e: React.ChangeEvent<any>, data: DropdownProps) => void;
- originalHandler(e, data);
- if (!data.value) {
- setMembersSearchQuery('');
- }
- }
- }
- value={values.workingGroupLead}
- />
- {errorLabelsProps.workingGroupLead && <Label {...errorLabelsProps.workingGroupLead} prompt />}
- </FormField>
- <Message info active={1}>
- <Message.Content>
- <Icon name="info circle"/>
- Current Content Working Group lead: <b>{ (currentLead && currentLead.profile.handle) || 'NONE' }</b>
- </Message.Content>
- </Message>
- </>)}
- </GenericProposalForm>
- </PromiseComponent>
- );
- };
- const FormContainer = withFormContainer<FormContainerProps, FormValues>({
- mapPropsToValues: (props: FormContainerProps) => ({
- ...defaultValues,
- ...(props.initialData || {})
- }),
- validationSchema: Yup.object().shape({
- ...genericFormDefaultOptions.validationSchema,
- workingGroupLead: Validation.SetLead.workingGroupLead
- }),
- handleSubmit: genericFormDefaultOptions.handleSubmit,
- displayName: "SetContentWorkingGroupLeadForm"
- })(SetContentWorkingGroupsLeadForm);
- export default withProposalFormData<FormContainerProps, ExportComponentProps>(FormContainer);
|