diff --git a/public/index.html b/public/index.html index edc9d8e1..59487592 100644 --- a/public/index.html +++ b/public/index.html @@ -13,29 +13,10 @@ #app-root { display:flex; height:100%; } /* Remove focus state on inputs */ - input:focus { + input:focus, + textarea:focus { outline: 0; } - - /* These styles are for src/view/com/modals/WebModal */ - div[data-modal-overlay] { - position: fixed; - top: 0; - left: 0; - background: #0004; - width: 100vw; - height: 100vh; - } - div[data-modal-container] { - position: fixed; - top: 20vh; - left: calc(50vw - 300px); - width: 600px; - padding: 20px; - background: #fff; - border-radius: 10px; - box-shadow: 0 5px 10px #0005; - } diff --git a/src/view/com/composer/ComposePost.tsx b/src/view/com/composer/ComposePost.tsx index 64e75328..2f30a1cf 100644 --- a/src/view/com/composer/ComposePost.tsx +++ b/src/view/com/composer/ComposePost.tsx @@ -11,25 +11,19 @@ import { TouchableWithoutFeedback, View, } from 'react-native' -import PasteInput, { - PastedFile, - PasteInputRef, -} from '@mattermost/react-native-paste-input' import LinearGradient from 'react-native-linear-gradient' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' -import {useAnalytics} from '@segment/analytics-react-native' +// import {useAnalytics} from '@segment/analytics-react-native' TODO import {UserAutocompleteViewModel} from '../../../state/models/user-autocomplete-view' import {Autocomplete} from './Autocomplete' import {ExternalEmbed} from './ExternalEmbed' import {Text} from '../util/text/Text' import * as Toast from '../util/Toast' -// @ts-ignore no type definition -prf -import ProgressCircle from 'react-native-progress/Circle' -// @ts-ignore no type definition -prf -import ProgressPie from 'react-native-progress/Pie' +import {TextInput, TextInputRef} from './text-input/TextInput' +import {CharProgress} from './char-progress/CharProgress' import {TextLink} from '../util/Link' import {UserAvatar} from '../util/UserAvatar' import {useStores} from '../../../state' @@ -49,7 +43,6 @@ import {SelectedPhoto} from './SelectedPhoto' import {usePalette} from '../../lib/hooks/usePalette' const MAX_TEXT_LENGTH = 256 -const DANGER_TEXT_LENGTH = MAX_TEXT_LENGTH const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10} export const ComposePost = observer(function ComposePost({ @@ -63,10 +56,10 @@ export const ComposePost = observer(function ComposePost({ onPost?: ComposerOpts['onPost'] onClose: () => void }) { - const {track} = useAnalytics() + // const {track} = useAnalytics() TODO const pal = usePalette('default') const store = useStores() - const textInput = useRef(null) + const textInput = useRef(null) const [isProcessing, setIsProcessing] = useState(false) const [processingState, setProcessingState] = useState('') const [error, setError] = useState('') @@ -80,7 +73,6 @@ export const ComposePost = observer(function ComposePost({ ) const [selectedPhotos, setSelectedPhotos] = useState([]) - // Using default import (React.use...) instead of named import (use...) to be able to mock store's data in jest environment const autocompleteView = React.useMemo( () => new UserAutocompleteViewModel(store), [store], @@ -219,19 +211,18 @@ export const ComposePost = observer(function ComposePost({ } } } - const onPaste = async (err: string | undefined, files: PastedFile[]) => { + const onPaste = async (err: string | undefined, uris: string[]) => { if (err) { return setError(cleanError(err)) } if (selectedPhotos.length >= 4) { return } - const imgFile = files.find(file => /\.(jpe?g|png)$/.test(file.fileName)) - if (!imgFile) { - return + const imgUri = uris.find(uri => /\.(jpe?g|png)$/.test(uri)) + if (imgUri) { + const finalImgPath = await cropPhoto(imgUri) + onSelectPhotos([...selectedPhotos, finalImgPath]) } - const finalImgPath = await cropPhoto(imgFile.uri) - onSelectPhotos([...selectedPhotos, finalImgPath]) } const onPressCancel = () => hackfixOnClose() const onPressPublish = async () => { @@ -257,9 +248,10 @@ export const ComposePost = observer(function ComposePost({ autocompleteView.knownHandles, setProcessingState, ) - track('Create Post', { - imageCount: selectedPhotos.length, - }) + // TODO + // track('Create Post', { + // imageCount: selectedPhotos.length, + // }) } catch (e: any) { setError(cleanError(e.message)) setIsProcessing(false) @@ -276,7 +268,6 @@ export const ComposePost = observer(function ComposePost({ } const canPost = text.length <= MAX_TEXT_LENGTH - const progressColor = text.length > DANGER_TEXT_LENGTH ? '#e60000' : undefined const selectTextInputLayout = selectedPhotos.length !== 0 @@ -311,7 +302,7 @@ export const ComposePost = observer(function ComposePost({ + style={styles.outer}> @@ -396,22 +387,19 @@ export const ComposePost = observer(function ComposePost({ avatar={store.me.avatar} size={50} /> - onChangeText(str)} onPaste={onPaste} placeholder={selectTextInputPlaceholder} - placeholderTextColor={pal.colors.textLight} style={[ pal.text, styles.textInput, styles.textInputFormatting, ]}> {textDecorated} - + - - {MAX_TEXT_LENGTH - text.length} - - - {text.length > DANGER_TEXT_LENGTH ? ( - - ) : ( - - )} - + DANGER_TEXT_LENGTH ? '#e60000' : undefined + return ( + <> + + {MAX_TEXT_LENGTH - count} + + + {count > DANGER_TEXT_LENGTH ? ( + + ) : ( + + )} + + + ) +} diff --git a/src/view/com/composer/char-progress/CharProgress.web.tsx b/src/view/com/composer/char-progress/CharProgress.web.tsx new file mode 100644 index 00000000..6bdcc139 --- /dev/null +++ b/src/view/com/composer/char-progress/CharProgress.web.tsx @@ -0,0 +1,39 @@ +import React from 'react' +import {View} from 'react-native' +import {Text} from '../util/text/Text' +import {s} from '../../lib/styles' + +const MAX_TEXT_LENGTH = 256 +const DANGER_TEXT_LENGTH = MAX_TEXT_LENGTH + +export function CharProgress({count}: {count: number}) { + const progressColor = count > DANGER_TEXT_LENGTH ? '#e60000' : undefined + return ( + <> + + {MAX_TEXT_LENGTH - count} + + + { + null /* TODO count > DANGER_TEXT_LENGTH ? ( + + ) : ( + + )*/ + } + + + ) +} diff --git a/src/view/com/composer/text-input/TextInput.tsx b/src/view/com/composer/text-input/TextInput.tsx new file mode 100644 index 00000000..3c5dacf8 --- /dev/null +++ b/src/view/com/composer/text-input/TextInput.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import {StyleProp, TextStyle} from 'react-native' +import PasteInput, { + PastedFile, + PasteInputRef, +} from '@mattermost/react-native-paste-input' +import {usePalette} from '../../../lib/hooks/usePalette' + +export type TextInputRef = PasteInputRef + +interface TextInputProps { + testID: string + innerRef: React.Ref + placeholder: string + style: StyleProp + onChangeText: (str: string) => void + onPaste: (err: string | undefined, uris: string[]) => void +} + +export function TextInput({ + testID, + innerRef, + placeholder, + style, + onChangeText, + onPaste, + children, +}: React.PropsWithChildren) { + const pal = usePalette('default') + const onPasteInner = (err: string | undefined, files: PastedFile[]) => { + if (err) { + onPaste(err, []) + } else { + onPaste( + undefined, + files.map(f => f.uri), + ) + } + } + return ( + onChangeText(str)} + onPaste={onPasteInner} + placeholder={placeholder} + placeholderTextColor={pal.colors.textLight} + style={style}> + {children} + + ) +} diff --git a/src/view/com/composer/text-input/TextInput.web.tsx b/src/view/com/composer/text-input/TextInput.web.tsx new file mode 100644 index 00000000..6960bf7a --- /dev/null +++ b/src/view/com/composer/text-input/TextInput.web.tsx @@ -0,0 +1,51 @@ +import React from 'react' +import { + StyleProp, + StyleSheet, + TextInput as RNTextInput, + TextStyle, +} from 'react-native' +import {usePalette} from '../../lib/hooks/usePalette' +import {addStyle} from '../../lib/addStyle' + +export type TextInputRef = RNTextInput + +interface TextInputProps { + testID: string + innerRef: React.Ref + placeholder: string + style: StyleProp + onChangeText: (str: string) => void + onPaste: (err: string | undefined, uris: string[]) => void +} + +export function TextInput({ + testID, + innerRef, + placeholder, + style, + onChangeText, + children, +}: React.PropsWithChildren) { + const pal = usePalette('default') + style = addStyle(style, styles.input) + return ( + onChangeText(str)} + placeholder={placeholder} + placeholderTextColor={pal.colors.textLight} + style={style}> + {children} + + ) +} + +const styles = StyleSheet.create({ + input: { + minHeight: 140, + }, +}) diff --git a/src/view/shell/mobile/Composer.tsx b/src/view/shell/mobile/Composer.tsx index a19a4704..c93931ab 100644 --- a/src/view/shell/mobile/Composer.tsx +++ b/src/view/shell/mobile/Composer.tsx @@ -48,9 +48,6 @@ export const Composer = observer( ], } - // events - // = - // rendering // = diff --git a/src/view/shell/web/Composer.tsx b/src/view/shell/web/Composer.tsx new file mode 100644 index 00000000..63904009 --- /dev/null +++ b/src/view/shell/web/Composer.tsx @@ -0,0 +1,65 @@ +import React from 'react' +import {observer} from 'mobx-react-lite' +import {StyleSheet, View} from 'react-native' +import {ComposePost} from '../../com/composer/ComposePost' +import {ComposerOpts} from '../../../state/models/shell-ui' +import {usePalette} from '../../lib/hooks/usePalette' + +export const Composer = observer( + ({ + active, + replyTo, + imagesOpen, + onPost, + onClose, + }: { + active: boolean + winHeight: number + replyTo?: ComposerOpts['replyTo'] + imagesOpen?: ComposerOpts['imagesOpen'] + onPost?: ComposerOpts['onPost'] + onClose: () => void + }) => { + const pal = usePalette('default') + + // rendering + // = + + if (!active) { + return + } + + return ( + + + + + + ) + }, +) + +const styles = StyleSheet.create({ + mask: { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + backgroundColor: '#000c', + alignItems: 'center', + justifyContent: 'center', + }, + container: { + maxWidth: 600, + width: '100%', + paddingVertical: 0, + paddingHorizontal: 2, + borderRadius: 8, + }, +}) diff --git a/src/view/shell/web/left-column.tsx b/src/view/shell/web/DesktopLeftColumn.tsx similarity index 100% rename from src/view/shell/web/left-column.tsx rename to src/view/shell/web/DesktopLeftColumn.tsx diff --git a/src/view/shell/web/right-column.tsx b/src/view/shell/web/DesktopRightColumn.tsx similarity index 100% rename from src/view/shell/web/right-column.tsx rename to src/view/shell/web/DesktopRightColumn.tsx diff --git a/src/view/shell/web/index.tsx b/src/view/shell/web/index.tsx index a4232eab..0eb5cf75 100644 --- a/src/view/shell/web/index.tsx +++ b/src/view/shell/web/index.tsx @@ -3,13 +3,14 @@ import {observer} from 'mobx-react-lite' import {View, StyleSheet} from 'react-native' import {useStores} from '../../../state' import {match, MatchResult} from '../../routes' -import {DesktopLeftColumn} from './left-column' -import {DesktopRightColumn} from './right-column' +import {DesktopLeftColumn} from './DesktopLeftColumn' +import {DesktopRightColumn} from './DesktopRightColumn' import {Onboard} from '../../screens/Onboard' import {Login} from '../../screens/Login' import {ErrorBoundary} from '../../com/util/ErrorBoundary' import {Lightbox} from '../../com/lightbox/Lightbox' import {Modal} from '../../com/modals/Modal' +import {Composer} from './Composer' import {usePalette} from '../../lib/hooks/usePalette' import {s} from '../../lib/styles' @@ -49,6 +50,14 @@ export const WebShell: React.FC = observer(() => { ))} + store.shell.closeComposer()} + winHeight={0} + replyTo={store.shell.composerOpts?.replyTo} + imagesOpen={store.shell.composerOpts?.imagesOpen} + onPost={store.shell.composerOpts?.onPost} + />