diff --git a/web/modals/threads/color-selector-button.react.js b/web/modals/threads/color-selector-button.react.js index 2f4f225e5..9492ba29a 100644 --- a/web/modals/threads/color-selector-button.react.js +++ b/web/modals/threads/color-selector-button.react.js @@ -1,39 +1,39 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; import css from './color-selector-button.css'; type ColorSelectorButtonProps = { +color: string, +currentColor: string, +onColorSelection: (hex: string) => void, }; function ColorSelectorButton(props: ColorSelectorButtonProps): React.Node { const { color, currentColor, onColorSelection } = props; - const active = color === currentColor; + const active = color.toLowerCase() === currentColor.toLowerCase(); const containerClassName = classNames(css.container, { [css.active]: active, }); const colorSplotchStyle = React.useMemo( () => ({ backgroundColor: `#${color}`, }), [color], ); const onColorSplotchClicked = React.useCallback(() => { onColorSelection(color); }, [onColorSelection, color]); return (
); } export default ColorSelectorButton; diff --git a/web/modals/threads/thread-settings-modal.react.js b/web/modals/threads/thread-settings-modal.react.js index cec6b844a..c155ed04d 100644 --- a/web/modals/threads/thread-settings-modal.react.js +++ b/web/modals/threads/thread-settings-modal.react.js @@ -1,599 +1,595 @@ // @flow import classNames from 'classnames'; import invariant from 'invariant'; import _pickBy from 'lodash/fp/pickBy'; import * as React from 'react'; import { deleteThreadActionTypes, deleteThread, changeThreadSettingsActionTypes, changeThreadSettings, } from 'lib/actions/thread-actions'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; import { threadInfoSelector } from 'lib/selectors/thread-selectors'; import { threadHasPermission, threadTypeDescriptions, robotextName, } from 'lib/shared/thread-utils'; import { type ThreadInfo, threadTypes, assertThreadType, type ChangeThreadSettingsPayload, type UpdateThreadRequest, type LeaveThreadPayload, threadPermissions, type ThreadChanges, } from 'lib/types/thread-types'; import type { UserInfos } from 'lib/types/user-types'; import { useDispatchActionPromise, useServerCall, type DispatchActionPromise, } from 'lib/utils/action-utils'; import { firstLine } from 'lib/utils/string-utils'; import Button from '../../components/button.react'; import { useModalContext } from '../../modals/modal-provider.react'; import { useSelector } from '../../redux/redux-utils'; import Input from '../input.react'; import Modal from '../modal.react'; -import ColorPicker from './color-picker.react'; +import ColorSelector from './color-selector.react'; import css from './thread-settings-modal.css'; const { COMMUNITY_OPEN_SUBTHREAD, COMMUNITY_SECRET_SUBTHREAD } = threadTypes; type TabType = 'general' | 'privacy' | 'delete'; type TabProps = { +name: string, +tabType: TabType, +selected: boolean, +onClick: (tabType: TabType) => void, }; class Tab extends React.PureComponent { render() { const classNamesForTab = classNames({ [css['current-tab']]: this.props.selected, [css['delete-tab']]: this.props.selected && this.props.tabType === 'delete', }); return (
  • {this.props.name}
  • ); } onClick = () => { return this.props.onClick(this.props.tabType); }; } type BaseProps = { +threadID: string, }; type Props = { ...BaseProps, +threadInfo: ThreadInfo, +changeInProgress: boolean, +viewerID: ?string, +userInfos: UserInfos, +dispatchActionPromise: DispatchActionPromise, +deleteThread: ( threadID: string, currentAccountPassword: string, ) => Promise, +changeThreadSettings: ( update: UpdateThreadRequest, ) => Promise, +onClose: () => void, }; type State = { +queuedChanges: ThreadChanges, +errorMessage: string, +accountPassword: string, +currentTabType: TabType, }; class ThreadSettingsModal extends React.PureComponent { nameInput: ?HTMLInputElement; newThreadPasswordInput: ?HTMLInputElement; accountPasswordInput: ?HTMLInputElement; constructor(props: Props) { super(props); this.state = { queuedChanges: Object.freeze({}), errorMessage: '', accountPassword: '', currentTabType: 'general', }; } componentDidMount() { invariant(this.nameInput, 'nameInput ref unset'); this.nameInput.focus(); } componentDidUpdate(prevProps: Props) { if (this.state.currentTabType !== 'delete') { return; } const permissionForDeleteTab = this.hasPermissionForTab( this.props.threadInfo, 'delete', ); const prevPermissionForDeleteTab = this.hasPermissionForTab( prevProps.threadInfo, 'delete', ); if (!permissionForDeleteTab && prevPermissionForDeleteTab) { this.setTab('general'); } } hasPermissionForTab(threadInfo: ThreadInfo, tab: TabType) { if (tab === 'general') { return threadHasPermission( threadInfo, threadPermissions.EDIT_THREAD_NAME, ); } else if (tab === 'privacy') { return threadHasPermission( threadInfo, threadPermissions.EDIT_PERMISSIONS, ); } else if (tab === 'delete') { return threadHasPermission(threadInfo, threadPermissions.DELETE_THREAD); } invariant(false, `invalid tab ${tab}`); } possiblyChangedValue(key: string) { const valueChanged = this.state.queuedChanges[key] !== null && this.state.queuedChanges[key] !== undefined; return valueChanged ? this.state.queuedChanges[key] : this.props.threadInfo[key]; } namePlaceholder() { return robotextName( this.props.threadInfo, this.props.viewerID, this.props.userInfos, ); } changeQueued() { return ( Object.keys( _pickBy( value => value !== null && value !== undefined, // the lodash/fp libdef coerces the returned object's properties to the // same type, which means it only works for object-as-maps $FlowFixMe )(this.state.queuedChanges), ).length > 0 ); } render() { const { threadInfo } = this.props; const inputDisabled = this.props.changeInProgress || !this.hasPermissionForTab(threadInfo, this.state.currentTabType); let mainContent = null; if (this.state.currentTabType === 'general') { mainContent = (
    Thread name
    Description