From 637a3e0aae36fbb1681c82e61c6114623702c548 Mon Sep 17 00:00:00 2001 From: Pegah Date: Fri, 27 Oct 2023 15:47:57 -0700 Subject: [PATCH 01/18] updates to toast + toast for preferences - fixes for modals with more than one step needed --- .../DeleteProjectModal/DeleteProjectModal.tsx | 2 +- web/src/components/Modal/Modal.scss | 9 +- web/src/components/Modal/Modal.tsx | 2 + .../PreferenceSelect/PreferenceSelect.scss | 13 +- .../components/Preferences/Preferences.scss | 133 ++++++++++++++---- .../components/Preferences/Preferences.tsx | 81 +++++++---- .../RemoveSelfProjectModal.tsx | 2 +- web/src/components/Toast/Toast.scss | 31 ++++ web/src/components/Toast/Toast.tsx | 18 ++- web/src/images/arrow-down-left.svg | 6 + web/src/images/arrow-right.svg | 7 +- web/src/images/aspect-ratio.svg | 7 + web/src/images/confirmation.svg | 17 +++ web/src/images/destructive.svg | 18 +++ web/src/images/information.svg | 18 +++ web/src/images/warning.svg | 18 +++ web/src/routes/GlobalModals.tsx | 22 ++- 17 files changed, 331 insertions(+), 73 deletions(-) create mode 100644 web/src/images/arrow-down-left.svg create mode 100644 web/src/images/aspect-ratio.svg create mode 100644 web/src/images/confirmation.svg create mode 100644 web/src/images/destructive.svg create mode 100644 web/src/images/information.svg create mode 100644 web/src/images/warning.svg diff --git a/web/src/components/DeleteProjectModal/DeleteProjectModal.tsx b/web/src/components/DeleteProjectModal/DeleteProjectModal.tsx index e6ca6455..786faf17 100644 --- a/web/src/components/DeleteProjectModal/DeleteProjectModal.tsx +++ b/web/src/components/DeleteProjectModal/DeleteProjectModal.tsx @@ -52,7 +52,7 @@ const DeleteProjectModal: React.FC = ({ - Are you sure you want to delete the project '{projectName}'? This + Are you sure you want to delete the project '{projectName}'? This action will delete all the project data and can’t be undone. diff --git a/web/src/components/Modal/Modal.scss b/web/src/components/Modal/Modal.scss index f9f5a3f2..f92d5829 100644 --- a/web/src/components/Modal/Modal.scss +++ b/web/src/components/Modal/Modal.scss @@ -54,7 +54,7 @@ position: absolute; width: 32.5rem; box-sizing: border-box; - padding: 4rem; + background-color: #ffff; border-radius: 1rem; box-shadow: 0px 0px 1rem var(--shadow-color); @@ -62,8 +62,13 @@ flex-direction: column; justify-content: space-between; transition: 0.2s all; - overflow-y: scroll; max-height: calc(100vh - 2rem); + overflow-y: hidden; + + .modal-scrollable-content { + padding: 4rem 4rem 3rem 4rem ; + overflow-y: auto; // change to auto so the scrollbar only appears when necessary + } .modal-button-close { position: absolute; diff --git a/web/src/components/Modal/Modal.tsx b/web/src/components/Modal/Modal.tsx index 5b34b24c..f7684591 100644 --- a/web/src/components/Modal/Modal.tsx +++ b/web/src/components/Modal/Modal.tsx @@ -115,7 +115,9 @@ const Modal: React.FC = ({ )} +
{children} +
{/* */} diff --git a/web/src/components/PreferenceSelect/PreferenceSelect.scss b/web/src/components/PreferenceSelect/PreferenceSelect.scss index 11479175..3f5bd080 100644 --- a/web/src/components/PreferenceSelect/PreferenceSelect.scss +++ b/web/src/components/PreferenceSelect/PreferenceSelect.scss @@ -1,7 +1,7 @@ /* PreferenceSelect */ .preference-select { - padding: 20px 0; + padding: 1.5rem 0; /* border-bottom: 1px solid #e3e3e3; */ } @@ -39,7 +39,7 @@ .preference-select-options-wrapper { display: flex; flex-direction: row; - padding: 0 30px; + // padding: 0 30px; margin-bottom: 1rem; } @@ -51,12 +51,15 @@ text-align: center; border: 1px solid #e3e3e3; border-radius: 10px; - width: 150px; + width: 180px; height: 120px; - margin: 0 auto; display: block; cursor: pointer; transition: border 0.2s ease-in-out; + + &:first-child { + margin-right: 1rem; + } } .preference-select-option:hover, @@ -66,7 +69,7 @@ } .preference-select-option-content { - width: 150px; + width: 180px; height: 120px; display: table-cell; vertical-align: middle; diff --git a/web/src/components/Preferences/Preferences.scss b/web/src/components/Preferences/Preferences.scss index c7d60786..c5aa64cd 100644 --- a/web/src/components/Preferences/Preferences.scss +++ b/web/src/components/Preferences/Preferences.scss @@ -1,22 +1,106 @@ -.preferences-title { - font-size: 24px; - line-height: 32px; - color: #4d4d4d; - font-family: 'gilroyextrabold', 'gilroyextrabold', Helvetica, sans-serif; - text-align: center; - margin-bottom: 20px; +.preferences-modal { } -.navigation-mode-option-icon-trackpad .icon { - height: 49px; - width: 47px; - margin-bottom: -8px; +// Preferences Modal animation +.preferences-modal-enter { + opacity: 0; } -.navigation-mode-option-icon-mouse .icon { - height: 45px; - width: 40px; - margin-bottom: -5px; +.preferences-modal-enter-active { + opacity: 1; + transition: 0.2s all; +} + +.preferences-modal-exit { + opacity: 1; +} + +.preferences-modal-exit-active { + opacity: 0; + transition: 0.2s all; +} + +.preferences { + background-color: var(--bg-color-secondary); + width: calc(100vw); + height: calc(100vh); + overflow-y: hidden; + box-sizing: border-box; + position: absolute; + // left: 3rem; + // top: 4rem; + z-index: 2; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + // border-radius: 1rem; + + .preferences-close { + position: absolute; + right: 2.5rem; + top: 2.5rem; + } + + .preferences-save-button { + border-top: 1.5px solid var(--border-color-timberwolf); + width: 100%; + text-align: center; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + padding: 1rem 0 2rem 0; + } + + .preferences-scrollable { + overflow-y: scroll; + height: 100%; + width: 100%; + // max-width: 30rem; + // padding: 4rem 0; + // box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + + .preferences-title { + font-size: 24px; + line-height: 1.5; + font-family: var(--font-family-primary-bold); + text-align: center; + border-bottom: 1.5px solid var(--border-color-timberwolf); + width: 100%; + padding: 2.75rem 0 2rem; + } + + .preferences-content { + height: 100%; + max-width: 30rem; + display: flex; + flex-direction: column; + align-items: center; + box-sizing: border-box; + padding: 0 3rem; + } +} + +.navigation-mode-option-icon-trackpad { + .icon { + height: 49px; + width: 47px; + margin-bottom: -8px; + } +} + +.navigation-mode-option-icon-mouse { + .icon { + height: 45px; + width: 40px; + margin-bottom: -5px; + } } .navigation-mode-description-title-wrapper { @@ -24,12 +108,12 @@ flex-direction: row; align-items: center; margin-bottom: 10px; -} -.navigation-mode-description-title-wrapper .icon { - height: 12px; - cursor: unset; - padding: 0; + .icon { + height: 12px; + cursor: unset; + padding: 0; + } } .navigation-mode-description-title { @@ -46,12 +130,3 @@ color: #3b3b3b; margin-left: 26px; } - -.preferences-save-button { - text-align: center; - margin-top: 40px; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; -} diff --git a/web/src/components/Preferences/Preferences.tsx b/web/src/components/Preferences/Preferences.tsx index 097fd9fe..294b5a12 100644 --- a/web/src/components/Preferences/Preferences.tsx +++ b/web/src/components/Preferences/Preferences.tsx @@ -1,9 +1,8 @@ -import React, { useState } from 'react' +import React, { useContext, useState } from 'react' import './Preferences.scss' import Icon from '../Icon/Icon' import Button from '../Button/Button' -import Modal from '../Modal/Modal' import { COORDINATES, KeyboardNavigationPreference, @@ -17,6 +16,9 @@ import PreferenceSelect, { PreferenceSelectOption, } from '../PreferenceSelect/PreferenceSelect' import { ModalState, OpenModal } from '../../context/ModalContexts' +import ButtonClose from '../ButtonClose/ButtonClose' +import { CSSTransition } from 'react-transition-group' +import ToastContext, { ShowToast } from '../../context/ToastContext' function Descriptions({ navigation }) { return ( @@ -101,19 +103,19 @@ function KeyboardNavigationModeInternal({ }) { const options = ( <> - setKeyboardNavigationSelected(MODAL)} - iconName="trackpad.svg" - iconExtraClassName="navigation-mode-option-icon-trackpad" - title="Use a Modal" - /> setKeyboardNavigationSelected(COORDINATES)} - iconName="mouse.svg" + iconName="arrow-down-left.svg" iconExtraClassName="navigation-mode-option-icon-mouse" - title="Use Coordinates" + title="The most left" + /> + setKeyboardNavigationSelected(MODAL)} + iconName="aspect-ratio.svg" + iconExtraClassName="navigation-mode-option-icon-trackpad" + title="Show a modal" /> ) @@ -150,15 +152,25 @@ export default function Preferences({ setModalState, }: PreferencesProps) { // hold an internal version of the preferences state, so that we can toggle it, before saving it - const [navigationSelected, setNavigationSelected] = useState(navigationPreference) + const [navigationSelected, setNavigationSelected] = useState( + navigationPreference + ) const [keyboardNavigationSelected, setKeyboardNavigationSelected] = useState( keyboardNavigationPreference ) + // pull in the toast context + const { setToastState } = useContext(ToastContext) + const save = () => { setNavigationPreference(navigationSelected) setKeyboardNavigationPreference(keyboardNavigationSelected) setModalState({ id: OpenModal.None }) + setToastState({ + id: ShowToast.Yes, + text: 'Preferences saved', + type: 'confirmation', + }) } const close = () => { // reset navigation selected to @@ -170,24 +182,35 @@ export default function Preferences({ } return ( - -
Preferences
- - - -
-
-
+ ) } + + diff --git a/web/src/components/RemoveSelfProjectModal/RemoveSelfProjectModal.tsx b/web/src/components/RemoveSelfProjectModal/RemoveSelfProjectModal.tsx index 6dd6615e..863f6da8 100644 --- a/web/src/components/RemoveSelfProjectModal/RemoveSelfProjectModal.tsx +++ b/web/src/components/RemoveSelfProjectModal/RemoveSelfProjectModal.tsx @@ -52,7 +52,7 @@ const RemoveSelfProjectModal: React.FC = ({ Are you sure you want to remove yourself from the shared project ' - {projectName}'? If you remove yourself from this project you won’t + {projectName}'? If you remove yourself from this project you won’t have access to it anymore.

You can join this project again later, as long as it still has diff --git a/web/src/components/Toast/Toast.scss b/web/src/components/Toast/Toast.scss index 7062c129..cea8ee45 100644 --- a/web/src/components/Toast/Toast.scss +++ b/web/src/components/Toast/Toast.scss @@ -13,6 +13,21 @@ font-family: var(--font-family-primary-medium); transition: 0.5s bottom ease; + .toast-icon { + cursor: default; + } + + .toast-text { + margin-right: 0.25rem; + margin-left: 0.25rem; + margin-bottom: 0.05rem; + cursor: default; + } + + .toast-close { + margin-top: 0.125rem; + } + &.visible { bottom: 1.25rem; } @@ -20,20 +35,36 @@ &.confirmation { color: var(--text-color-toast-confirmation); background-color: var(--bg-color-toast-confirmation); + + .icon.toast-icon .inner-icon { + background-color: var(--text-color-toast-confirmation); + } } &.information { color: var(--text-color-toast-information); background-color: var(--bg-color-toast-information); + + .icon.toast-icon .inner-icon { + background-color: var(--text-color-toast-information); + } } &.warning { color: var(--text-color-toast-warning); background-color: var(--bg-color-toast-warning); + + .icon.toast-icon .inner-icon { + background-color: var(--text-color-toast-warning); + } } &.destructive { color: var(--text-color-toast-destructive); background-color: var(--bg-color-toast-destructive); + + .icon.toast-icon .inner-icon { + background-color: var(--text-color-toast-destructive); + } } } diff --git a/web/src/components/Toast/Toast.tsx b/web/src/components/Toast/Toast.tsx index 8ecc7087..e41c46d2 100644 --- a/web/src/components/Toast/Toast.tsx +++ b/web/src/components/Toast/Toast.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from 'react' import './Toast.scss' import ButtonClose from '../ButtonClose/ButtonClose' import { ShowToast, ToastState } from '../../context/ToastContext' +import Icon from '../Icon/Icon' export type ToastProps = { toastState: ToastState @@ -13,26 +14,35 @@ const Toast: React.FC = ({ toastState, setToastState }) => { // create a local cached state of whatever the most // recent text and type given is, so that we can make a nice // disappearing transition - const { recentText, recentType } = useToastStateCache(toastState, setToastState) + const { recentText, recentType } = useToastStateCache( + toastState, + setToastState + ) return (

- {recentText} + +
{recentText}
+
setToastState({ id: ShowToast.No })} /> +
) } export default Toast -const TOAST_TIMEOUT_DELAY = 3000 +const TOAST_TIMEOUT_DELAY = 4000 // 2 purposes: // 1. cache the most recent text and type of toast // 2. set a timeout-driven delay to clear the toast -function useToastStateCache(toastState: ToastState, setToastState: React.Dispatch>) { +function useToastStateCache( + toastState: ToastState, + setToastState: React.Dispatch> +) { const [recentText, setRecentText] = useState('') const [recentType, setRecentType] = useState('') const intervalRef = useRef(undefined) diff --git a/web/src/images/arrow-down-left.svg b/web/src/images/arrow-down-left.svg new file mode 100644 index 00000000..93ee4c0b --- /dev/null +++ b/web/src/images/arrow-down-left.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/web/src/images/arrow-right.svg b/web/src/images/arrow-right.svg index 401820e5..4abcc6a1 100644 --- a/web/src/images/arrow-right.svg +++ b/web/src/images/arrow-right.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/web/src/images/aspect-ratio.svg b/web/src/images/aspect-ratio.svg new file mode 100644 index 00000000..91f85930 --- /dev/null +++ b/web/src/images/aspect-ratio.svg @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/web/src/images/confirmation.svg b/web/src/images/confirmation.svg new file mode 100644 index 00000000..be5f1e0f --- /dev/null +++ b/web/src/images/confirmation.svg @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/web/src/images/destructive.svg b/web/src/images/destructive.svg new file mode 100644 index 00000000..bbec8b1c --- /dev/null +++ b/web/src/images/destructive.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/web/src/images/information.svg b/web/src/images/information.svg new file mode 100644 index 00000000..08f3f465 --- /dev/null +++ b/web/src/images/information.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/web/src/images/warning.svg b/web/src/images/warning.svg new file mode 100644 index 00000000..a07e353a --- /dev/null +++ b/web/src/images/warning.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/web/src/routes/GlobalModals.tsx b/web/src/routes/GlobalModals.tsx index 5523d590..8e6448a4 100644 --- a/web/src/routes/GlobalModals.tsx +++ b/web/src/routes/GlobalModals.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect } from 'react' import ReactMarkdown from 'react-markdown' import Modal, { ModalContent } from '../components/Modal/Modal' @@ -74,6 +74,23 @@ const GlobalModals: React.FC = ({ history.push('/dashboard') } + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + e.stopPropagation() + setModalToNone() + } + } + + if (modalState.id !== OpenModal.None) { + window.addEventListener('keydown', handleKeyDown) + } + + return () => { + window.removeEventListener('keydown', handleKeyDown) + } + }, [modalState]) + return ( <> {/* This will only show when 'active' prop is true */} @@ -240,3 +257,6 @@ const GlobalModals: React.FC = ({ } export default GlobalModals +function handleKeyDown(this: Window, ev: KeyboardEvent) { + throw new Error('Function not implemented.') +} From 86ee74f12f5c4f70c45d32b8c96114c54979d93a Mon Sep 17 00:00:00 2001 From: Pegah Date: Fri, 27 Oct 2023 17:27:47 -0700 Subject: [PATCH 02/18] some styling fixes and updates to modals --- .../CreateProjectModal.scss | 14 ++++--- .../JoinProjectModal/JoinProjectModal.scss | 30 +++++++++++++-- .../JoinProjectModal/JoinProjectModal.tsx | 23 ++++++++++-- .../PreferenceSelect/PreferenceSelect.tsx | 9 ++++- .../components/Preferences/Preferences.scss | 37 +++++++++---------- .../components/Preferences/Preferences.tsx | 30 +++++++-------- .../ProfileEditForm/ProfileEditForm.scss | 7 ++-- .../ProjectSettingsModal.component.tsx | 15 +++++++- web/src/components/Typography/Typography.scss | 19 +++++++--- web/src/components/Typography/Typography.tsx | 1 + .../ValidatingFormInput.js | 4 +- .../ValidatingFormInput.scss | 2 +- web/src/images/validation-check.svg | 10 +++++ web/src/images/validation-x.svg | 11 ++++++ .../CreateProfilePage/CreateProfilePage.scss | 2 +- web/src/variables.scss | 3 ++ 16 files changed, 153 insertions(+), 64 deletions(-) create mode 100644 web/src/images/validation-check.svg create mode 100644 web/src/images/validation-x.svg diff --git a/web/src/components/CreateProjectModal/CreateProjectModal.scss b/web/src/components/CreateProjectModal/CreateProjectModal.scss index 732cb194..fa78e867 100644 --- a/web/src/components/CreateProjectModal/CreateProjectModal.scss +++ b/web/src/components/CreateProjectModal/CreateProjectModal.scss @@ -1,6 +1,5 @@ .create-project-modal-wrapper { - /* width: 550px; */ - height: 523px; + height: 500px; overflow: hidden; position: relative; justify-content: space-between; @@ -44,13 +43,16 @@ opacity: 1; transition: opacity 0.3s, transform 0.4s; width: 420px; + left: 50px; } .create-project-form.project-created { - /* background-color: blue; */ + // background-color: blue; opacity: 0; -webkit-transform: translateX(-300px); transform: translateX(-300px); + width: 420px; + left: 52px; } /* project created modal animation */ @@ -58,7 +60,7 @@ /* default is to be hidden */ .project-created-modal { - /* position: absolute; */ + position: absolute; opacity: 0; -webkit-transform: scale(0.8); transform: scale(0.8); @@ -74,6 +76,6 @@ } .project-created-modal.project-created .project-modal-content-spacer { - margin-bottom: 7rem; - margin-top: 3rem; + margin-bottom: 5.5rem; + margin-top: 4rem; } diff --git a/web/src/components/JoinProjectModal/JoinProjectModal.scss b/web/src/components/JoinProjectModal/JoinProjectModal.scss index f3c36190..c04e1f2b 100644 --- a/web/src/components/JoinProjectModal/JoinProjectModal.scss +++ b/web/src/components/JoinProjectModal/JoinProjectModal.scss @@ -17,6 +17,7 @@ opacity: 1; transition: opacity 0.3s, transform 0.4s; width: 420px; + top: 70px; } .join-project-form.project-join-check-is-done { @@ -27,6 +28,7 @@ display: flex; flex-direction: column; justify-content: space-between; + height: 420px; } /* project join follow up modal animation */ @@ -50,10 +52,32 @@ display: flex; flex-direction: column; justify-content: space-between; - height: 420px; } .project-join-follow-up-content-wrapper { - margin-bottom: 40px; - line-height: 1.45; + margin-bottom: 0.5rem; + margin-top: 0.5rem; +} + +.text-important-block { + background-color: var(--bg-color-text-important-information); + color: var(--text-color-toast-information); + padding: 1.25rem; + border-radius: 1rem; + display: flex; + flex-direction: row; + box-sizing: border-box; + align-items: flex-start; + + .icon { + padding-top: 0; + margin-right: 0.5rem; + width: 4rem; + box-sizing: border-box; + .inner-icon { + background-color: var(--text-color-toast-information); + } + } + + } diff --git a/web/src/components/JoinProjectModal/JoinProjectModal.tsx b/web/src/components/JoinProjectModal/JoinProjectModal.tsx index bf387ec4..df9154c2 100644 --- a/web/src/components/JoinProjectModal/JoinProjectModal.tsx +++ b/web/src/components/JoinProjectModal/JoinProjectModal.tsx @@ -12,6 +12,8 @@ import { } from '../ProjectModal/ProjectModal' import ButtonWithPendingState from '../ButtonWithPendingState/ButtonWithPendingState' import { CellIdString } from '../../types/shared' +import Icon from '../Icon/Icon' +import Typography from '../Typography/Typography' function JoinProjectForm({ checkDone, @@ -70,13 +72,26 @@ function ProjectJoinFollowUp({ onDone, checkDone }) { >
- + {/* */}
- If a peer is found, you are likely to be able to immediately begin to - access the project, although a short sync period in the queue may be - required before you can access it. + +
+ + In order to join this project, you and a peer must simultaneously + open the app. +
+
+ If a peer is found, you are likely to be able to immediately begin + to access the project, although a short sync period in the queue + may be required before you can access it. +
+
diff --git a/web/src/components/PreferenceSelect/PreferenceSelect.tsx b/web/src/components/PreferenceSelect/PreferenceSelect.tsx index ad4c4e3d..b0078987 100644 --- a/web/src/components/PreferenceSelect/PreferenceSelect.tsx +++ b/web/src/components/PreferenceSelect/PreferenceSelect.tsx @@ -1,6 +1,7 @@ import React from 'react' import './PreferenceSelect.scss' import Icon from '../Icon/Icon' +import Typography from '../Typography/Typography' function PreferenceSelectExtra({ children, @@ -61,9 +62,13 @@ function PreferenceSelect({ // @ts-ignore )} */} -
{title}
+
+ {title} +
+ +
+ {subtitle}
-
{subtitle}
{options}
{descriptions} diff --git a/web/src/components/Preferences/Preferences.scss b/web/src/components/Preferences/Preferences.scss index c5aa64cd..1fa78df6 100644 --- a/web/src/components/Preferences/Preferences.scss +++ b/web/src/components/Preferences/Preferences.scss @@ -27,13 +27,11 @@ overflow-y: hidden; box-sizing: border-box; position: absolute; - // left: 3rem; - // top: 4rem; - z-index: 2; display: flex; flex-direction: column; justify-content: center; align-items: center; + z-index: 4; // border-radius: 1rem; .preferences-close { @@ -53,17 +51,26 @@ padding: 1rem 0 2rem 0; } - .preferences-scrollable { - overflow-y: scroll; - height: 100%; - width: 100%; - // max-width: 30rem; - // padding: 4rem 0; - // box-sizing: border-box; + .preferences-inner-wrapper { + overflow-y: hidden; display: flex; flex-direction: column; justify-content: center; align-items: center; + + .preferences-scrollable { + overflow-y: auto; + flex-grow: 1; + } + + .preferences-content { + max-width: 30rem; + display: flex; + flex-direction: column; + align-items: center; + box-sizing: border-box; + padding: 1rem 3rem; + } } .preferences-title { @@ -75,16 +82,6 @@ width: 100%; padding: 2.75rem 0 2rem; } - - .preferences-content { - height: 100%; - max-width: 30rem; - display: flex; - flex-direction: column; - align-items: center; - box-sizing: border-box; - padding: 0 3rem; - } } .navigation-mode-option-icon-trackpad { diff --git a/web/src/components/Preferences/Preferences.tsx b/web/src/components/Preferences/Preferences.tsx index 294b5a12..666d9d1d 100644 --- a/web/src/components/Preferences/Preferences.tsx +++ b/web/src/components/Preferences/Preferences.tsx @@ -159,8 +159,8 @@ export default function Preferences({ keyboardNavigationPreference ) - // pull in the toast context - const { setToastState } = useContext(ToastContext) + // pull in the toast context + const { setToastState } = useContext(ToastContext) const save = () => { setNavigationPreference(navigationSelected) @@ -190,19 +190,21 @@ export default function Preferences({ >
- +
Preferences
-
-
- - +
+
+
+ + +
@@ -212,5 +214,3 @@ export default function Preferences({ ) } - - diff --git a/web/src/components/ProfileEditForm/ProfileEditForm.scss b/web/src/components/ProfileEditForm/ProfileEditForm.scss index 92296838..a8f9f186 100644 --- a/web/src/components/ProfileEditForm/ProfileEditForm.scss +++ b/web/src/components/ProfileEditForm/ProfileEditForm.scss @@ -1,8 +1,9 @@ .profile_edit_form { text-align: center; - margin: 0 auto; - overflow-y: scroll; - height: 100%; + overflow-y: auto; + // margin: 0 auto; + // overflow-y: scroll; + // height: 100%; .small-close { position: absolute; diff --git a/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx b/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx index b6f351f9..7f00982d 100644 --- a/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx +++ b/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useContext } from 'react' import ValidatingFormInput from '../ValidatingFormInput/ValidatingFormInput' import Modal from '../Modal/Modal' import { @@ -15,6 +15,7 @@ import { ModalState, OpenModal } from '../../context/ModalContexts' import { passphraseToUid } from '../../secrets' import { PROJECT_APP_PREFIX } from '../../holochainConfig' import { getAllApps } from '../../projectAppIds' +import ToastContext, { ShowToast } from '../../context/ToastContext' function ProjectDeleteButton({ onClick, @@ -94,7 +95,8 @@ function EditProjectForm({ // } }, [projectCoverUrl]) - // const subheading = `Any changes will apply for all team members.` + // pull in the toast context + const { setToastState } = useContext(ToastContext) // validate before firing event const submit = () => { @@ -102,6 +104,11 @@ function EditProjectForm({ setShouldInvalidateProjectName(true) if (projectName.length > 0 && !updatingProject) { onSubmit() + setToastState({ + id: ShowToast.Yes, + text: 'Project settings saved', + type: 'confirmation', + }) } } @@ -265,3 +272,7 @@ export default function ProjectSettingsModal({ ) } +function setToastState(arg0: { id: any; text: string; type: string }) { + throw new Error('Function not implemented.') +} + diff --git a/web/src/components/Typography/Typography.scss b/web/src/components/Typography/Typography.scss index b68b1f5e..f1b756f2 100644 --- a/web/src/components/Typography/Typography.scss +++ b/web/src/components/Typography/Typography.scss @@ -3,20 +3,20 @@ color: var(--text-color-primary); // Headings &.h1 { - font: 2rem var(--font-family-secondary-extrabold); + font: 2rem var(--font-family-primary-bold); line-height: 1.45; } &.h2 { - font: 1.75rem var(--font-family-secondary-extrabold); + font: 1.75rem var(--font-family-primary-bold); line-height: 1.45; } &.h3 { - font: 1.5rem var(--font-family-secondary-extrabold); + font: 1.5rem var(--font-family-primary-bold); line-height: 1.35; letter-spacing: 0.01rem; } &.h4 { - font: 1.375rem var(--font-family-secondary-extrabold); + font: 1.375rem var(--font-family-primary-bold); line-height: 1.45; } &.h5 { @@ -59,7 +59,7 @@ } } &.subtitle-modal { - font: 1rem var(--font-family-primary-bold); + font: 1rem var(--font-family-primary-semibold); color: var(--text-color-tertiary); line-height: 1.45; } @@ -143,4 +143,13 @@ color: var(--text-color-primary); line-height: 1.6; } + + &.label-help { + font-family: var(--font-family-primary-medium); + color: var(--text-color-tertiary); + font-size: max(0.875rem, 11.5px); + line-height: 1.6; + // margin: 0.125rem 0 0.375rem 0; + // flex: 1; + } } diff --git a/web/src/components/Typography/Typography.tsx b/web/src/components/Typography/Typography.tsx index ff19004b..90adcd88 100644 --- a/web/src/components/Typography/Typography.tsx +++ b/web/src/components/Typography/Typography.tsx @@ -26,6 +26,7 @@ export type TypographyProps = { | 'breadcrumbs-bold' | 'button-text' | 'label' + | 'label-help' | 'heading-modal' | 'subtitle-modal' | 'body-modal' diff --git a/web/src/components/ValidatingFormInput/ValidatingFormInput.js b/web/src/components/ValidatingFormInput/ValidatingFormInput.js index 301f1508..11dacf01 100644 --- a/web/src/components/ValidatingFormInput/ValidatingFormInput.js +++ b/web/src/components/ValidatingFormInput/ValidatingFormInput.js @@ -3,8 +3,8 @@ import PropTypes from 'prop-types' import './ValidatingFormInput.scss' -import ValidationCheck from '../../images/circle-check.svg' -import ValidationX from '../../images/circle-x.svg' +import ValidationCheck from '../../images/validation-check.svg' +import ValidationX from '../../images/validation-x.svg' import Typography from '../Typography/Typography' function ValidatingFormInput({ diff --git a/web/src/components/ValidatingFormInput/ValidatingFormInput.scss b/web/src/components/ValidatingFormInput/ValidatingFormInput.scss index ef9c1871..f063742a 100644 --- a/web/src/components/ValidatingFormInput/ValidatingFormInput.scss +++ b/web/src/components/ValidatingFormInput/ValidatingFormInput.scss @@ -13,7 +13,7 @@ .help-text { color: var(--text-color-tertiary); - font-size: 0.875rem; + font-size: max(0.825rem, 11.5px); line-height: 1.45; margin: 0.125rem 0 0.375rem 0; flex: 1; diff --git a/web/src/images/validation-check.svg b/web/src/images/validation-check.svg new file mode 100644 index 00000000..f6b7dfc3 --- /dev/null +++ b/web/src/images/validation-check.svg @@ -0,0 +1,10 @@ + + + + + + + diff --git a/web/src/images/validation-x.svg b/web/src/images/validation-x.svg new file mode 100644 index 00000000..52843cd7 --- /dev/null +++ b/web/src/images/validation-x.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/web/src/routes/CreateProfilePage/CreateProfilePage.scss b/web/src/routes/CreateProfilePage/CreateProfilePage.scss index b6b52999..c6c532d3 100644 --- a/web/src/routes/CreateProfilePage/CreateProfilePage.scss +++ b/web/src/routes/CreateProfilePage/CreateProfilePage.scss @@ -22,7 +22,7 @@ width: 100%; min-height: 100%; max-width: 38rem; - overflow-y: scroll; + overflow-y: auto; display: flex; flex-direction: column; align-items: center; diff --git a/web/src/variables.scss b/web/src/variables.scss index 3cfa2630..31cd500f 100644 --- a/web/src/variables.scss +++ b/web/src/variables.scss @@ -43,6 +43,9 @@ --bg-color-toast-information: #dbe5e2; --bg-color-toast-warning: #f1e5d8; --bg-color-toast-destructive: #eedad4; + // background color for important text + --bg-color-text-important-information: #e6f5f0; + --bg-color-text-important-warning: #f6ece2d8; // background dark --bg-color-primary-dark: #555555; From 3430eaa9ba1244003b7ef80f2f9f6bea3dd25d90 Mon Sep 17 00:00:00 2001 From: Connor Turland <1409121+Connoropolous@users.noreply.github.com> Date: Sun, 29 Oct 2023 18:27:50 -0700 Subject: [PATCH 03/18] fix scrolling on sign up page --- web/src/routes/CreateProfilePage/CreateProfilePage.scss | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/src/routes/CreateProfilePage/CreateProfilePage.scss b/web/src/routes/CreateProfilePage/CreateProfilePage.scss index c6c532d3..5d088eb1 100644 --- a/web/src/routes/CreateProfilePage/CreateProfilePage.scss +++ b/web/src/routes/CreateProfilePage/CreateProfilePage.scss @@ -12,7 +12,7 @@ min-width: 556px; box-sizing: border-box; padding-top: 64px; - overflow-y: hidden; + overflow-y: auto; display: flex; align-items: center; justify-content: center; @@ -20,9 +20,7 @@ .profile_edit_form { margin: 3rem 5rem; width: 100%; - min-height: 100%; max-width: 38rem; - overflow-y: auto; display: flex; flex-direction: column; align-items: center; From f9658cc5b5502d205d01edde47eb0af439ba8957 Mon Sep 17 00:00:00 2001 From: Connor Turland <1409121+Connoropolous@users.noreply.github.com> Date: Sun, 29 Oct 2023 18:47:34 -0700 Subject: [PATCH 04/18] generate passphrase for imported projects randomly --- ...ProjectModal.js => CreateProjectModal.tsx} | 17 +------------ .../ImportProjectModal/ImportProjectModal.tsx | 24 ++++++++++--------- .../routes/Dashboard/Dashboard.component.tsx | 1 - web/src/secrets.ts | 9 ++++++- web/tsconfig.json | 2 +- 5 files changed, 23 insertions(+), 30 deletions(-) rename web/src/components/CreateProjectModal/{CreateProjectModal.js => CreateProjectModal.tsx} (91%) diff --git a/web/src/components/CreateProjectModal/CreateProjectModal.js b/web/src/components/CreateProjectModal/CreateProjectModal.tsx similarity index 91% rename from web/src/components/CreateProjectModal/CreateProjectModal.js rename to web/src/components/CreateProjectModal/CreateProjectModal.tsx index 954ea1d8..6e73be9d 100644 --- a/web/src/components/CreateProjectModal/CreateProjectModal.js +++ b/web/src/components/CreateProjectModal/CreateProjectModal.tsx @@ -12,13 +12,7 @@ import { } from '../ProjectModal/ProjectModal' import ProjectSecret from '../ProjectSecret/ProjectSecret' import ButtonWithPendingState from '../ButtonWithPendingState/ButtonWithPendingState' - -// since this is a big wordset, dynamically import it -// instead of including in the main bundle -async function generatePassphrase() { - const { default: randomWord } = await import('diceware-word') - return `${randomWord()} ${randomWord()} ${randomWord()} ${randomWord()} ${randomWord()}` -} +import { generatePassphrase } from '../../secrets' function CreateProjectForm({ creatingProject, @@ -56,15 +50,6 @@ function CreateProjectForm({ validateProjectName() }, [projectName, shouldInvalidateProjectName]) - useEffect(() => { - // if (projectCoverUrl.length > 0) { - // setisValidProjectCoverUrl(true) - // } else { - // setisValidProjectCoverUrl(false) - // setErrorProjectCoverUrl('Project name is not... ?') - // } - }, [projectCoverUrl]) - const subheading = 'You can share the project with people or just keep it to yourself' diff --git a/web/src/components/ImportProjectModal/ImportProjectModal.tsx b/web/src/components/ImportProjectModal/ImportProjectModal.tsx index bdc09a89..7c3e550e 100644 --- a/web/src/components/ImportProjectModal/ImportProjectModal.tsx +++ b/web/src/components/ImportProjectModal/ImportProjectModal.tsx @@ -16,6 +16,7 @@ import { CellIdString } from '../../types/shared' import { installProject } from '../../projects/installProject' import { CellId } from '@holochain/client' import { BackwardsCompatibleProjectExportSchema } from 'zod-models' +import { generatePassphrase } from '../../secrets' function ImportProjectFilePicker({ showModal, onFilePicked, onCancel }) { const [fileFormatInvalidMessage, setFileFormatInvalidMessage] = useState( @@ -172,11 +173,6 @@ const ImportProjectModal: React.FC = ({ const [outcomeCount, setOutcomeCount] = useState(0) const onFilePicked = async (projectData: object) => { - // if (!projectData.tags) { - // alert('Cannot import projects from versions prior to v1.0.0-alpha') - // onClose() - // return - // } let projectIds: { cellIdString: CellIdString cellId: CellId @@ -186,16 +182,22 @@ const ImportProjectModal: React.FC = ({ let projectDataParsed = BackwardsCompatibleProjectExportSchema.parse( projectData ) - console.log(projectDataParsed) - const passphrase = projectDataParsed.projectMeta.passphrase + const newRandomPassphrase = await generatePassphrase() + const newProjectData = { + ...projectDataParsed, + projectMeta: { + ...projectDataParsed.projectMeta, + passphrase: newRandomPassphrase, + }, + } setImportingProject(true) - setProjectName(projectDataParsed.projectMeta.name) - setOutcomeCount(Object.keys(projectDataParsed.outcomes).length) + setProjectName(newProjectData.projectMeta.name) + setOutcomeCount(Object.keys(newProjectData.outcomes).length) // first step is to install the app and DNA - projectIds = await installProject(passphrase) + projectIds = await installProject(newRandomPassphrase) // keep the existing passphrase, such that other users know how // to enter the new project - await onImportProject(projectIds.cellIdString, projectData, passphrase) + await onImportProject(projectIds.cellIdString, projectData, newRandomPassphrase) setImportingProject(false) setProjectImported(true) } catch (e) { diff --git a/web/src/routes/Dashboard/Dashboard.component.tsx b/web/src/routes/Dashboard/Dashboard.component.tsx index 74e35d9a..db360355 100644 --- a/web/src/routes/Dashboard/Dashboard.component.tsx +++ b/web/src/routes/Dashboard/Dashboard.component.tsx @@ -7,7 +7,6 @@ import DashboardEmptyState from '../../components/DashboardEmptyState/DashboardE import CreateProjectModal from '../../components/CreateProjectModal/CreateProjectModal' import ImportProjectModal from '../../components/ImportProjectModal/ImportProjectModal' import JoinProjectModal from '../../components/JoinProjectModal/JoinProjectModal' -import ProjectSettingsModal from '../../components/ProjectSettingsModal/ProjectSettingsModal.connector' // import new modals here import { diff --git a/web/src/secrets.ts b/web/src/secrets.ts index e530b613..44df7236 100644 --- a/web/src/secrets.ts +++ b/web/src/secrets.ts @@ -1,7 +1,14 @@ +// since this is a big wordset, dynamically import it +// instead of including in the main bundle +async function generatePassphrase() { + const { default: randomWord } = await import('diceware-word') + return `${randomWord()} ${randomWord()} ${randomWord()} ${randomWord()} ${randomWord()}` +} + const passphraseToUid = (passphrase: string) => `uid-${passphrase.split(' ').join('-')}` const uidToPassphrase = (uid: string) => uid.replace('uid-', '').split('-').join(' ') -export { passphraseToUid, uidToPassphrase } +export { generatePassphrase, passphraseToUid, uidToPassphrase } diff --git a/web/tsconfig.json b/web/tsconfig.json index bef23a1e..c802b1e2 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -4,7 +4,7 @@ "checkJs": false, "sourceMap": true, "noImplicitAny": false, - "module": "ES6", + "module": "ES2020", "target": "ES6", "jsx": "react", "allowJs": true, From bd643bd55819f48a734504d1d26a8dfd73cb9812 Mon Sep 17 00:00:00 2001 From: Pegah Date: Sun, 29 Oct 2023 19:23:51 -0700 Subject: [PATCH 05/18] modal css improvements for join and create project --- .../CreateProjectModal.scss | 101 +++++++++--------- .../JoinProjectModal/JoinProjectModal.scss | 8 +- .../ProjectSecret/ProjectSecret.scss | 4 +- 3 files changed, 57 insertions(+), 56 deletions(-) diff --git a/web/src/components/CreateProjectModal/CreateProjectModal.scss b/web/src/components/CreateProjectModal/CreateProjectModal.scss index fa78e867..3268ba17 100644 --- a/web/src/components/CreateProjectModal/CreateProjectModal.scss +++ b/web/src/components/CreateProjectModal/CreateProjectModal.scss @@ -1,39 +1,40 @@ .create-project-modal-wrapper { - height: 500px; - overflow: hidden; + height: 32rem; + width: 30rem; + overflow: auto; position: relative; justify-content: space-between; align-items: center; -} -.create-project-modal-wrapper .validating-form-input { - margin-bottom: 28px; -} + .validating-form-input { + margin-bottom: 28px; + } -.project-modal-content { - line-height: 1.45; -} + .project-modal-content { + line-height: 1.45; + } -.create-project-image-row { - display: flex; - flex-direction: row; - align-items: flex-end; -} + .create-project-image-row { + display: flex; + flex-direction: row; + align-items: flex-end; -.create-project-image-row .validating-form-input { - margin-bottom: 0; -} + .validating-form-input { + margin-bottom: 0; + } -.create-project-image { - margin-left: 8px; - width: 44px; - height: 44px; - background-size: cover; - background-repeat: no-repeat; - border-radius: 10px; - border: 2px solid #f3efeb; - box-sizing: border-box; - background-color: var(--bg-color-tertiary); + .create-project-image { + margin-left: 8px; + width: 44px; + height: 44px; + background-size: cover; + background-repeat: no-repeat; + border-radius: 10px; + border: 2px solid #f3efeb; + box-sizing: border-box; + background-color: var(--bg-color-tertiary); + } + } } /* create project modal animation */ @@ -42,21 +43,20 @@ position: absolute; opacity: 1; transition: opacity 0.3s, transform 0.4s; - width: 420px; - left: 50px; -} + width: 23rem; + left: 3.5rem; -.create-project-form.project-created { - // background-color: blue; - opacity: 0; - -webkit-transform: translateX(-300px); - transform: translateX(-300px); - width: 420px; - left: 52px; + /* project created modal animation */ + &.project-created { + // background-color: blue; + opacity: 0; + -webkit-transform: translateX(-300px); + transform: translateX(-300px); + width: 23rem; + left: 3.5rem; + } } -/* project created modal animation */ - /* default is to be hidden */ .project-created-modal { @@ -65,17 +65,18 @@ -webkit-transform: scale(0.8); transform: scale(0.8); transition: opacity 0.3s, transform 0.4s; -} -/* then it shows */ - -.project-created-modal.project-created { - opacity: 1; - -webkit-transform: scale(1); - transform: scale(1); -} + /* then it shows */ + &.project-created { + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1); + width: 23rem; + left: 3.5rem; -.project-created-modal.project-created .project-modal-content-spacer { - margin-bottom: 5.5rem; - margin-top: 4rem; + .project-modal-content-spacer { + margin-top: 5rem; + margin-bottom: 5rem; + } + } } diff --git a/web/src/components/JoinProjectModal/JoinProjectModal.scss b/web/src/components/JoinProjectModal/JoinProjectModal.scss index c04e1f2b..5a9772b3 100644 --- a/web/src/components/JoinProjectModal/JoinProjectModal.scss +++ b/web/src/components/JoinProjectModal/JoinProjectModal.scss @@ -2,8 +2,7 @@ overflow: hidden; position: relative; justify-content: space-between; - width: 34.5rem; - height: 27rem; + width: 33.5rem; } .project-modal-button .button.large { @@ -16,8 +15,9 @@ position: absolute; opacity: 1; transition: opacity 0.3s, transform 0.4s; - width: 420px; - top: 70px; + width: 24.5rem; + top: 5rem; + left: 4.5rem; } .join-project-form.project-join-check-is-done { diff --git a/web/src/components/ProjectSecret/ProjectSecret.scss b/web/src/components/ProjectSecret/ProjectSecret.scss index fa848c48..38a7cc45 100644 --- a/web/src/components/ProjectSecret/ProjectSecret.scss +++ b/web/src/components/ProjectSecret/ProjectSecret.scss @@ -44,8 +44,8 @@ .secret-copy-message { color: var(--color-forest-green); - font-size: 14px; + font-size: max(0.825rem, 13px); position: absolute; bottom: -1.375rem; - left: 0.25rem; + left: 0.75rem; } From 1bf64af1c84f417404e0fc83c867d5e06820cf36 Mon Sep 17 00:00:00 2001 From: Connor Turland <1409121+Connoropolous@users.noreply.github.com> Date: Sun, 29 Oct 2023 19:26:44 -0700 Subject: [PATCH 06/18] Apply suggestions from code review --- web/src/components/CreateProjectModal/CreateProjectModal.scss | 1 - web/src/components/JoinProjectModal/JoinProjectModal.tsx | 1 - web/src/components/PreferenceSelect/PreferenceSelect.scss | 1 - web/src/components/Preferences/Preferences.scss | 1 - web/src/components/ProfileEditForm/ProfileEditForm.scss | 3 --- .../ProjectSettingsModal/ProjectSettingsModal.component.tsx | 3 --- web/src/components/Typography/Typography.scss | 2 -- web/src/routes/GlobalModals.tsx | 3 --- 8 files changed, 15 deletions(-) diff --git a/web/src/components/CreateProjectModal/CreateProjectModal.scss b/web/src/components/CreateProjectModal/CreateProjectModal.scss index 3268ba17..6d9fd0b1 100644 --- a/web/src/components/CreateProjectModal/CreateProjectModal.scss +++ b/web/src/components/CreateProjectModal/CreateProjectModal.scss @@ -48,7 +48,6 @@ /* project created modal animation */ &.project-created { - // background-color: blue; opacity: 0; -webkit-transform: translateX(-300px); transform: translateX(-300px); diff --git a/web/src/components/JoinProjectModal/JoinProjectModal.tsx b/web/src/components/JoinProjectModal/JoinProjectModal.tsx index df9154c2..b71db777 100644 --- a/web/src/components/JoinProjectModal/JoinProjectModal.tsx +++ b/web/src/components/JoinProjectModal/JoinProjectModal.tsx @@ -72,7 +72,6 @@ function ProjectJoinFollowUp({ onDone, checkDone }) { >
- {/* */}
diff --git a/web/src/components/PreferenceSelect/PreferenceSelect.scss b/web/src/components/PreferenceSelect/PreferenceSelect.scss index 3f5bd080..504f89f9 100644 --- a/web/src/components/PreferenceSelect/PreferenceSelect.scss +++ b/web/src/components/PreferenceSelect/PreferenceSelect.scss @@ -39,7 +39,6 @@ .preference-select-options-wrapper { display: flex; flex-direction: row; - // padding: 0 30px; margin-bottom: 1rem; } diff --git a/web/src/components/Preferences/Preferences.scss b/web/src/components/Preferences/Preferences.scss index 1fa78df6..0268111b 100644 --- a/web/src/components/Preferences/Preferences.scss +++ b/web/src/components/Preferences/Preferences.scss @@ -32,7 +32,6 @@ justify-content: center; align-items: center; z-index: 4; - // border-radius: 1rem; .preferences-close { position: absolute; diff --git a/web/src/components/ProfileEditForm/ProfileEditForm.scss b/web/src/components/ProfileEditForm/ProfileEditForm.scss index a8f9f186..2926f3f3 100644 --- a/web/src/components/ProfileEditForm/ProfileEditForm.scss +++ b/web/src/components/ProfileEditForm/ProfileEditForm.scss @@ -1,9 +1,6 @@ .profile_edit_form { text-align: center; overflow-y: auto; - // margin: 0 auto; - // overflow-y: scroll; - // height: 100%; .small-close { position: absolute; diff --git a/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx b/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx index 7f00982d..db4ecdce 100644 --- a/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx +++ b/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx @@ -272,7 +272,4 @@ export default function ProjectSettingsModal({ ) } -function setToastState(arg0: { id: any; text: string; type: string }) { - throw new Error('Function not implemented.') -} diff --git a/web/src/components/Typography/Typography.scss b/web/src/components/Typography/Typography.scss index f1b756f2..9bf0b94f 100644 --- a/web/src/components/Typography/Typography.scss +++ b/web/src/components/Typography/Typography.scss @@ -149,7 +149,5 @@ color: var(--text-color-tertiary); font-size: max(0.875rem, 11.5px); line-height: 1.6; - // margin: 0.125rem 0 0.375rem 0; - // flex: 1; } } diff --git a/web/src/routes/GlobalModals.tsx b/web/src/routes/GlobalModals.tsx index 8e6448a4..06678de6 100644 --- a/web/src/routes/GlobalModals.tsx +++ b/web/src/routes/GlobalModals.tsx @@ -257,6 +257,3 @@ const GlobalModals: React.FC = ({ } export default GlobalModals -function handleKeyDown(this: Window, ev: KeyboardEvent) { - throw new Error('Function not implemented.') -} From 8ab48e0c9723126c254a0a6071782a0737403f36 Mon Sep 17 00:00:00 2001 From: Connor Turland <1409121+Connoropolous@users.noreply.github.com> Date: Sun, 29 Oct 2023 20:13:45 -0700 Subject: [PATCH 07/18] remove ProjectExported modal and use Toast --- web/src/components/Header/HeaderLeftPanel.tsx | 50 +++++++++++++------ .../components/Preferences/Preferences.tsx | 4 +- web/src/context/ModalContexts.ts | 5 -- web/src/hooks/useFileDownloaded.ts | 2 +- .../ephemeral/local-preferences/reducer.ts | 2 +- web/src/routes/GlobalModals.tsx | 24 --------- web/src/utils.ts | 11 ++++ 7 files changed, 49 insertions(+), 49 deletions(-) diff --git a/web/src/components/Header/HeaderLeftPanel.tsx b/web/src/components/Header/HeaderLeftPanel.tsx index 7f57eaa4..94d3b30d 100644 --- a/web/src/components/Header/HeaderLeftPanel.tsx +++ b/web/src/components/Header/HeaderLeftPanel.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react' +import React, { useContext, useEffect, useRef, useState } from 'react' import { NavLink, Route, useLocation, useRouteMatch } from 'react-router-dom' import useOnClickOutside from 'use-onclickoutside' @@ -23,6 +23,9 @@ import triangleTopWhite from '../../images/triangle-top-white.svg' // @ts-ignore import DoorOpen from '../../images/door-open.svg' import { ModalState, OpenModal } from '../../context/ModalContexts' +import { getCurrentDateFormatted } from '../../utils' +import useFileDownloaded from '../../hooks/useFileDownloaded' +import ToastContext, { ShowToast } from '../../context/ToastContext' function ActiveEntryPoint({ entryPoint, @@ -98,6 +101,19 @@ const HeaderLeftPanel: React.FC = ({ : [] const ref = useRef() + const { setToastState } = useContext(ToastContext) + const { fileDownloaded, setFileDownloaded } = useFileDownloaded() + + useEffect(() => { + if (fileDownloaded) { + setFileDownloaded(false) + setToastState({ + id: ShowToast.Yes, + text: 'Project Exported', + type: 'confirmation', + }) + } + }, [fileDownloaded, setFileDownloaded, setToastState]) // map, table and priority view routes @@ -111,6 +127,14 @@ const HeaderLeftPanel: React.FC = ({ useOnClickOutside(ref, () => setOpenEntryPointPicker(false)) const [openEntryPointPicker, setOpenEntryPointPicker] = useState(false) + // replace spaces with dashes for project name + // add in the export date like 2023-12-31 and make it based on the + // timezone of the user + const projectNameForExport = `${projectName.replace( + /\s/g, + '-' + )}-${getCurrentDateFormatted()}` + return (
@@ -212,10 +236,12 @@ const HeaderLeftPanel: React.FC = ({ withTooltip tooltipText="Project Settings" size="header" - onClick={() => setModalState({ - id: OpenModal.ProjectSettings, - cellId: projectId - })} + onClick={() => + setModalState({ + id: OpenModal.ProjectSettings, + cellId: projectId, + }) + } className="header-action-icon" /> {/* Export */} @@ -240,24 +266,16 @@ const HeaderLeftPanel: React.FC = ({ { - setModalState({ - id: OpenModal.ProjectExported, - projectName, - }) setIsExportOpen(false) }} /> { - setModalState({ - id: OpenModal.ProjectExported, - projectName, - }) setIsExportOpen(false) }} /> @@ -274,7 +292,7 @@ const HeaderLeftPanel: React.FC = ({ onClickInviteMember={() => { setModalState({ id: OpenModal.InviteMembers, - passphrase: projectPassphrase + passphrase: projectPassphrase, }) }} /> diff --git a/web/src/components/Preferences/Preferences.tsx b/web/src/components/Preferences/Preferences.tsx index 666d9d1d..4cf1838a 100644 --- a/web/src/components/Preferences/Preferences.tsx +++ b/web/src/components/Preferences/Preferences.tsx @@ -85,7 +85,7 @@ function NavigationModeInternal({ navigation, setNavigationSelected }) {
{ if (window.require) { const { ipcRenderer } = window.require('electron') - ipcRenderer.once('exportDownloaded', () => { + ipcRenderer.on('exportDownloaded', () => { setFileDownloaded(true) }) } diff --git a/web/src/redux/ephemeral/local-preferences/reducer.ts b/web/src/redux/ephemeral/local-preferences/reducer.ts index 95fdb4a7..d9dbe139 100644 --- a/web/src/redux/ephemeral/local-preferences/reducer.ts +++ b/web/src/redux/ephemeral/local-preferences/reducer.ts @@ -25,7 +25,7 @@ const defaultState = { // default to trackpad navigation navigation: getLocalItem(NAV_KEY) || TRACKPAD, // default to modal keyboard navigation - keyboardNavigation: getLocalItem(KEYBOARD_NAV_KEY) || MODAL, + keyboardNavigation: getLocalItem(KEYBOARD_NAV_KEY) || COORDINATES, } export default function (state = defaultState, action) { diff --git a/web/src/routes/GlobalModals.tsx b/web/src/routes/GlobalModals.tsx index 06678de6..529f5c0c 100644 --- a/web/src/routes/GlobalModals.tsx +++ b/web/src/routes/GlobalModals.tsx @@ -228,30 +228,6 @@ const GlobalModals: React.FC = ({ } /> - - {/* Export Successful Modal */} - - - You just exported the{' '} - - {modalState.id === OpenModal.ProjectExported && - modalState.projectName} - {' '} - project data. You can use that file to transfer the project to a - different owner, or archive as a backup. - - } - primaryButton="Got it" - primaryButtonAction={setModalToNone} - /> - ) } diff --git a/web/src/utils.ts b/web/src/utils.ts index 0cc76a0d..b83ac318 100644 --- a/web/src/utils.ts +++ b/web/src/utils.ts @@ -23,3 +23,14 @@ export function cellIdFromString(str: string): CellId { const [dnahashstring, agentpubkeyhashstring] = str.split(CELL_ID_DIVIDER) return [hashFromString(dnahashstring), hashFromString(agentpubkeyhashstring)] } + +export function getCurrentDateFormatted() { + const now = new Date() + + const year = now.getFullYear() + // getMonth() returns a zero-based month, so +1 to get the correct month number + const month = (now.getMonth() + 1).toString().padStart(2, '0') + const day = now.getDate().toString().padStart(2, '0') + + return `${year}-${month}-${day}` +} From b19eca9842e1546a67e213ee00061c30253648ac Mon Sep 17 00:00:00 2001 From: Connor Turland <1409121+Connoropolous@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:07:24 -0700 Subject: [PATCH 08/18] fix project settings editing bug --- .../ProjectSettingsModal.component.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx b/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx index db4ecdce..5c251a25 100644 --- a/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx +++ b/web/src/components/ProjectSettingsModal/ProjectSettingsModal.component.tsx @@ -206,6 +206,7 @@ export default function ProjectSettingsModal({ setModalState, memberCount, }: ProjectSettingsModalProps) { + const [cachedProjectMetaHash, setCachedProjectMetaHash] = useState('') const [updatingProject, setUpdatingProject] = useState(false) const [projectName, setProjectName] = useState('') const [projectCoverUrl, setProjectCoverUrl] = useState('') @@ -224,18 +225,28 @@ export default function ProjectSettingsModal({ cellIdString ) setUpdatingProject(false) + setCachedProjectMetaHash('') + onClose() + } + + const onCancel = () => { + setCachedProjectMetaHash('') onClose() } // editable useEffect(() => { - if (project) { + if (project && project.actionHash !== cachedProjectMetaHash) { + setCachedProjectMetaHash(project.actionHash) setProjectName(project.name) setProjectCoverUrl(project.image) setProjectPassphrase(project.passphrase) } - }, [project]) + }, [project, setCachedProjectMetaHash]) + // TODO: once we migrate off version 9 stream, we can simplify this + // this commented out code, because we won't have any projects that use + // the old pattern which had randomness in it. // const uid = passphraseToUid(projectPassphrase) // const installedAppId = `${PROJECT_APP_PREFIX}-${uid}` useEffect(() => { @@ -253,7 +264,7 @@ export default function ProjectSettingsModal({ Date: Mon, 30 Oct 2023 12:53:50 -0700 Subject: [PATCH 09/18] remove project created modal and add toast for create project --- .../CreateProjectModal.scss | 93 ++++--------- .../CreateProjectModal/CreateProjectModal.tsx | 124 ++++++++---------- .../components/IntroScreen/IntroScreen.scss | 22 ---- .../LoadingScreen/LoadingScreen.scss | 2 +- .../MigrationProgress/MigrationProgress.scss | 5 +- web/src/components/Modal/Modal.scss | 9 +- .../PickerTemplate/PickerTemplate.scss | 2 +- web/src/components/Toast/Toast.scss | 2 +- web/src/components/Tooltip/Tooltip.scss | 2 +- web/src/components/Typography/Typography.scss | 2 +- 10 files changed, 85 insertions(+), 178 deletions(-) diff --git a/web/src/components/CreateProjectModal/CreateProjectModal.scss b/web/src/components/CreateProjectModal/CreateProjectModal.scss index 6d9fd0b1..a7db7dfa 100644 --- a/web/src/components/CreateProjectModal/CreateProjectModal.scss +++ b/web/src/components/CreateProjectModal/CreateProjectModal.scss @@ -1,81 +1,32 @@ .create-project-modal-wrapper { - height: 32rem; width: 30rem; overflow: auto; position: relative; justify-content: space-between; align-items: center; - .validating-form-input { - margin-bottom: 28px; - } - - .project-modal-content { - line-height: 1.45; - } - - .create-project-image-row { - display: flex; - flex-direction: row; - align-items: flex-end; - - .validating-form-input { - margin-bottom: 0; - } - - .create-project-image { - margin-left: 8px; - width: 44px; - height: 44px; - background-size: cover; - background-repeat: no-repeat; - border-radius: 10px; - border: 2px solid #f3efeb; - box-sizing: border-box; - background-color: var(--bg-color-tertiary); - } - } -} - -/* create project modal animation */ - -.create-project-form { - position: absolute; - opacity: 1; - transition: opacity 0.3s, transform 0.4s; - width: 23rem; - left: 3.5rem; - - /* project created modal animation */ - &.project-created { - opacity: 0; - -webkit-transform: translateX(-300px); - transform: translateX(-300px); - width: 23rem; - left: 3.5rem; - } -} - -/* default is to be hidden */ - -.project-created-modal { - position: absolute; - opacity: 0; - -webkit-transform: scale(0.8); - transform: scale(0.8); - transition: opacity 0.3s, transform 0.4s; - - /* then it shows */ - &.project-created { - opacity: 1; - -webkit-transform: scale(1); - transform: scale(1); - width: 23rem; - left: 3.5rem; - - .project-modal-content-spacer { - margin-top: 5rem; - margin-bottom: 5rem; + .create-project-form { + + .create-project-image-row { + display: flex; + flex-direction: row; + align-items: flex-end; + + .validating-form-input { + margin-bottom: 0; + } + + .create-project-image { + margin-left: 8px; + width: 44px; + height: 44px; + background-size: cover; + background-repeat: no-repeat; + border-radius: 10px; + border: 2px solid #f3efeb; + box-sizing: border-box; + background-color: var(--bg-color-tertiary); + } } } } diff --git a/web/src/components/CreateProjectModal/CreateProjectModal.tsx b/web/src/components/CreateProjectModal/CreateProjectModal.tsx index 6e73be9d..c8aa3925 100644 --- a/web/src/components/CreateProjectModal/CreateProjectModal.tsx +++ b/web/src/components/CreateProjectModal/CreateProjectModal.tsx @@ -1,8 +1,8 @@ -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useContext } from 'react' import './CreateProjectModal.scss' import ValidatingFormInput from '../ValidatingFormInput/ValidatingFormInput' -import Modal from '../Modal/Modal' +import Modal, { ModalContent } from '../Modal/Modal' import { ProjectModalButton, ProjectModalContent, @@ -13,11 +13,11 @@ import { import ProjectSecret from '../ProjectSecret/ProjectSecret' import ButtonWithPendingState from '../ButtonWithPendingState/ButtonWithPendingState' import { generatePassphrase } from '../../secrets' +import ToastContext, { ShowToast } from '../../context/ToastContext' function CreateProjectForm({ creatingProject, onSubmit, - projectCreated, projectName, setProjectName, projectCoverUrl, @@ -50,9 +50,6 @@ function CreateProjectForm({ validateProjectName() }, [projectName, shouldInvalidateProjectName]) - const subheading = - 'You can share the project with people or just keep it to yourself' - const actionButtonContent = ( +
- {/* project name */} @@ -118,55 +110,18 @@ function CreateProjectForm({
) } - -function ProjectCreatedModal({ onDone, projectCreated, projectSecret }) { - return ( -
- - - - - - - -
- ) -} - export default function CreateProjectModal({ showModal, onClose, onCreateProject, }) { - const reset = () => { - setProjectName('') - setProjectCoverUrl('') - } - const [creatingProject, setCreatingProject] = useState(false) - - const onSubmit = async () => { - setCreatingProject(true) - try { - await onCreateProject( - { - name: projectName, - image: projectCoverUrl, - }, - projectSecret - ) - setCreatingProject(false) - setProjectCreated(true) - reset() - } catch (e) { - console.log(e) - } - } + // pull in the toast context + const { setToastState } = useContext(ToastContext) - let hasUnmounted = false + const [creatingProject, setCreatingProject] = useState(false) + const [projectSecret, setProjectSecret] = useState('') + const [projectName, setProjectName] = useState('') + const [projectCoverUrl, setProjectCoverUrl] = useState('') const genAndSetPassphrase = async () => { try { @@ -177,16 +132,7 @@ export default function CreateProjectModal({ } } - const onDone = () => { - onClose() - setProjectCreated(false) - genAndSetPassphrase() - } - - const [projectSecret, setProjectSecret] = useState('') - const [projectCreated, setProjectCreated] = useState(false) - const [projectName, setProjectName] = useState('') - const [projectCoverUrl, setProjectCoverUrl] = useState('') + let hasUnmounted = false // generate a passphrase on component mount useEffect(() => { @@ -197,22 +143,56 @@ export default function CreateProjectModal({ } }, []) + const resetCreateProjectState = () => { + onClose() + // wait for the closing of the modal animation + setTimeout(() => { + setCreatingProject(false) + setProjectName('') + setProjectCoverUrl('') + genAndSetPassphrase() + }, 100) + } + + const onSubmit = async () => { + setCreatingProject(true) + try { + await onCreateProject( + { + name: projectName, + image: projectCoverUrl, + }, + projectSecret + ) + resetCreateProjectState() + // if project creation is successful + setToastState({ + id: ShowToast.Yes, + text: 'New project created', + type: 'confirmation', + }) + } catch (e) { + // if project creation is not successful + console.log(e) + resetCreateProjectState() + setToastState({ + id: ShowToast.Yes, + text: 'Error creating project', + type: 'destructive', + }) + } + } + return ( - Date: Mon, 30 Oct 2023 14:40:39 -0700 Subject: [PATCH 10/18] move multieditbar to left side of the screen --- web/src/components/MultiEditBar/MultiEditBar.scss | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/web/src/components/MultiEditBar/MultiEditBar.scss b/web/src/components/MultiEditBar/MultiEditBar.scss index 04496efa..601e0b49 100644 --- a/web/src/components/MultiEditBar/MultiEditBar.scss +++ b/web/src/components/MultiEditBar/MultiEditBar.scss @@ -2,17 +2,19 @@ .multi-edit-bar { position: fixed; - bottom: -100px; - left: 50%; - transform: translateX(-50%); - padding: 0 8px; + left: -100px; + top: 50%; + transform: translateY(-50%); + padding: 0.5rem; background-color: #ffffff; box-shadow: 0px 1px 6px rgba(0, 0, 0, 0.16); border-radius: 10px; opacity: 1; display: flex; + flex-direction: column; align-items: center; - height: 50px; + justify-content: center; + width: 50px; transition: bottom 0.2s ease-in-out; } @@ -22,7 +24,7 @@ } .multi-edit-bar.has-selection { - bottom: 10px; + left: 10px; } .multi-edit-bar > .icon { From 570ebf8d23421ad661dc5ca17cadcff6de96b52e Mon Sep 17 00:00:00 2001 From: Pegah Date: Mon, 30 Oct 2023 15:25:00 -0700 Subject: [PATCH 11/18] remove project join followup modal and add toast for join project + fix the pending button icon --- .../ButtonWithPendingState.js | 2 +- .../CreateProjectModal.scss | 2 +- .../CreateProjectModal/CreateProjectModal.tsx | 6 +- .../JoinProjectModal/JoinProjectModal.scss | 84 +-------------- .../JoinProjectModal/JoinProjectModal.tsx | 100 +++++++----------- 5 files changed, 44 insertions(+), 150 deletions(-) diff --git a/web/src/components/ButtonWithPendingState/ButtonWithPendingState.js b/web/src/components/ButtonWithPendingState/ButtonWithPendingState.js index 8ccbb7be..e46beaa1 100644 --- a/web/src/components/ButtonWithPendingState/ButtonWithPendingState.js +++ b/web/src/components/ButtonWithPendingState/ButtonWithPendingState.js @@ -10,7 +10,7 @@ export default function ButtonWithPendingState({ }) { return pending ? ( - + {pendingText} ) : ( diff --git a/web/src/components/CreateProjectModal/CreateProjectModal.scss b/web/src/components/CreateProjectModal/CreateProjectModal.scss index a7db7dfa..10b12ebb 100644 --- a/web/src/components/CreateProjectModal/CreateProjectModal.scss +++ b/web/src/components/CreateProjectModal/CreateProjectModal.scss @@ -1,4 +1,4 @@ -.create-project-modal-wrapper { +.create-project-modal { width: 30rem; overflow: auto; position: relative; diff --git a/web/src/components/CreateProjectModal/CreateProjectModal.tsx b/web/src/components/CreateProjectModal/CreateProjectModal.tsx index c8aa3925..96604e7c 100644 --- a/web/src/components/CreateProjectModal/CreateProjectModal.tsx +++ b/web/src/components/CreateProjectModal/CreateProjectModal.tsx @@ -2,15 +2,13 @@ import React, { useState, useEffect, useContext } from 'react' import './CreateProjectModal.scss' import ValidatingFormInput from '../ValidatingFormInput/ValidatingFormInput' -import Modal, { ModalContent } from '../Modal/Modal' +import Modal from '../Modal/Modal' import { ProjectModalButton, ProjectModalContent, ProjectModalContentSpacer, ProjectModalHeading, - ProjectModalSubHeading, } from '../ProjectModal/ProjectModal' -import ProjectSecret from '../ProjectSecret/ProjectSecret' import ButtonWithPendingState from '../ButtonWithPendingState/ButtonWithPendingState' import { generatePassphrase } from '../../secrets' import ToastContext, { ShowToast } from '../../context/ToastContext' @@ -188,7 +186,7 @@ export default function CreateProjectModal({ white active={showModal} onClose={onClose} - className="create-project-modal-wrapper" + className="create-project-modal" >
) From 609df5375492e6ba0626278f688e3e8376274930 Mon Sep 17 00:00:00 2001 From: Pegah Date: Mon, 30 Oct 2023 16:39:24 -0700 Subject: [PATCH 13/18] invite members modal cleanups --- .../InviteMembersModal/InviteMembersModal.scss | 3 +++ .../{InviteMembersModal.js => InviteMembersModal.tsx} | 10 ++++------ .../components/JoinProjectModal/JoinProjectModal.tsx | 2 +- web/src/components/ProjectSecret/ProjectSecret.scss | 6 +++--- web/src/components/ProjectSecret/ProjectSecret.tsx | 1 - 5 files changed, 11 insertions(+), 11 deletions(-) rename web/src/components/InviteMembersModal/{InviteMembersModal.js => InviteMembersModal.tsx} (75%) diff --git a/web/src/components/InviteMembersModal/InviteMembersModal.scss b/web/src/components/InviteMembersModal/InviteMembersModal.scss index e69de29b..e8ed65e5 100644 --- a/web/src/components/InviteMembersModal/InviteMembersModal.scss +++ b/web/src/components/InviteMembersModal/InviteMembersModal.scss @@ -0,0 +1,3 @@ +.invite-members-modal { + width: 33rem; +} \ No newline at end of file diff --git a/web/src/components/InviteMembersModal/InviteMembersModal.js b/web/src/components/InviteMembersModal/InviteMembersModal.tsx similarity index 75% rename from web/src/components/InviteMembersModal/InviteMembersModal.js rename to web/src/components/InviteMembersModal/InviteMembersModal.tsx index 323c54d7..b9e3d288 100644 --- a/web/src/components/InviteMembersModal/InviteMembersModal.js +++ b/web/src/components/InviteMembersModal/InviteMembersModal.tsx @@ -7,28 +7,26 @@ import { ProjectModalContent, ProjectModalContentSpacer, ProjectModalHeading, + ProjectModalSubHeading, } from '../ProjectModal/ProjectModal' import ProjectSecret from '../ProjectSecret/ProjectSecret' export default function InviteMembersModal({ showModal, onClose, passphrase }) { - const onDone = () => { - onClose() - } - return ( + - + ) } diff --git a/web/src/components/JoinProjectModal/JoinProjectModal.tsx b/web/src/components/JoinProjectModal/JoinProjectModal.tsx index e301ddda..dc1d03ef 100644 --- a/web/src/components/JoinProjectModal/JoinProjectModal.tsx +++ b/web/src/components/JoinProjectModal/JoinProjectModal.tsx @@ -42,7 +42,7 @@ function JoinProjectForm({ invalidInput={invalidText} errorText={invalidText} label="Project Invitation Secret" - helpText="Paste the 5 word secret you received from the project host" + helpText="Paste the 5-word secret you received from the project host" /> diff --git a/web/src/components/ProjectSecret/ProjectSecret.scss b/web/src/components/ProjectSecret/ProjectSecret.scss index fd4a1b24..2e9b7605 100644 --- a/web/src/components/ProjectSecret/ProjectSecret.scss +++ b/web/src/components/ProjectSecret/ProjectSecret.scss @@ -14,10 +14,10 @@ } .project-secret-copy-secret { - margin-left: 8px; + margin-left: 0.5rem; border: 0.125rem solid var(--border-color-form-input); - border-radius: 10px; - padding: 7px; + border-radius: 0.625rem; + padding: 0.5rem; cursor: pointer; transition: border 0.2s ease-in-out; box-sizing: border-box; diff --git a/web/src/components/ProjectSecret/ProjectSecret.tsx b/web/src/components/ProjectSecret/ProjectSecret.tsx index 07511f79..394d7a28 100644 --- a/web/src/components/ProjectSecret/ProjectSecret.tsx +++ b/web/src/components/ProjectSecret/ProjectSecret.tsx @@ -24,7 +24,6 @@ export default function ProjectSecret({ passphrase }) { {/* copy to clipboard button */}
Date: Mon, 30 Oct 2023 18:50:47 -0700 Subject: [PATCH 14/18] add small scope checkbox option to create outcome card to be fixed: - after checking the checkbox, hitting enter should finish creating the outcome - also if the mouse is clicked too close to the edge of the screen for opening the create outcome card, the card should be contained within the edges of the screen. --- .../ButtonCheckbox/ButtonCheckbox.scss | 3 +- .../EVRightColumn/EVRightColumn.scss | 5 +- .../MapViewContextMenu/MapViewContextMenu.tsx | 12 ++- .../MapViewCreateOutcome.component.tsx} | 97 +++++++++++++------ .../MapViewCreateOutcome.connector.ts} | 11 ++- .../MapViewCreateOutcome.scss | 45 +++++++++ .../MapViewOutcomeTitleForm.scss | 26 ----- .../computeArguments/argsForDrawGlow.ts | 4 +- web/src/drawing/index.ts | 4 +- .../ProjectView/MapView/MapView.component.tsx | 5 +- web/src/styles.ts | 4 +- web/src/variables.scss | 3 +- 12 files changed, 148 insertions(+), 71 deletions(-) rename web/src/components/{MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.component.tsx => MapViewCreateOutcome/MapViewCreateOutcome.component.tsx} (63%) rename web/src/components/{MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.connector.ts => MapViewCreateOutcome/MapViewCreateOutcome.connector.ts} (89%) create mode 100644 web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.scss delete mode 100644 web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.scss diff --git a/web/src/components/ButtonCheckbox/ButtonCheckbox.scss b/web/src/components/ButtonCheckbox/ButtonCheckbox.scss index b879d730..80ea00cf 100644 --- a/web/src/components/ButtonCheckbox/ButtonCheckbox.scss +++ b/web/src/components/ButtonCheckbox/ButtonCheckbox.scss @@ -14,7 +14,7 @@ transition: 0.2s box-shadow ease; -webkit-font-smoothing: antialiased; user-select: none; - border: 0.15rem solid #ffffff00; + border: 0.15rem solid var(--border-color-button-checkbox); &:hover, &.focused { @@ -45,6 +45,7 @@ .button-checkbox-text { user-select: none; + margin-right: 0.5rem; /* Standard */ } } diff --git a/web/src/components/ExpandedViewMode/EVRightColumn/EVRightColumn.scss b/web/src/components/ExpandedViewMode/EVRightColumn/EVRightColumn.scss index 79a284d9..aea78a77 100644 --- a/web/src/components/ExpandedViewMode/EVRightColumn/EVRightColumn.scss +++ b/web/src/components/ExpandedViewMode/EVRightColumn/EVRightColumn.scss @@ -9,8 +9,9 @@ flex-direction: column; justify-content: space-between; overflow: scroll; - padding: 2.5rem 3rem 0 2rem; - width: 11.25rem; + padding: 2.5rem 3rem 0 1.75rem; + box-sizing: border-box; + width: 16.25rem; } .ev-right-column-section { diff --git a/web/src/components/MapViewContextMenu/MapViewContextMenu.tsx b/web/src/components/MapViewContextMenu/MapViewContextMenu.tsx index c6c34b3e..9455267f 100644 --- a/web/src/components/MapViewContextMenu/MapViewContextMenu.tsx +++ b/web/src/components/MapViewContextMenu/MapViewContextMenu.tsx @@ -1,8 +1,9 @@ -import React from 'react' +import React, { useContext } from 'react' import { ActionHashB64, CellIdString } from '../../types/shared' import './MapViewContextMenu.scss' import ContextMenu from '../ContextMenu/ContextMenu' +import ToastContext, { ShowToast } from '../../context/ToastContext' export type CheckboxProps = { projectCellId: CellIdString @@ -36,6 +37,10 @@ const Checkbox: React.FC = ({ collapseOutcome, unsetContextMenu, }) => { + +// pull in the toast context +const { setToastState } = useContext(ToastContext) + const wrappedCollapseOutcome = () => { collapseOutcome(projectCellId, outcomeActionHash) unsetContextMenu() @@ -47,6 +52,11 @@ const Checkbox: React.FC = ({ const copyOutcomeStatement = () => { navigator.clipboard.writeText(outcomeStatement) unsetContextMenu() + setToastState({ + id: ShowToast.Yes, + text: 'Outcome statement copied to clipboard', + type: 'confirmation', + }) } const actions = [] diff --git a/web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.component.tsx b/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.component.tsx similarity index 63% rename from web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.component.tsx rename to web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.component.tsx index c190643e..db311497 100644 --- a/web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.component.tsx +++ b/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.component.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from 'react' +import React, { useRef, useState } from 'react' import useOnClickOutside from 'use-onclickoutside' import TextareaAutosize from 'react-textarea-autosize' import moment from 'moment' @@ -11,7 +11,7 @@ import { lineHeightMultiplier, secondZoomThreshold, } from '../../drawing/dimensions' -import './MapViewOutcomeTitleForm.scss' +import './MapViewCreateOutcome.scss' import { AgentPubKeyB64, CellIdString, @@ -19,14 +19,22 @@ import { Option, } from '../../types/shared' import { LinkedOutcomeDetails, Outcome, RelationInput } from '../../types' +import Checkbox from '../Checkbox/Checkbox' +import ButtonCheckbox from '../ButtonCheckbox/ButtonCheckbox' +import { coordsCanvasToPage } from '../../drawing/coordinateSystems' +import Icon from '../Icon/Icon' -export type MapViewOutcomeTitleFormOwnProps = { +export type MapViewCreateOutcomeOwnProps = { projectId: CellIdString } -export type MapViewOutcomeTitleFormConnectorStateProps = { +export type MapViewCreateOutcomeConnectorStateProps = { activeAgentPubKey: AgentPubKeyB64 scale: number + translate: { + x: number + y: number + } // the value of the text input content: string // coordinates in css terms for the box @@ -38,7 +46,7 @@ export type MapViewOutcomeTitleFormConnectorStateProps = { existingParentConnectionAddress: ActionHashB64 } -export type MapViewOutcomeTitleFormConnectorDispatchProps = { +export type MapViewCreateOutcomeConnectorDispatchProps = { // callbacks updateContent: (content: string) => void deleteConnection: (actionHash: ActionHashB64) => Promise @@ -49,13 +57,14 @@ export type MapViewOutcomeTitleFormConnectorDispatchProps = { closeOutcomeForm: () => void } -export type MapViewOutcomeTitleFormProps = MapViewOutcomeTitleFormOwnProps & - MapViewOutcomeTitleFormConnectorStateProps & - MapViewOutcomeTitleFormConnectorDispatchProps +export type MapViewCreateOutcomeProps = MapViewCreateOutcomeOwnProps & + MapViewCreateOutcomeConnectorStateProps & + MapViewCreateOutcomeConnectorDispatchProps -const MapViewOutcomeTitleForm: React.FC = ({ +const MapViewCreateOutcome: React.FC = ({ activeAgentPubKey, scale, + translate, content, maybeLinkedOutcome, existingParentConnectionAddress, @@ -66,6 +75,8 @@ const MapViewOutcomeTitleForm: React.FC = ({ createOutcomeWithConnection, closeOutcomeForm, }) => { + const [isSmallScopeChecked, setIsSmallScopeChecked] = useState(false) + /* EVENT HANDLERS */ // when the text value changes const handleChange = (event) => { @@ -87,12 +98,13 @@ const MapViewOutcomeTitleForm: React.FC = ({ const handleBlur = (event) => { // if creating an Outcome event.preventDefault() - handleSubmit() + // handleSubmit() } // this can get called via keyboard events - // or via 'onClickOutside' of the MapViewOutcomeTitleForm component + // or via 'onClickOutside' of the MapViewCreateOutcome component const handleSubmit = async (event?) => { + console.log('here') if (event) { // this is to prevent the page from refreshing // when the form is submitted, which is the @@ -132,9 +144,21 @@ const MapViewOutcomeTitleForm: React.FC = ({ // defaults timestampUpdated: null, editorAgentPubKey: null, - scope: { - Uncertain: { smallsEstimate: 0, timeFrame: null, inBreakdown: false }, - }, + scope: isSmallScopeChecked + ? { + Small: { + achievementStatus: 'NotAchieved', + targetDate: null, + taskList: [], + }, + } + : { + Uncertain: { + smallsEstimate: 0, + timeFrame: null, + inBreakdown: false, + }, + }, tags: [], description: '', isImported: false, @@ -152,28 +176,34 @@ const MapViewOutcomeTitleForm: React.FC = ({ } else if (scale < firstZoomThreshold) { fontSizeToUse = fontSizeLarge } - const textStyle = { - fontSize: fontSizeToUse, - lineHeight: `${parseInt(fontSizeToUse) * lineHeightMultiplier}px`, - } + // const textStyle = { + // fontSize: fontSize, + // lineHeight: `${parseInt(fontSizeToUse) * lineHeightMultiplier}px`, + // } const ref = useRef() useOnClickOutside(ref, handleSubmit) + const pageCoords = coordsCanvasToPage( + { + x: leftConnectionXPosition, + y: topConnectionYPosition, + }, + translate, + scale + ) + return (
-
+ = ({ onKeyDown={handleKeyDown} onFocus={handleFocus} onBlur={handleBlur} - style={textStyle} + // style={textStyle} /> + + {/* + This Outcome is Small Scope */} + {/* small scope option checkbox */} +
+ setIsSmallScopeChecked(newState)} + icon={} + text={'This Outcome is Small Scope'} + /> +
) } -export default MapViewOutcomeTitleForm +export default MapViewCreateOutcome diff --git a/web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.connector.ts b/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.connector.ts similarity index 89% rename from web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.connector.ts rename to web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.connector.ts index a4fcbb55..c5bc9dfb 100644 --- a/web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.connector.ts +++ b/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.connector.ts @@ -8,7 +8,7 @@ import { closeOutcomeForm, updateContent, } from '../../redux/ephemeral/outcome-form/actions' -import MapViewOutcomeTitleForm, { MapViewOutcomeTitleFormConnectorDispatchProps, MapViewOutcomeTitleFormConnectorStateProps, MapViewOutcomeTitleFormOwnProps } from './MapViewOutcomeTitleForm.component' +import MapViewCreateOutcome, { MapViewCreateOutcomeConnectorDispatchProps, MapViewCreateOutcomeConnectorStateProps, MapViewCreateOutcomeOwnProps } from './MapViewCreateOutcome.component' import ProjectsZomeApi from '../../api/projectsApi' import { getAppWs } from '../../hcWebsockets' import { cellIdFromString } from '../../utils' @@ -23,10 +23,10 @@ import { LAYOUT_ANIMATION_TYPICAL_MS } from '../../constants' // to pass it to a component for rendering, as specific properties that // that component expects -function mapStateToProps(state: RootState): MapViewOutcomeTitleFormConnectorStateProps { +function mapStateToProps(state: RootState): MapViewCreateOutcomeConnectorStateProps { const { ui: { - viewport: { scale }, + viewport: { scale, translate }, // all the state for this component is store under state->ui->outcomeForm outcomeForm: { content, @@ -42,6 +42,7 @@ function mapStateToProps(state: RootState): MapViewOutcomeTitleFormConnectorStat maybeLinkedOutcome, activeAgentPubKey: state.agentAddress, scale, + translate, existingParentConnectionAddress, content, leftConnectionXPosition: leftConnectionXPosition, @@ -53,7 +54,7 @@ function mapStateToProps(state: RootState): MapViewOutcomeTitleFormConnectorStat // Designed to pass functions into components which are already wrapped as // action dispatchers for redux action types -function mapDispatchToProps(dispatch, ownProps: MapViewOutcomeTitleFormOwnProps): MapViewOutcomeTitleFormConnectorDispatchProps { +function mapDispatchToProps(dispatch, ownProps: MapViewCreateOutcomeOwnProps): MapViewCreateOutcomeConnectorDispatchProps { const { projectId: cellIdString } = ownProps const cellId = cellIdFromString(cellIdString) return { @@ -105,4 +106,4 @@ function mapDispatchToProps(dispatch, ownProps: MapViewOutcomeTitleFormOwnProps) export default connect( mapStateToProps, mapDispatchToProps -)(MapViewOutcomeTitleForm) \ No newline at end of file +)(MapViewCreateOutcome) \ No newline at end of file diff --git a/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.scss b/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.scss new file mode 100644 index 00000000..c2a779e5 --- /dev/null +++ b/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.scss @@ -0,0 +1,45 @@ +.map-view-outcome-statement-card { + position: absolute; + display: flex; + flex-direction: column; + justify-content: left; + background-color: var(--bg-color-secondary); + border-radius: 1rem; + box-shadow: 0 0 36px var(--color-brown-shadow); + padding: 3rem 2.75rem 2.5rem 2.75rem; + width: 24rem; + box-sizing: border-box; + + .map-view-outcome-statement-form { + margin-bottom: 2.5rem; + // margin: 4rem 4rem 1.5rem 4rem; + + textarea { + background-color: transparent; + border: 0; + outline: none; + padding: 0; + width: 18rem; + height: 4.3rem; + resize: none; + color: var(--text-color-primary); + font-family: var(--font-family-primary-semibold); + font-size: 18px; + + &::selection { + // TODO: decide on this + background-color: var(--bg-color-light-green); + } + } + } + + .map-view-outcome-statement-checkbox { + width: 100%; + + + .button-checkbox-wrapper { + width: fit-content; + font-family: var(--font-family-primary-medium); + } + } +} diff --git a/web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.scss b/web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.scss deleted file mode 100644 index 1bb2be98..00000000 --- a/web/src/components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.scss +++ /dev/null @@ -1,26 +0,0 @@ -.map-view-outcome-title-form-wrapper { - position: absolute; - display: flex; -} - -.map-view-outcome-title-form-form { - width: 25rem; -} - -.map-view-outcome-title-form-form textarea { - background-color: transparent; - border: 0; - outline: none; - padding: 0; - margin: 4.65rem 4rem 1.5rem 4rem; - width: 24.5rem; - height: 4.3rem; - resize: none; - color: var(--text-color-primary); - font-family: var(--font-family-primary-bold); -} - -.map-view-outcome-title-form-form ::selection { - // TODO: decide on this - // background: var(--bg-color-achieved); -} diff --git a/web/src/drawing/drawOutcome/computeArguments/argsForDrawGlow.ts b/web/src/drawing/drawOutcome/computeArguments/argsForDrawGlow.ts index 22eaa62d..dda56932 100644 --- a/web/src/drawing/drawOutcome/computeArguments/argsForDrawGlow.ts +++ b/web/src/drawing/drawOutcome/computeArguments/argsForDrawGlow.ts @@ -1,4 +1,4 @@ -import { TOP_PRIORITY_GLOW_COLOR } from '../../../styles' +import { HIGH_PRIORITY_GLOW_COLOR } from '../../../styles' import { borderWidth, cornerRadius } from '../../dimensions' import drawGlow from '../drawGlow' @@ -36,7 +36,7 @@ export const argsForDrawGlow = ({ height, cornerRadius: glowCornerRadius, useGlow, - glowColor: TOP_PRIORITY_GLOW_COLOR, + glowColor: HIGH_PRIORITY_GLOW_COLOR, glowBlur, ctx, } diff --git a/web/src/drawing/index.ts b/web/src/drawing/index.ts index 4ac9e0a3..0de57d92 100644 --- a/web/src/drawing/index.ts +++ b/web/src/drawing/index.ts @@ -452,7 +452,7 @@ function render( DRAW NEW OUTCOME PLACEHOLDER */ // creating a new Outcome - if (outcomeFormIsOpen) { + if (false && outcomeFormIsOpen) { const isHovered = false const isSelected = false const isEditing = true @@ -461,7 +461,7 @@ function render( drawOutcomeCard({ // draw the Outcome with empty text // since the text is presented in the - // MapViewOutcomeTitleForm + // MapViewCreateOutcome skipStatementRender: true, useLineLimit: false, zoomLevel: zoomLevel, diff --git a/web/src/routes/ProjectView/MapView/MapView.component.tsx b/web/src/routes/ProjectView/MapView/MapView.component.tsx index 90790885..fc99934e 100644 --- a/web/src/routes/ProjectView/MapView/MapView.component.tsx +++ b/web/src/routes/ProjectView/MapView/MapView.component.tsx @@ -13,7 +13,7 @@ import ProjectEmptyState from '../../../components/ProjectEmptyState/ProjectEmpt import MultiEditBar from '../../../components/MultiEditBar/MultiEditBar.connector' import OutcomeConnectors from '../../../components/OutcomeConnectors/OutcomeConnectors.connector' import CollapsedChildrenPills from '../../../components/CollapsedChildrenPills/CollapsedChildrenPills.connector' -import MapViewOutcomeTitleForm from '../../../components/MapViewOutcomeTitleForm/MapViewOutcomeTitleForm.connector' +import MapViewCreateOutcome from '../../../components/MapViewCreateOutcome/MapViewCreateOutcome.connector' import Tooltip from '../../../components/Tooltip/Tooltip' import './MapView.scss' @@ -183,9 +183,10 @@ const MapView: React.FC = ({ {/* if the scale is greater than or equal to 60% (or we are creating an Outcome) */} {/* because otherwise the font size gets to small and the text is cut off */} - {outcomeFormIsOpen && }
+ + {outcomeFormIsOpen && } {/* below items inside 'mapview-elements-container' maintain their normal scale */} {/* while positioning themselves absolutely (position: absolute) on the screen */} diff --git a/web/src/styles.ts b/web/src/styles.ts index 564b0b80..3de1db0e 100644 --- a/web/src/styles.ts +++ b/web/src/styles.ts @@ -45,7 +45,7 @@ const colors = { // map view colors const SELECTED_COLOR = '#344cff' -const TOP_PRIORITY_GLOW_COLOR = '#334CF8' +const HIGH_PRIORITY_GLOW_COLOR = '#334CF8' // canvas outcome statement const STATEMENT_FONT_COLOR = '#222222' @@ -99,7 +99,7 @@ export { STATEMENT_FONT_COLOR, STATEMENT_PLACEHOLDER_COLOR, SELECTED_COLOR, - TOP_PRIORITY_GLOW_COLOR, + HIGH_PRIORITY_GLOW_COLOR, DEFAULT_OUTCOME_BACKGROUND_COLOR, NOT_ACHIEVED_BACKGROUND_COLOR, ACHIEVED_BACKGROUND_COLOR, diff --git a/web/src/variables.scss b/web/src/variables.scss index 31cd500f..fc54af7d 100644 --- a/web/src/variables.scss +++ b/web/src/variables.scss @@ -6,7 +6,7 @@ // color --color-virtually-transaprent: #ffffff00; // when you need an element but transparent --color-green-shadow: #4a604392; // for achieved outcome cards - --shadow-brown-shadow: #8377699b; // for not achieved outcome cards + --color-brown-shadow: #8377699b; // for not achieved outcome cards --color-silver: #757171; //Silver --color-green: #008414; //Green --color-forest-green: #15841d; //Forset green (used for achieved small) @@ -59,6 +59,7 @@ --border-color-bright-gray: #efefef; //Bright Gray --border-color-pale-silver: #c3bfba; //Pale Silver (100%) --border-color-form-input: #f3efeb; + --border-color-button-checkbox: #ffffff00; // shadow --shadow-color: #c7beb472; //Pale Silver (40% opacity) From c54572bb573a8f2a47776088eefe7eeb9ef6306f Mon Sep 17 00:00:00 2001 From: Pegah Date: Mon, 30 Oct 2023 19:22:10 -0700 Subject: [PATCH 15/18] some dashboard component cleanups --- .../routes/Dashboard/Dashboard.component.tsx | 24 +- web/src/routes/Dashboard/Dashboard.scss | 293 ++++++++---------- 2 files changed, 129 insertions(+), 188 deletions(-) diff --git a/web/src/routes/Dashboard/Dashboard.component.tsx b/web/src/routes/Dashboard/Dashboard.component.tsx index db360355..f5fdc65c 100644 --- a/web/src/routes/Dashboard/Dashboard.component.tsx +++ b/web/src/routes/Dashboard/Dashboard.component.tsx @@ -82,7 +82,7 @@ const Dashboard: React.FC = ({ const [showJoinModal, setShowJoinModal] = useState(false) const [showImportModal, setShowImportModal] = useState(false) const [showProjectMigratedModal, setShowProjectMigratedModal] = useState('') - // + // // calling this triggers the fetchProjectMeta for each project const { projectStatusInfos, setProjectStatusInfos } = useProjectStatusInfos( @@ -141,20 +141,7 @@ const Dashboard: React.FC = ({ return ( <>
-
- {/* TODO: decide if we want to have this menu on Dashboard page */} - {/* - - My Projects - - - - Settings - */} -
+
My Projects{' '} @@ -192,7 +179,7 @@ const Dashboard: React.FC = ({
= ({ }} onClickUpdateNow={() => { setShowProjectMigratedModal('') - setModalState({ id: OpenModal.UpdateApp, section: ViewingReleaseNotes.MainMessage }) + setModalState({ + id: OpenModal.UpdateApp, + section: ViewingReleaseNotes.MainMessage, + }) }} /> diff --git a/web/src/routes/Dashboard/Dashboard.scss b/web/src/routes/Dashboard/Dashboard.scss index b622be1c..7af2f68c 100644 --- a/web/src/routes/Dashboard/Dashboard.scss +++ b/web/src/routes/Dashboard/Dashboard.scss @@ -8,40 +8,6 @@ overflow: hidden; } -.dashboard-left-menu-item { - font-size: 14px; - font-family: 'gilroyextrabold', Helvetica, sans-serif; - color: #535353; - margin-bottom: 10px; - padding: 8px; - display: flex; - flex-direction: row; - cursor: pointer; - transition: color 0.1s ease-in, background-color 0.1s ease-in; - width: 110px; - border-radius: 10px; - text-decoration: none; -} - -.dashboard-left-menu-item .icon { - margin-right: 6px; -} - -.dashboard-left-menu-item:hover, -.dashboard-left-menu-item.active { - color: #5f65ff; - background-color: #f3f2f2; -} - -.dashboard-left-menu-item:hover .inner-icon, -.dashboard-left-menu-item.active .inner-icon { - background-color: #5f65ff; -} - -.dashboard-left-menu-item .inner-icon { - transition: background-color 0.1s ease-in; -} - /* dashboard my projects */ .dashboard-my-projects { @@ -50,138 +16,139 @@ max-width: 60rem; display: flex; flex-direction: column; -} -@media only screen and (max-width: 1024px) { - .dashboard-my-projects { - margin-right: 2rem; - margin-left: 2rem; - max-width: 100%; + .my-projects-heading { + margin-bottom: 1.25rem; + padding: 0 1rem; + box-sizing: border-box; } -} - -.my-projects-heading { - margin-bottom: 1.25rem; - padding: 0 1rem; - box-sizing: border-box; -} - -.my-projects-header { - border-bottom: 0.125rem solid var(--border-color-timberwolf); - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - padding-bottom: 6px; - margin: 0 1rem; -} - -.my-projects-header-buttons { - display: flex; - flex-direction: row; -} - -.my-projects-button { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - border-radius: 30px; - font-size: 14px; - padding: 10px 0; - text-align: center; - cursor: pointer; - margin-right: 40px; - font-family: 'PlusJakartaSans-bold'; - color: var(--text-color-primary); - transition: 0.2s all ease; - - &:hover { - color: var(--text-color-link); - } - - &.invite-members { - width: 140px; - // position: absolute; - // top: -2px; - // right: 0; - padding: 6px 16px 6px 8px; - background-color: #f9f8f6; - border: none; - margin-right: 0; - - &:hover { - background-color: #f5f2ef; + .my-projects-header { + border-bottom: 0.125rem solid var(--border-color-timberwolf); + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding-bottom: 0.25rem; + margin: 0 1rem; + + .my-projects-header-buttons { + display: flex; + flex-direction: row; + + .my-projects-button { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + font-size: 14px; + padding: 0.625rem 0; + text-align: center; + cursor: pointer; + margin-right: 2.5rem; + font-family: var(--font-family-primary-semibold); + color: var(--text-color-primary); + transition: 0.2s all ease; + + &:hover { + color: var(--text-color-link); + } + + &.invite-members { + width: 140px; + padding: 6px 16px 6px 8px; + background-color: #f9f8f6; + border: none; + margin-right: 0; + + &:hover { + background-color: #f5f2ef; + } + } + + .icon { + margin-right: 6px; + } + } } - } - - .icon { - margin-right: 6px; - } -} -.my-projects-sorting { - color: #8e8e8e; - font-size: 0.825rem; - display: flex; - flex-direction: row; - align-items: center; - position: relative; - padding: 0.5rem 0; -} - -.my-projects-sorting-selected { - font-size: 0.825rem; - display: flex; - cursor: pointer; - color: #5d5d5d; - margin-left: 8px; - margin-top: 1px; - - .icon { - margin-left: 6px; - transition: transform 0.2s ease-in; - padding: 0px; - margin-top: 2px; - - &.active { - transform: rotateX(180deg); - -webkit-transform: rotate(180deg); + .my-projects-sorting { + color: var(--text-color-quaternary); + font-size: 0.825rem; + display: flex; + flex-direction: row; + align-items: center; + position: relative; + + .my-projects-sorting-selected { + font-size: 0.825rem; + display: flex; + cursor: pointer; + color: var(--text-color-secondary); + margin-left: 0.5rem; + + .icon { + margin-left: 0.375rem; + transition: transform 0.2s ease; + top: 0.075rem; + + &.active { + transform: rotateX(180deg); + -webkit-transform: rotate(180deg); + } + } + + &:hover { + color: #5f65ff; + + .icon .inner-icon { + background-color: #5f65ff; + } + } + } + + .my-projects-sorting-select { + width: 7.5rem; + position: absolute; + right: 0px; + top: 1.5rem; + background: var(--bg-color-secondary); + padding: 8px 0; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.08); + border-radius: 8px; + z-index: 2; + list-style: none; + + li { + text-align: left; + padding: 0.5rem 1rem; + color: var(--text-color-secondary); + + &:hover { + background-color: var(--bg-color-hover); + cursor: pointer; + } + } + } } } - &:hover { - color: #5f65ff; - - .icon .inner-icon { - background-color: #5f65ff; + .my-projects-content { + position: relative; + overflow-y: auto; + flex: 1; + scrollbar-width: none; + padding: 0 1rem 3rem; + box-sizing: border-box; + width: 100%; + + &::-webkit-scrollbar { + width: 0px; /* For vertical scrollbars */ + height: 0px; /* For horizontal scrollbars */ } } } -.my-projects-sorting-select { - position: absolute; - right: 0px; - top: 18px; - background: #ffffff; - padding: 8px 0; - box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.08); - border-radius: 8px; - z-index: 2; - list-style: none; -} - -.my-projects-sorting-select li { - padding: 8px 20px; - color: #535353; - - &:hover { - background-color: #f2f2f2; - cursor: pointer; - } -} - -// for sorting +// sorting popup anitmation .my-projects-sorting-select-enter { opacity: 0; } @@ -199,19 +166,3 @@ opacity: 0; transition: opacity 100ms ease-out; } - -/* dont show a physical scroll bar */ - -.my-projects-content::-webkit-scrollbar { - // display: none; -} - -.my-projects-content { - position: relative; - overflow-y: scroll; - flex: 1; - scrollbar-width: none; - padding: 0 1rem; - box-sizing: border-box; - width: 100%; -} From 362321f14802881dd894e83569dfc7ea71a2b537 Mon Sep 17 00:00:00 2001 From: Pegah Date: Tue, 31 Oct 2023 15:14:12 -0700 Subject: [PATCH 16/18] save changes on expanded view when closing with esc key --- .../EvDetails/EvDetails.component.tsx | 42 ++++++------- .../EvDetails/EvDetails.connector.tsx | 9 --- .../ExpandedViewMode.component.tsx | 7 ++- .../ExpandedViewMode.connector.tsx | 62 ++++++++++++++++++- web/src/event-listeners/index.ts | 1 - .../ProjectView/ProjectView.component.tsx | 16 ----- 6 files changed, 86 insertions(+), 51 deletions(-) diff --git a/web/src/components/ExpandedViewMode/EVMiddleColumn/TabContent/EvDetails/EvDetails.component.tsx b/web/src/components/ExpandedViewMode/EVMiddleColumn/TabContent/EvDetails/EvDetails.component.tsx index e0c34b8d..40ca13a1 100644 --- a/web/src/components/ExpandedViewMode/EVMiddleColumn/TabContent/EvDetails/EvDetails.component.tsx +++ b/web/src/components/ExpandedViewMode/EVMiddleColumn/TabContent/EvDetails/EvDetails.component.tsx @@ -34,6 +34,15 @@ import Icon from '../../../../Icon/Icon' export type EvDetailsOwnProps = { projectId: CellIdString outcome: ComputedOutcome + content: string + githubInputLinkText: string + description: string + setContent: React.Dispatch> + setGithubInputLinkText: React.Dispatch> + setDescription: React.Dispatch> + updateOutcome: (outcome: Outcome, actionHash: ActionHashB64) => Promise + updateOutcomeWithLatest: () => Promise + cleanOutcome: () => Outcome } export type EvDetailsConnectorStateProps = { @@ -64,7 +73,6 @@ export type EvDetailsConnectorDispatchProps = { text: string, backgroundColor: string ) => Promise - updateOutcome: (outcome: Outcome, actionHash: ActionHashB64) => Promise createOutcomeMember: ( outcomeActionHash: ActionHashB64, memberAgentPubKey: AgentPubKeyB64, @@ -85,6 +93,14 @@ const EvDetails: React.FC = ({ // own props projectId, outcome, + content, + githubInputLinkText, + description, + setContent, + setGithubInputLinkText, + setDescription, + updateOutcomeWithLatest, + cleanOutcome, // state props activeAgentPubKey, projectTags, @@ -113,31 +129,17 @@ const EvDetails: React.FC = ({ } }, [outcomeActionHash]) - const cleanOutcome = (): Outcome => { - return { - ...outcome, - editorAgentPubKey: activeAgentPubKey, - timestampUpdated: moment().unix(), - content, - description, - githubLink: githubInputLinkText, - } - } - const updateOutcomeWithLatest = async () => { - await updateOutcome(cleanOutcome(), outcomeActionHash) - } - /* Title */ - // the live editor state - const [content, setContent] = useState('') + // handle change (or update) of outcome const outcomeContent = outcome ? outcome.content : '' useEffect(() => { setContent(outcomeContent) }, [outcomeContent]) const onTitleBlur = () => { + console.log('onTitleBlur triggered') updateOutcomeWithLatest() endTitleEdit(outcomeActionHash) } @@ -159,8 +161,7 @@ const EvDetails: React.FC = ({ /* Github Link */ - // the live github link editor state - const [githubInputLinkText, setGithubInputLinkText] = useState('') + const [isEditingGithubLink, setIsEditingGithubLink] = useState(false) const outcomeGithubLink = outcome ? outcome.githubLink : '' useEffect(() => { @@ -256,8 +257,7 @@ const EvDetails: React.FC = ({ /* Description */ - // the live editor state - const [description, setDescription] = useState('') + // the latest persisted state const outcomeDescription = outcome ? outcome.description : '' // sync the live editor state with the diff --git a/web/src/components/ExpandedViewMode/EVMiddleColumn/TabContent/EvDetails/EvDetails.connector.tsx b/web/src/components/ExpandedViewMode/EVMiddleColumn/TabContent/EvDetails/EvDetails.connector.tsx index 79137e8f..05cf7974 100644 --- a/web/src/components/ExpandedViewMode/EVMiddleColumn/TabContent/EvDetails/EvDetails.connector.tsx +++ b/web/src/components/ExpandedViewMode/EVMiddleColumn/TabContent/EvDetails/EvDetails.connector.tsx @@ -145,15 +145,6 @@ function mapDispatchToProps( }) return dispatch(updateTag(cellIdString, updatedExistingTag)) }, - updateOutcome: async (outcome: Outcome, actionHash: ActionHashB64) => { - const appWebsocket = await getAppWs() - const projectsZomeApi = new ProjectsZomeApi(appWebsocket) - const updatedOutcome = await projectsZomeApi.outcome.update(cellId, { - actionHash, - entry: outcome, - }) - return dispatch(updateOutcome(cellIdString, updatedOutcome)) - }, createOutcomeMember: async ( outcomeActionHash: ActionHashB64, memberAgentPubKey: AgentPubKeyB64, diff --git a/web/src/components/ExpandedViewMode/ExpandedViewMode.component.tsx b/web/src/components/ExpandedViewMode/ExpandedViewMode.component.tsx index 108527fe..aa8e5596 100644 --- a/web/src/components/ExpandedViewMode/ExpandedViewMode.component.tsx +++ b/web/src/components/ExpandedViewMode/ExpandedViewMode.component.tsx @@ -3,13 +3,14 @@ import { CSSTransition } from 'react-transition-group' import EVMiddleColumn from './EVMiddleColumn/EVMiddleColumn' import EVLeftColumn from './EVLeftColumn/EVLeftColumn' import { ExpandedViewTab } from './NavEnum' -import { CellIdString, ActionHashB64 } from '../../types/shared' -import { ComputedOutcome, ComputedScope } from '../../types' +import { CellIdString, ActionHashB64, AgentPubKeyB64 } from '../../types/shared' +import { ComputedOutcome, ComputedScope, Outcome } from '../../types' import './ExpandedViewMode.scss' import ButtonClose from '../ButtonClose/ButtonClose' import Breadcrumbs from '../Breadcrumbs/Breadcrumbs' import hashCodeId from '../../api/clientSideIdHash' import OnClickOutside from '../OnClickOutside/OnClickOutside' +import moment from 'moment' // props passed to the component by the parent export type ExpandedViewModeOwnProps = { @@ -23,12 +24,14 @@ export type ExpandedViewModeOwnProps = { childrenList: React.ReactElement taskList: React.ReactElement rightColumn: React.ReactElement + updateOutcome: (outcome: Outcome, actionHash: ActionHashB64) => Promise } // redux props export type ExpandedViewModeConnectorProps = { outcomeActionHash: ActionHashB64 commentCount: number + activeAgentPubKey: AgentPubKeyB64 } export type ExpandedViewModeProps = ExpandedViewModeOwnProps & diff --git a/web/src/components/ExpandedViewMode/ExpandedViewMode.connector.tsx b/web/src/components/ExpandedViewMode/ExpandedViewMode.connector.tsx index a19568fe..95eee700 100644 --- a/web/src/components/ExpandedViewMode/ExpandedViewMode.connector.tsx +++ b/web/src/components/ExpandedViewMode/ExpandedViewMode.connector.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' import { connect } from 'react-redux' import { RootState } from '../../redux/reducer' import ConnectedEVRightColumn from './EVRightColumn/EVRightColumn.connector' @@ -42,6 +42,7 @@ function mapStateToProps( return { outcomeActionHash, commentCount: comments.length, + activeAgentPubKey: state.agentAddress, } } @@ -75,6 +76,46 @@ const ConnectedExpandedViewMode: React.FC = ({ updateOutcome, createOutcomeWithConnection, }) => { + // the live editor state + const [content, setContent] = useState('') + // the live github link editor state + const [githubInputLinkText, setGithubInputLinkText] = useState('') + // the live editor state + const [description, setDescription] = useState('') + + // close Expanded view after hitting Esc key: + useEffect(() => { + const onKeyDown = async (event) => { + // if we are on Map View + // we should let Map View handle the Escape key + if (event.key === 'Escape') { + const updateTo = localCleanOutcome() + await updateOutcome(updateTo, outcome.actionHash) + + onClose() + } + } + document.body.addEventListener('keyup', onKeyDown) + // for teardown, unbind event listeners + return () => { + document.body.removeEventListener('keyup', onKeyDown) + } + }, [outcome, content, description, githubInputLinkText, activeAgentPubKey]) + + const localCleanOutcome = (): Outcome => { + return { + ...outcome, + editorAgentPubKey: activeAgentPubKey, + timestampUpdated: moment().unix(), + content, + description, + githubLink: githubInputLinkText, + } + } + const updateOutcomeWithLatest = async () => { + await updateOutcome(localCleanOutcome(), outcome.actionHash) + } + const updateOutcomeTaskList = (taskList: Array) => { const cleanedOutcome = cleanOutcome(outcome) return updateOutcome( @@ -163,7 +204,23 @@ const ConnectedExpandedViewMode: React.FC = ({ } // redux connected expanded view components - const details = + const details = ( + + ) const comments = ( = ({ onClose={onClose} outcome={outcome} outcomeAndAncestors={outcomeAndAncestors} + updateOutcome={updateOutcome} /> ) } diff --git a/web/src/event-listeners/index.ts b/web/src/event-listeners/index.ts index b4593b9e..2b1409aa 100644 --- a/web/src/event-listeners/index.ts +++ b/web/src/event-listeners/index.ts @@ -307,7 +307,6 @@ export default function setupEventListeners( if (!state.ui.expandedView.isOpen && !state.ui.navigationModal.open) { store.dispatch(unselectAll()) } - store.dispatch(closeExpandedView()) store.dispatch(closeOutcomeForm()) store.dispatch(resetOutcomeConnector()) break diff --git a/web/src/routes/ProjectView/ProjectView.component.tsx b/web/src/routes/ProjectView/ProjectView.component.tsx index 8aed0d96..4b36a0ab 100644 --- a/web/src/routes/ProjectView/ProjectView.component.tsx +++ b/web/src/routes/ProjectView/ProjectView.component.tsx @@ -171,22 +171,6 @@ const ProjectViewInner: React.FC = ({ setActiveEntryPoints(entryPointActionHashes) }, [JSON.stringify(entryPointActionHashes)]) - // close Expanded view after hitting Esc key: - useEffect(() => { - const onKeyDown = (event) => { - // if we are on Map View - // we should let Map View handle the Escape key - if (!location.pathname.includes('map') && event.key === 'Escape') { - closeExpandedView() - } - } - document.body.addEventListener('keydown', onKeyDown) - // for teardown, unbind event listeners - return () => { - document.body.removeEventListener('keydown', onKeyDown) - } - }, [location]) - return ( <> From 83e5d16dcc3e807129e06f4e65a103cc87c9f69e Mon Sep 17 00:00:00 2001 From: Connor Turland <1409121+Connoropolous@users.noreply.github.com> Date: Tue, 31 Oct 2023 18:07:59 -0700 Subject: [PATCH 17/18] pipe logging in and parse and categorize it --- electron/package.json | 2 +- electron/src/holochain.ts | 5 +- electron/src/index.ts | 36 ++++++---- .../components/NetworkInfo/NetworkInfo.tsx | 70 +++++++++++++++++-- web/src/hooks/useHolochainErrorAndLog.ts | 55 +++++++++++++++ web/src/routes/App.component.tsx | 4 +- yarn.lock | 8 +-- 7 files changed, 154 insertions(+), 26 deletions(-) create mode 100644 web/src/hooks/useHolochainErrorAndLog.ts diff --git a/electron/package.json b/electron/package.json index 8bc4b53a..5e126a2b 100644 --- a/electron/package.json +++ b/electron/package.json @@ -53,7 +53,7 @@ "eslint-plugin-import": "^2.22.1" }, "dependencies": { - "@lightningrodlabs/electron-holochain": "=0.7.8", + "@lightningrodlabs/electron-holochain": "=0.7.12", "electron-context-menu": "^3.5.0", "electron-default-menu": "^1.0.2", "electron-log": "^4.3.5", diff --git a/electron/src/holochain.ts b/electron/src/holochain.ts index 73cffc22..fba31ccb 100644 --- a/electron/src/holochain.ts +++ b/electron/src/holochain.ts @@ -1,9 +1,6 @@ -import * as path from 'path' -import { app } from 'electron' import { ElectronHolochainOptions, StateSignal, - PathOptions, } from '@lightningrodlabs/electron-holochain' import { DATASTORE_PATH, KEYSTORE_PATH, PROFILES_HAPP_PATH } from './paths' @@ -53,6 +50,7 @@ const devOptions: ElectronHolochainOptions = { keystorePath: KEYSTORE_PATH, passphrase: 'test-passphrase', bootstrapUrl: 'https://bootstrap.holo.host', + logging: 'Json', } const prodOptions: ElectronHolochainOptions = { happPath: PROFILES_HAPP_PATH, // preload @@ -63,6 +61,7 @@ const prodOptions: ElectronHolochainOptions = { keystorePath: KEYSTORE_PATH, passphrase: 'test-passphrase', bootstrapUrl: 'https://bootstrap.holo.host', + logging: 'Json', } export { devOptions, prodOptions } diff --git a/electron/src/index.ts b/electron/src/index.ts index 43bb52d3..1dcd2604 100644 --- a/electron/src/index.ts +++ b/electron/src/index.ts @@ -1,17 +1,13 @@ -import { - app, - BrowserWindow, - ipcMain, - shell, - autoUpdater, - Menu, -} from 'electron' +import { app, BrowserWindow, ipcMain, shell, autoUpdater, Menu } from 'electron' import * as contextMenu from 'electron-context-menu' import * as path from 'path' import * as fs from 'fs' import initAgent, { + ERROR_EVENT, StateSignal, STATUS_EVENT, + HOLOCHAIN_LOG_EVENT, + WASM_LOG_EVENT, } from '@lightningrodlabs/electron-holochain' import { devOptions, prodOptions, stateSignalToText } from './holochain' @@ -45,8 +41,6 @@ contextMenu.default({ showSaveImageAs: true, }) - - // Handle creating/removing shortcuts on Windows when installing/uninstalling. // if (require('electron-squirrel-startup')) { // eslint-disable-line global-require @@ -177,19 +171,37 @@ app.on('ready', async () => { const splashWindow = createSplashWindow() const opts = app.isPackaged ? prodOptions : devOptions const { statusEmitter, shutdown } = await initAgent(app, opts, BINARY_PATHS) + let mainWindow: BrowserWindow | null = null statusEmitter.on(STATUS_EVENT, (state: StateSignal) => { switch (state) { case StateSignal.IsReady: // important that this line comes before the next one // otherwise this triggers the 'all-windows-closed' // event - createMainWindow() + mainWindow = createMainWindow() splashWindow.close() break default: splashWindow.webContents.send('status', stateSignalToText(state)) } }) + statusEmitter.on(ERROR_EVENT, (error: Error) => { + if (!error.message.includes('no project meta exists')) { + if (mainWindow) { + mainWindow.webContents.send('holochainError', error.message) + } + } + }) + statusEmitter.on(HOLOCHAIN_LOG_EVENT, (log: string) => { + if (mainWindow) { + mainWindow.webContents.send('holochainLog', log) + } + }) + statusEmitter.on(WASM_LOG_EVENT, (log: string) => { + if (mainWindow) { + mainWindow.webContents.send('wasmLog', log) + } + }) }) // Quit when all windows are closed, except on macOS. There, it's common @@ -285,7 +297,7 @@ ipcMain.handle('checkForMigrationData', (event) => { ) return { file: existingMigrationPath, - data: prevVersionMigrationDataString + data: prevVersionMigrationDataString, } } else { return null diff --git a/web/src/components/NetworkInfo/NetworkInfo.tsx b/web/src/components/NetworkInfo/NetworkInfo.tsx index 58a9cb44..c0e578b0 100644 --- a/web/src/components/NetworkInfo/NetworkInfo.tsx +++ b/web/src/components/NetworkInfo/NetworkInfo.tsx @@ -6,14 +6,33 @@ import { VersionInfo } from '../../hooks/useVersionChecker' export type NetworkInfoProps = { versionInfo: VersionInfo + wasmLogs: string[] + holochainLogs: { + info: object[] + warn: object[] + error: object[] + debug: object[] + unknown: string[] + } + errors: string[] } -const NetworkInfo: React.FC = ( - { - versionInfo - } -) => { +enum LogsToShow { + Info = 'Info', + Warn = 'Warn', + Debug = 'Debug', + Error = 'Error', + Unknown = 'Unknown', +} + +const NetworkInfo: React.FC = ({ + versionInfo, + holochainLogs, + wasmLogs, + errors, +}) => { const [networkStats, setNetworkStats] = useState({}) + const [logsToShow, setLogsToShow] = useState(LogsToShow.Info) useEffect(() => { const getNetworkStats = async () => { @@ -28,6 +47,9 @@ const NetworkInfo: React.FC = ( return () => clearInterval(interval) }, []) + const logs = + holochainLogs[logsToShow.toLowerCase() as keyof typeof holochainLogs] + return (
@@ -46,6 +68,44 @@ const NetworkInfo: React.FC = ( updates every 5 seconds
+ +

Errors

+ {errors.map((error) => ( +

{error}

+ ))} + +

Wasm Logs

+ + {wasmLogs.map((log) => ( +

{log}

+ ))} +

Holochain Logs

+ + + + + +

{logsToShow} Logs

+ {logs.map((log: string | object, index: number) => { + return ( +

+ {typeof log === 'object' + ? log['fields']['message'] || + log['fields']['msg'] || + log['fields']['err'] + : log} + {'\n'}({typeof log === 'object' ? log['module_path'] : ''}) +

+ ) + })}
) } diff --git a/web/src/hooks/useHolochainErrorAndLog.ts b/web/src/hooks/useHolochainErrorAndLog.ts new file mode 100644 index 00000000..65766104 --- /dev/null +++ b/web/src/hooks/useHolochainErrorAndLog.ts @@ -0,0 +1,55 @@ +import { useEffect, useState } from 'react' + +export default function useHolochainErrorAndLog() { + const [wasmLogs, setWasmLogs] = useState([]) + const [holochainInfo, setHolochainInfo] = useState([]) + const [holochainWarn, setHolochainWarn] = useState([]) + const [holochainDebug, setHolochainDebug] = useState([]) + const [holochainError, setHolochainError] = useState([]) + const [holochainUnknown, setHolochainUnknown] = useState([]) + const [errors, setErrors] = useState([]) + + const subscribeToEvents = async () => { + if (window.require) { + const { ipcRenderer } = window.require('electron') + ipcRenderer.on('wasmLog', (event: object, log: string) => { + setWasmLogs((logs) => [...logs, log]) + }) + ipcRenderer.on('holochainLog', (event: object, log: string) => { + try { + let parsedLog: object = JSON.parse(log) + if (parsedLog['level'] === 'INFO') { + setHolochainInfo((logs) => [...logs, parsedLog]) + } else if (parsedLog['level'] === 'WARN') { + setHolochainWarn((logs) => [...logs, parsedLog]) + } else if (parsedLog['level'] === 'DEBUG') { + setHolochainDebug((logs) => [...logs, parsedLog]) + } else if (parsedLog['level'] === 'ERROR') { + setHolochainError((logs) => [...logs, parsedLog]) + } + } catch (e) { + setHolochainUnknown((logs) => [...logs, log]) + } + }) + ipcRenderer.on('holochainError', (event: object, error: string) => { + setErrors((errors) => [...errors, error]) + }) + } + } + + useEffect(() => { + subscribeToEvents() + }, []) + + return { + wasmLogs, + holochainLogs: { + info: holochainInfo, + warn: holochainWarn, + debug: holochainDebug, + error: holochainError, + unknown: holochainUnknown, + }, + errors, + } +} diff --git a/web/src/routes/App.component.tsx b/web/src/routes/App.component.tsx index e6f539da..22331ab9 100644 --- a/web/src/routes/App.component.tsx +++ b/web/src/routes/App.component.tsx @@ -56,6 +56,7 @@ import { } from '../redux/ephemeral/local-preferences/reducer.js' import { ProjectMetaState } from '../redux/persistent/projects/project-meta/reducer' import { MembersState } from '../redux/persistent/projects/members/reducer' +import useHolochainErrorAndLog from '../hooks/useHolochainErrorAndLog' export type AppStateProps = { projectMetas: ProjectMetaState @@ -154,6 +155,7 @@ const App: React.FC = ({ // const updateVersionInfo = useVersionChecker(true) const finishMigrationChecker = useFinishMigrationChecker() const { fileDownloaded, setFileDownloaded } = useFileDownloaded() + const { wasmLogs, holochainLogs, errors: holochainErrors } = useHolochainErrorAndLog() useEffect(() => { if (fileDownloaded) { @@ -337,7 +339,7 @@ const App: React.FC = ({
- {networkInfoOpen && } + {networkInfoOpen && } ) } diff --git a/yarn.lock b/yarn.lock index 1b87d8f0..86c451ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1670,10 +1670,10 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@lightningrodlabs/electron-holochain@=0.7.8": - version "0.7.8" - resolved "https://registry.yarnpkg.com/@lightningrodlabs/electron-holochain/-/electron-holochain-0.7.8.tgz#7ef0ade47a720d96180ce847de9269e4f55c5b08" - integrity sha512-rJ3ashN7Qz6NLz/J19+O7rLDUB++4aAkBlSNI5xe9++3pLRs0WGxR8YWwiSO3FX4VsiPeTx+sZn9yLBkfiaXgQ== +"@lightningrodlabs/electron-holochain@=0.7.12": + version "0.7.12" + resolved "https://registry.yarnpkg.com/@lightningrodlabs/electron-holochain/-/electron-holochain-0.7.12.tgz#010bf6837b738b9245800ab61220ca34da93b2a7" + integrity sha512-p1TKr5FYYfFlNgeeT5tCCjDPOJ9hHqUPySj7iv50bOyIe1Ch+Rq5DniJEpV9NYCcPDGfZ1eoua2EUeHML2r/4g== dependencies: request "^2.88.2" split "^1.0.1" From e546713b4235d8a66406b13426834669069a415c Mon Sep 17 00:00:00 2001 From: Connor Turland <1409121+Connoropolous@users.noreply.github.com> Date: Wed, 8 Nov 2023 20:41:16 -0800 Subject: [PATCH 18/18] hook up keyboard listeners for MapViewCreateOutcome --- .../ButtonCheckbox/ButtonCheckbox.scss | 2 +- .../ButtonCheckbox/ButtonCheckbox.tsx | 18 ++++- .../ExpandedViewMode.component.tsx | 4 +- .../ExpandedViewMode.connector.tsx | 8 +- .../MapViewCreateOutcome.component.tsx | 73 +++++++++---------- web/src/hooks/useVersionChecker.ts | 4 +- web/tsconfig.json | 1 + 7 files changed, 61 insertions(+), 49 deletions(-) diff --git a/web/src/components/ButtonCheckbox/ButtonCheckbox.scss b/web/src/components/ButtonCheckbox/ButtonCheckbox.scss index 80ea00cf..63ee2f48 100644 --- a/web/src/components/ButtonCheckbox/ButtonCheckbox.scss +++ b/web/src/components/ButtonCheckbox/ButtonCheckbox.scss @@ -17,7 +17,7 @@ border: 0.15rem solid var(--border-color-button-checkbox); &:hover, - &.focused { + &:focus { box-shadow: 0rem 0rem 1.25rem var(--shadow-color-hover); } diff --git a/web/src/components/ButtonCheckbox/ButtonCheckbox.tsx b/web/src/components/ButtonCheckbox/ButtonCheckbox.tsx index 69054016..858f98ce 100644 --- a/web/src/components/ButtonCheckbox/ButtonCheckbox.tsx +++ b/web/src/components/ButtonCheckbox/ButtonCheckbox.tsx @@ -1,6 +1,5 @@ -import React from 'react' +import React, { useEffect } from 'react' import Checkbox from '../Checkbox/Checkbox' -import Icon from '../Icon/Icon' import './ButtonCheckbox.scss' @@ -19,8 +18,23 @@ const ButtonCheckbox: React.FC = ({ icon, text, }) => { + useEffect(() => { + // listen for Enter key to be pressed, but + // ignore if Command/Ctrl is also pressed + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Enter' && !e.metaKey) { + onChange(!isChecked) + } + } + document.addEventListener('keydown', handleKeyDown) + return () => { + document.removeEventListener('keydown', handleKeyDown) + } + }, [isChecked, onChange]) return (
= ({ onClose() } } - document.body.addEventListener('keyup', onKeyDown) + if (outcome) { + document.body.addEventListener('keyup', onKeyDown) + } // for teardown, unbind event listeners return () => { - document.body.removeEventListener('keyup', onKeyDown) + if (outcome) { + document.body.removeEventListener('keyup', onKeyDown) + } } }, [outcome, content, description, githubInputLinkText, activeAgentPubKey]) diff --git a/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.component.tsx b/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.component.tsx index db311497..61b508c8 100644 --- a/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.component.tsx +++ b/web/src/components/MapViewCreateOutcome/MapViewCreateOutcome.component.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react' +import React, { useEffect, useRef, useState } from 'react' import useOnClickOutside from 'use-onclickoutside' import TextareaAutosize from 'react-textarea-autosize' import moment from 'moment' @@ -76,42 +76,52 @@ const MapViewCreateOutcome: React.FC = ({ closeOutcomeForm, }) => { const [isSmallScopeChecked, setIsSmallScopeChecked] = useState(false) + const [textIsFocused, setTextIsFocused] = useState(false) /* EVENT HANDLERS */ // when the text value changes const handleChange = (event) => { updateContent(event.target.value) } - // when a key is pressed - const handleKeyDown = (event) => { - if (event.key === 'Enter') { - event.preventDefault() - handleSubmit() + + // keyboard listener + useEffect(() => { + // since Enter will be used to toggle the checkbox + // when that is focused, we can submit on pure Enter for the textbox + // but they must use Command/Ctrl-Enter to submit when the button/checkbox + // is focused + const handleKeyDown = (e: KeyboardEvent) => { + if (textIsFocused && e.key === 'Enter') { + handleSubmit() + } else if (!textIsFocused && e.key === 'Enter' && e.metaKey) { + handleSubmit() + } } - } + document.addEventListener('keydown', handleKeyDown) + return () => { + document.removeEventListener('keydown', handleKeyDown) + } + }, [ + textIsFocused, + content, + activeAgentPubKey, + isSmallScopeChecked, + existingParentConnectionAddress, + ]) + // when the input comes into focus const handleFocus = (event) => { // select the text event.target.select() + setTextIsFocused(true) } - // when the input leaves focus (not focused on editing title) - const handleBlur = (event) => { - // if creating an Outcome - event.preventDefault() - // handleSubmit() + const handleBlur = () => { + setTextIsFocused(false) } // this can get called via keyboard events // or via 'onClickOutside' of the MapViewCreateOutcome component - const handleSubmit = async (event?) => { - console.log('here') - if (event) { - // this is to prevent the page from refreshing - // when the form is submitted, which is the - // default behaviour - event.preventDefault() - } - + const handleSubmit = async () => { // do not allow submit with no content if (!content || content === '') { closeOutcomeForm() @@ -169,18 +179,6 @@ const MapViewCreateOutcome: React.FC = ({ ) } - // the default - let fontSizeToUse = fontSize - if (scale < secondZoomThreshold) { - fontSizeToUse = fontSizeExtraLarge - } else if (scale < firstZoomThreshold) { - fontSizeToUse = fontSizeLarge - } - // const textStyle = { - // fontSize: fontSize, - // lineHeight: `${parseInt(fontSizeToUse) * lineHeightMultiplier}px`, - // } - const ref = useRef() useOnClickOutside(ref, handleSubmit) @@ -203,21 +201,16 @@ const MapViewCreateOutcome: React.FC = ({ // ref for the sake of onClickOutside ref={ref} > -
+
- - {/* - This Outcome is Small Scope */} - +
{/* small scope option checkbox */}