diff --git a/web/modals/modal.css b/web/modals/modal.css index 6bffc6511..99d335dd4 100644 --- a/web/modals/modal.css +++ b/web/modals/modal.css @@ -1,41 +1,45 @@ div.modalContainer { display: flex; background-color: var(--modal-bg); border-radius: 8px; flex-direction: column; margin: 20px; overflow: hidden; } div.modalContainerSmall { width: 330px; } div.modalContainerLarge { width: 500px; } .modalClose { color: var(--modal-close-color); } .modalClose:hover { color: var(--modal-close-color-hover); } div.modalHeader { display: flex; justify-content: space-between; align-items: center; padding: 32px 32px 0 32px; } +div.modalHeaderCentered { + justify-content: center; +} + h2.title { font-size: 20px; font-weight: 500; line-height: 32px; color: var(--fg); display: flex; align-items: center; column-gap: 8px; } diff --git a/web/modals/modal.react.js b/web/modals/modal.react.js index 7d4b18096..c40798bb3 100644 --- a/web/modals/modal.react.js +++ b/web/modals/modal.react.js @@ -1,76 +1,83 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; import ModalOverlay from 'lib/components/modal-overlay.react'; import Button from '../components/button.react'; import SWMansionIcon, { type Icon } from '../SWMansionIcon.react'; import css from './modal.css'; export type ModalSize = 'small' | 'large' | 'fit-content'; export type ModalOverridableProps = { +name: string, +icon?: Icon, +onClose: () => void, +withCloseButton?: boolean, +size?: ModalSize, + +modalHeaderCentered?: boolean, }; type ModalProps = { ...ModalOverridableProps, +children?: React.Node, }; function Modal(props: ModalProps): React.Node { const { size = 'small', children, onClose, name, icon, withCloseButton = true, + modalHeaderCentered = false, } = props; const modalContainerClasses = classNames(css.modalContainer, { [css.modalContainerLarge]: size === 'large', [css.modalContainerSmall]: size === 'small', }); + const modalHeader = classNames({ + [css.modalHeader]: true, + [css.modalHeaderCentered]: modalHeaderCentered, + }); + const cornerCloseButton = React.useMemo(() => { if (!withCloseButton) { return null; } return ( ); }, [onClose, withCloseButton]); const headerIcon = React.useMemo(() => { if (!icon) { return null; } return ; }, [icon]); return (
-
+

{headerIcon} {name}

{cornerCloseButton}
{children}
); } export default Modal; diff --git a/web/modals/terms-and-privacy-modal.css b/web/modals/terms-and-privacy-modal.css new file mode 100644 index 000000000..582a0abea --- /dev/null +++ b/web/modals/terms-and-privacy-modal.css @@ -0,0 +1,31 @@ +.container { + color: var(--modal-fg); + padding: 20px 32px; + text-align: center; + line-height: var(--line-height-text); +} + +.button { + display: flex; + flex-direction: column; + width: 110px; + padding: 5px 0 16px; + margin: auto; + justify-content: space-around; +} + +.link { + color: var(--purple-link); +} + +.error { + margin-top: 16px; + font-size: var(--s-font-14); + color: var(--error); + font-style: italic; + text-align: center; +} + +.buttonContent { + height: 20px; +} diff --git a/web/modals/terms-and-privacy-modal.react.js b/web/modals/terms-and-privacy-modal.react.js new file mode 100644 index 000000000..fdcc2205b --- /dev/null +++ b/web/modals/terms-and-privacy-modal.react.js @@ -0,0 +1,96 @@ +// @flow + +import * as React from 'react'; + +import { + policyAcknowledgment, + policyAcknowledgmentActionTypes, +} from 'lib/actions/user-actions.js'; +import { policyTypes } from 'lib/facts/policies.js'; +import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; +import { + useDispatchActionPromise, + useServerCall, +} from 'lib/utils/action-utils.js'; +import { acknowledgePolicy } from 'lib/utils/policy-acknowledge-utlis.js'; + +import Button, { buttonThemes } from '../components/button.react.js'; +import LoadingIndicator from '../loading-indicator.react.js'; +import { useSelector } from '../redux/redux-utils.js'; +import Modal from './modal.react'; +import css from './terms-and-privacy-modal.css'; + +const loadingStatusSelector = createLoadingStatusSelector( + policyAcknowledgmentActionTypes, +); + +const disabledOnClose = () => undefined; + +function TermsAndPrivacyModal(): React.Node { + const loading = useSelector(loadingStatusSelector); + const [acknowledgmentError, setAcknowledgmentError] = React.useState(''); + const sendAcknowledgmentRequest = useServerCall(policyAcknowledgment); + const dispatchActionPromise = useDispatchActionPromise(); + + const onAccept = React.useCallback(() => { + acknowledgePolicy( + policyTypes.tosAndPrivacyPolicy, + dispatchActionPromise, + sendAcknowledgmentRequest, + setAcknowledgmentError, + ); + }, [dispatchActionPromise, sendAcknowledgmentRequest]); + + const buttonContent = React.useMemo(() => { + if (loading === 'loading') { + return ; + } + return 'I accept'; + }, [loading]); + + return ( + +
+ We recently updated our{' '} + + Terms of Service + + {' & '} + + Privacy Policy + + . In order to continue using Comm, we're asking you to read + through, acknowledge, and accept the updated policies. +
+ +
+ +
{acknowledgmentError}
+
+
+ ); +} + +export default TermsAndPrivacyModal; diff --git a/web/theme.css b/web/theme.css index 5265a1fde..eb87fac84 100644 --- a/web/theme.css +++ b/web/theme.css @@ -1,193 +1,194 @@ :root { /* Never use color values defined here directly in CSS. Add color variables to "Color Theme" below The reason we never use color values defined here directly in CSS is 1. It makes changing themes from light / dark / user generated impossible. 2. Gives the programmer context into the color being used. 3. If our color system changes it's much easier to change color values in one place. Add a color value to the theme below, and then use it in your CSS. naming convention: - bg: background. - fg: foreground. - color: text-color */ --shades-white-100: #ffffff; --shades-white-90: #f5f5f5; --shades-white-80: #ebebeb; --shades-white-70: #e0e0e0; --shades-white-60: #cccccc; --shades-black-100: #0a0a0a; --shades-black-90: #1f1f1f; --shades-black-80: #404040; --shades-black-70: #666666; --shades-black-60: #808080; --violet-dark-100: #7e57c2; --violet-dark-80: #6d49ab; --violet-dark-60: #563894; --violet-dark-40: #44297a; --violet-dark-20: #331f5c; --violet-light-100: #ae94db; --violet-light-80: #b9a4df; --violet-light-60: #d3c6ec; --violet-light-40: #e8e0f5; --violet-light-20: #f3f0fa; --success-light-10: #d5f6e3; --success-light-50: #6cdf9c; --success-primary: #00c853; --success-dark-50: #029841; --success-dark-90: #034920; --error-light-10: #feebe6; --error-light-50: #f9947b; --error-primary: #f53100; --error-dark-50: #b62602; --error-dark-90: #4f1203; --logo-bg: #111827; --bg: var(--shades-black-100); --fg: var(--shades-white-100); --color-disabled: var(--shades-black-60); --text-input-bg: var(--shades-black-80); --text-input-color: var(--shades-white-60); --text-input-placeholder: var(--shades-white-60); --border: var(--shades-black-80); --error: var(--error-primary); --success: var(--success-dark-50); /* Color Theme */ --btn-bg-filled: var(--violet-dark-100); --btn-bg-outline: var(--shades-black-90); --btn-bg-success: var(--success-dark-50); --btn-bg-danger: var(--error-primary); --btn-bg-disabled: var(--shades-black-80); --btn-disabled-color: var(--shades-black-60); --chat-bg: var(--violet-dark-80); --chat-confirmation-icon: var(--violet-dark-100); --keyserver-selection: var(--violet-dark-60); --thread-selection: var(--violet-light-80); --thread-hover-bg: var(--shades-black-80); --thread-active-bg: var(--shades-black-80); --chat-timestamp-color: var(--shades-black-60); --tool-tip-bg: var(--shades-black-80); --tool-tip-color: var(--shades-white-60); --border-color: var(--shades-black-80); --calendar-chevron: var(--shades-black-60); --calendar-day-bg: var(--shades-black-60); --calendar-day-selected-color: var(--violet-dark-80); --community-bg: var(--shades-black-90); --community-settings-selected: var(--violet-dark-60); --unread-bg: var(--error-primary); --settings-btn-bg: var(--violet-dark-100); --modal-bg: var(--shades-black-90); --modal-fg: var(--shades-white-60); --join-bg: var(--shades-black-90); --help-color: var(--shades-black-60); --breadcrumb-color: var(--shades-white-60); --breadcrumb-color-unread: var(--shades-white-60); --btn-outline-border: var(--shades-black-60); --thread-color-read: var(--shades-black-60); --thread-from-color-read: var(--shades-black-80); --thread-last-message-color-read: var(--shades-black-60); --relationship-button-green: var(--success-dark-50); --relationship-button-red: var(--error-primary); --relationship-button-text: var(--fg); --disconnected-bar-alert-bg: var(--error-dark-50); --disconnected-bar-alert-color: var(--shades-white-100); --disconnected-bar-connecting-bg: var(--shades-white-70); --disconnected-bar-connecting-color: var(--shades-black-100); --permission-color: var(--shades-white-60); --thread-top-bar-color: var(--shades-white-100); --thread-top-bar-menu-color: var(--shades-white-70); --thread-ancestor-keyserver-border: var(--shades-black-70); --thread-ancestor-color-light: var(--shades-white-70); --thread-ancestor-color-dark: var(--shades-black-100); --thread-ancestor-separator-color: var(--shades-white-60); --text-message-default-background: var(--shades-black-80); --message-action-tooltip-bg: var(--shades-black-90); --menu-bg: var(--shades-black-90); --menu-bg-light: var(--shades-black-80); --menu-separator-color: var(--shades-black-80); --menu-color: var(--shades-black-60); --menu-color-light: var(--shades-white-60); --menu-color-hover: var(--shades-white-100); --menu-color-dangerous: var(--error-primary); --menu-color-dangerous-hover: var(--error-light-50); --app-list-icon-read-only-color: var(--shades-black-60); --app-list-icon-enabled-color: var(--success-primary); --app-list-icon-disabled-color: var(--shades-white-80); --account-settings-label: var(--shades-black-60); --account-button-color: var(--violet-dark-100); --chat-thread-list-color-active: var(--shades-white-60); --chat-thread-list-menu-color: var(--shades-white-60); --chat-thread-list-menu-bg: var(--shades-black-80); --chat-thread-list-menu-active-color: var(--shades-white-60); --chat-thread-list-menu-active-bg: var(--shades-black-90); --search-clear-color: var(--shades-white-100); --search-clear-bg: var(--shades-black-70); --search-input-color: var(--shades-white-100); --search-input-placeholder: var(--shades-black-60); --search-icon-color: var(--shades-black-60); --tabs-header-active-color: var(--shades-white-100); --tabs-header-active-border: var(--violet-light-100); --tabs-header-background-color: var(--shades-black-60); --tabs-header-background-border: var(--shades-black-80); --tabs-header-background-color-hover: var(--shades-white-80); --tabs-header-background-border-hover: var(--shades-black-70); --members-modal-member-text: var(--shades-black-60); --members-modal-member-text-hover: var(--shades-white-100); --label-default-bg: var(--violet-dark-80); --label-default-color: var(--shades-white-80); --subchannels-modal-color: var(--shades-black-60); --subchannels-modal-color-hover: var(--shades-white-100); --color-selector-active-bg: var(--shades-black-80); --relationship-modal-color: var(--shades-black-60); --arrow-extension-color: var(--shades-black-60); --modal-close-color: var(--shades-black-60); --modal-close-color-hover: var(--shades-white-100); --add-members-group-header-color: var(--shades-black-60); --add-members-item-color: var(--shades-black-60); --add-members-item-color-hover: var(--shades-white-100); --add-members-item-disabled-color: var(--shades-black-80); --add-members-item-disabled-color-hover: var(--shades-black-60); --add-members-remove-pending-color: var(--error-primary); --add-members-remove-pending-color-hover: var(--error-light-50); --radio-border: var(--shades-black-70); --radio-color: var(--shades-white-60); --notification-settings-option-selected-bg: var(--shades-black-80); --notification-settings-option-title-color: var(--shades-white-90); --notification-settings-option-color: var(--shades-white-60); --notification-settings-option-invalid-color: var(--shades-black-80); --notification-settings-option-invalid-selected-color: var(--shades-black-60); --danger-zone-subheading-color: var(--shades-white-60); --danger-zone-explanation-color: var(--shades-black-60); --thread-creation-search-container-bg: var(--shades-black-90); --thread-creation-close-search-color: var(--shades-black-60); --thread-creation-search-item-bg-hover: var(--shades-black-80); --thread-creation-search-item-info-color: var(--shades-black-60); --chat-message-list-active-border: #5989d6; --sidebars-modal-color: var(--shades-black-60); --sidebars-modal-color-hover: var(--shades-white-100); --inline-sidebar-bg: var(--shades-black-70); --inline-sidebar-bg-hover: var(--shades-black-80); --inline-sidebar-color: var(--fg); --compose-subchannel-header-fg: var(--shades-black-60); --compose-subchannel-header-bg: var(--shades-black-80); --compose-subchannel-label-color: var(--shades-black-60); --compose-subchannel-mark-color: var(--violet-light-100); --enum-option-icon-color: var(--violet-dark-100); --show-password-bg-hover: var(--shades-black-70); --typeahead-overlay-light: var(--shades-black-80); --typeahead-overlay-dark: var(--shades-black-90); --typeahead-overlay-text: var(--shades-white-100); --typeahead-overlay-shadow-primary: rgba(0, 0, 0, 0.25); --typeahead-overlay-shadow-secondary: rgba(0, 0, 0, 0.4); --spoiler-text-color: #33332c; --spoiler-background-color: #33332c; + --purple-link: var(--violet-light-100); }