Skip to content

Commit 7f92991

Browse files
committed
refactor(mobile): improve emoji picker ux flow
1 parent 30d73bd commit 7f92991

17 files changed

Lines changed: 314 additions & 110 deletions

File tree

packages/webapp/components/BottomSheet.tsx

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import React, { useEffect, useMemo } from 'react'
1+
import React, { useEffect, useMemo, useRef } from 'react'
22
import { Sheet, SheetProps } from 'react-modal-sheet'
3-
43
import { useSheetStore, useChatStore, useStore } from '@stores'
54
import FilterModal from '@components/pages/document/components/FilterModal'
65
import NotificationModal from './notificationPanel/mobile/NotificationModal'
76
import ChatContainerMobile from './pages/document/components/chat/ChatContainerMobile'
8-
import useKeyboardHeight from '@hooks/useKeyboardHeight'
97
import { EmojiPanel } from './chat/components/EmojiPanel'
108
import { CHAT_OPEN } from '@services/eventsHub'
119

@@ -15,8 +13,11 @@ const BottomSheet = () => {
1513
const closeChatRoom = useChatStore((state) => state.closeChatRoom)
1614
const destroyChatRoom = useChatStore((state) => state.destroyChatRoom)
1715
const setSheetContainerRef = useSheetStore((state) => state.setSheetContainerRef)
18-
const { height: keyboardHeight } = useKeyboardHeight()
16+
const setSheetState = useSheetStore((state) => state.setSheetState)
17+
const { keyboardHeight, virtualKeyboardState } = useStore((state) => state)
1918
const { deviceDetect } = useStore((state) => state.settings)
19+
const sheetContentRef = useRef<HTMLDivElement>(null)
20+
2021
const isDeviceIOS = useMemo(() => {
2122
return deviceDetect?.os() === 'iOS'
2223
}, [deviceDetect])
@@ -75,58 +76,60 @@ const BottomSheet = () => {
7576
}
7677
}
7778

78-
const getSheetContentProps = () => {
79-
switch (activeSheet) {
80-
case 'filters':
81-
return {
82-
// Fix the bottom sheet height when keyboard is open
83-
style: { paddingBottom: isDeviceIOS ? keyboardHeight : 0 }
84-
}
85-
case 'chatroom':
86-
return {
87-
style: {
88-
paddingBottom: isDeviceIOS ? keyboardHeight : 0
89-
}
90-
}
91-
case 'emojiPicker':
92-
return {
93-
style: {
94-
paddingBottom: isDeviceIOS ? keyboardHeight : 0
95-
}
96-
}
97-
default:
98-
return {}
99-
}
100-
}
101-
10279
const handleClose = () => {
10380
if (activeSheet === 'chatroom') {
10481
closeChatRoom()
10582
destroyChatRoom()
10683
} else if (activeSheet === 'emojiPicker' && sheetData.chatRoomState) {
10784
const { headingId } = sheetData.chatRoomState
108-
closeSheet()
109-
setTimeout(() => {
110-
PubSub.publish(CHAT_OPEN, {
111-
headingId: headingId,
112-
focusEditor: true,
113-
clearSheetState: true
114-
})
115-
}, 100)
85+
PubSub.publish(CHAT_OPEN, {
86+
headingId: headingId,
87+
focusEditor: true,
88+
clearSheetState: true,
89+
switchSheet: 'chatroom'
90+
})
11691
} else {
11792
closeSheet()
11893
}
11994
}
12095

96+
useEffect(() => {
97+
if (!sheetContentRef.current) return
98+
99+
const paddingBottom = isDeviceIOS ? Math.round(keyboardHeight).toString() + 'px' : '0px'
100+
101+
switch (activeSheet) {
102+
case 'filters':
103+
sheetContentRef.current.style.paddingBottom = paddingBottom
104+
case 'chatroom':
105+
sheetContentRef.current.style.paddingBottom = paddingBottom
106+
case 'emojiPicker':
107+
sheetContentRef.current.style.paddingBottom =
108+
virtualKeyboardState === 'open' ? paddingBottom : '0px'
109+
default:
110+
return
111+
}
112+
}, [sheetContentRef, activeSheet, keyboardHeight, isDeviceIOS, virtualKeyboardState])
113+
114+
// NOTE: these events are more reliable than the other events for sheet state
115+
const onOpenEndHandler = () => setSheetState('closed')
116+
const onViewportEnterHandler = () => setSheetState('open')
117+
const onOpenStartHandler = () => setSheetState('opening')
118+
const onCloseStartHandler = () => setSheetState('closing')
119+
121120
return (
122121
<Sheet
123122
className="bottom-sheet"
124123
isOpen={!!activeSheet}
125124
onClose={handleClose}
125+
onCloseStart={onCloseStartHandler}
126+
onOpenStart={onOpenStartHandler}
127+
onOpenEnd={onOpenEndHandler}
128+
onViewportEnter={onViewportEnterHandler}
126129
{...getSheetProps()}>
127-
<Sheet.Container ref={setSheetContainerRef}>
130+
<Sheet.Container ref={setSheetContainerRef} onViewportEnter={onViewportEnterHandler}>
128131
<Sheet.Header />
129-
<Sheet.Content {...getSheetContentProps()}>{renderContent()}</Sheet.Content>
132+
<Sheet.Content ref={sheetContentRef}>{renderContent()}</Sheet.Content>
130133
</Sheet.Container>
131134
<Sheet.Backdrop onTap={handleClose} />
132135
</Sheet>

packages/webapp/components/chat/components/ChannelActionBar.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ import { useChannel } from '../context/ChannelProvider'
88
import SignInToJoinChannel from './SignInToJoinChannel'
99
import { TChannelSettings } from '@types'
1010
import MessageComposer from './MessageComposer/MessageComposer'
11-
import useKeyboardHeight from '@hooks/useKeyboardHeight'
1211
import { useMessageComposer } from './MessageComposer/hooks/useMessageComposer'
1312

1413
const MobileToolbar = () => {
15-
const { isOpen: isKeyboardOpen } = useKeyboardHeight()
14+
const { isKeyboardOpen } = useStore((state) => state)
1615
const { isToolbarOpen, toggleToolbar } = useMessageComposer()
1716

1817
if (!isKeyboardOpen) return null

packages/webapp/components/chat/components/EmojiPanel/Selector.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export const EmojiSelector = () => {
1919

2020
PubSub.publish(CHAT_OPEN, {
2121
headingId: headingId,
22-
focusEditor: true,
2322
insertContent: emoji.native,
2423
clearSheetState: true
2524
})

packages/webapp/components/chat/components/MessageComposer/Mobile/MobileWrapper.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { useSheetStore, useStore } from '@stores'
22
import { useEffect } from 'react'
3-
import useKeyboardHeight from '@hooks/useKeyboardHeight'
43
import { applyStyles } from '../helpers/domUtils'
54

65
type Props = {
@@ -21,7 +20,7 @@ const SHEET_STYLES = {
2120

2221
export const MobileWrapper = ({ children }: Props) => {
2322
const { sheetContainerRef } = useSheetStore()
24-
const { isOpen: isKeyboardOpen } = useKeyboardHeight()
23+
const { isKeyboardOpen } = useStore((state) => state)
2524
const {
2625
settings: {
2726
editor: { isMobile }

packages/webapp/components/chat/components/MessageComposer/components/Actions/ActionButtons/EmojiButton.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const getCaretPosition = (editor: Editor) => {
2929
export const EmojiButton = ({ className, size = 20, ...props }: Props) => {
3030
const { editor } = useMessageComposer()
3131
const { toggleEmojiPicker } = useChatStore()
32-
const { openSheet, closeSheet } = useSheetStore()
32+
const { switchSheet } = useSheetStore()
3333
const { chatRoom } = useChatStore()
3434
const {
3535
settings: {
@@ -73,8 +73,7 @@ export const EmojiButton = ({ className, size = 20, ...props }: Props) => {
7373
if (!editor) return
7474

7575
if (isMobile) {
76-
closeSheet()
77-
openSheet('emojiPicker', {
76+
switchSheet('emojiPicker', {
7877
chatRoomState: { ...chatRoom }
7978
})
8079
} else {

packages/webapp/components/pages/document/components/BigPencilBtn.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React from 'react'
22
import { useStore } from '@stores'
3-
import useDetectKeyboardOpen from 'use-detect-keyboard-open'
43
import { Pencil } from '@icons'
54

65
const BigPencilBtn = () => {
@@ -9,7 +8,7 @@ const BigPencilBtn = () => {
98
editor: { isMobile, instance: editor, selectionPos }
109
}
1110
} = useStore((state) => state)
12-
const isKeyboardOpen = useDetectKeyboardOpen() || false
11+
const { isKeyboardOpen } = useStore((state) => state)
1312

1413
const toggleToolbar = () => {
1514
if (!isMobile) return

packages/webapp/components/pages/document/components/MobileEditor.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
import { useEffect, useRef, useState } from 'react'
22
import EditorContent from './EditorContent'
33
import ToolbarMobile from './toolbarMobile/ToolbarMobile'
4-
import { useChatStore } from '@stores'
4+
import { useChatStore, useStore } from '@stores'
55
import { useAdjustEditorSizeForChatRoom } from '../hooks'
66
import useEditableDocControl from '@components/pages/document/hooks/useEditableDocControl'
77
import usePageHeightAdjust from '@components/pages/document/hooks/usePageHeightAdjust'
88
import useUpdateDocPageUnreadMsg from '@components/pages/document/hooks/useUpdateDocPageUnreadMsg'
99
import { MobileBubbleMenu } from './MobileBubbleMenu'
10-
import { animate, useMotionValue } from 'motion/react'
11-
import useKeyboardHeight from '@hooks/useKeyboardHeight'
1210

1311
const Editor = () => {
1412
const editorWrapperRef = useRef<HTMLDivElement>(null)
1513

1614
const chatRoom = useChatStore((state) => state.chatRoom)
1715

18-
const { isOpen: isKeyboardOpen, height: keyboardHeight, viewportHeight } = useKeyboardHeight()
16+
const { isKeyboardOpen } = useStore((state) => state)
1917

2018
// @ts-ignore
2119
useAdjustEditorSizeForChatRoom(editorWrapperRef)

packages/webapp/components/pages/document/hooks/useEditableDocControl.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useEffect } from 'react'
2-
import useDetectKeyboardOpen from 'use-detect-keyboard-open'
32
import { useStore } from '@stores'
43

54
const useEditableDocControl = () => {
@@ -8,7 +7,7 @@ const useEditableDocControl = () => {
87
editor: { instance: editor, loading }
98
} = useStore((state) => state.settings)
109

11-
const isKeyboardOpen = useDetectKeyboardOpen() || false
10+
const { isKeyboardOpen } = useStore((state) => state)
1211

1312
useEffect(() => {
1413
if (!editor) return

packages/webapp/components/pages/document/layouts/MobileLayout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ModalDrawer } from '@components/ui/ModalDrawer'
88
import { useHashRouter } from '@hooks/useHashRouter'
99
import MobileHistory from '@components/pages/history/mobile/MobileHistory'
1010
import BottomSheet from '@components/BottomSheet'
11+
import useVirtualKeyboard from '@hooks/useVirtualKeyboard'
1112

1213
const MobileLeftSidePanel = ({ filterModalRef }: any) => {
1314
return (
@@ -28,6 +29,7 @@ const MobileLayout = () => {
2829
const deviceClass = isMobile ? 'm_mobile' : 'm_desktop'
2930

3031
const isHistoryView = useHashRouter()
32+
useVirtualKeyboard()
3133

3234
if (isHistoryView) return <MobileHistory />
3335

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
1-
import { useEffect, useState } from 'react'
1+
import { useEffect } from 'react'
2+
import { useStore } from '@stores'
23

3-
interface KeyboardState {
4-
isOpen: boolean
5-
height: number
6-
viewportHeight: number
7-
}
8-
9-
const useKeyboardHeight = (): KeyboardState => {
10-
const [keyboardState, setKeyboardState] = useState<KeyboardState>({
11-
isOpen: false,
12-
height: 0,
13-
viewportHeight: window.innerHeight
14-
})
4+
const useVirtualKeyboard = () => {
5+
const { setKeyboardOpen, setKeyboardHeight, setVirtualKeyboardState } = useStore((state) => state)
156

167
useEffect(() => {
178
const visualViewport = window.visualViewport
@@ -27,11 +18,9 @@ const useKeyboardHeight = (): KeyboardState => {
2718
const keyboardHeight = windowHeight - viewportHeight
2819
const isKeyboardOpen = keyboardHeight > 0
2920

30-
setKeyboardState({
31-
isOpen: isKeyboardOpen,
32-
height: keyboardHeight,
33-
viewportHeight: viewportHeight
34-
})
21+
setKeyboardOpen(isKeyboardOpen)
22+
setKeyboardHeight(keyboardHeight)
23+
setVirtualKeyboardState(isKeyboardOpen ? 'open' : 'closed')
3524
}
3625

3726
// Initial check
@@ -46,8 +35,6 @@ const useKeyboardHeight = (): KeyboardState => {
4635
visualViewport.removeEventListener('scroll', handleViewportChange)
4736
}
4837
}, [])
49-
50-
return keyboardState
5138
}
5239

53-
export default useKeyboardHeight
40+
export default useVirtualKeyboard

0 commit comments

Comments
 (0)