Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
146f5f1
Merge 5 stub platform pairs into unified .tsx files
chrisnojima May 17, 2026
b03f052
Fix desktop build: move native-only imports inside mobile code path
chrisnojima May 17, 2026
a27b071
Merge observer hooks and simple util platform pairs
chrisnojima May 17, 2026
2a46a04
Fix Task 2 type errors: DOM stubs in globals.native.d.ts, avoid typeo…
chrisnojima May 18, 2026
8646445
merge platform files: image, list, bottom-sheet, floating-box, text-u…
chrisnojima May 18, 2026
afa616f
merge navigation: screen-layout, crypto sub-nav, relogin
chrisnojima May 18, 2026
b763a34
merge chat components: giphy, input, list-area, video, conversation, …
chrisnojima May 18, 2026
7e108e0
merge infrastructure: push, settings-contacts, fs-platform, storeless…
chrisnojima May 18, 2026
675b58a
merge platform constants and utils: platform, init, electron, misc
chrisnojima May 18, 2026
62b26aa
merge router and styles — final large platform splits
chrisnojima May 18, 2026
cedad8e
merge common-adapters/index and fix styles/globals native stubs
chrisnojima May 18, 2026
429330d
fix webpack: null-load .native.tsx files; inline useRootKey in router
chrisnojima May 18, 2026
685cce1
fix lodash require: remove incorrect .default on CommonJS exports
chrisnojima May 18, 2026
3ae49e5
fix useIntersectionObserver require: access .default for default export
chrisnojima May 18, 2026
b14a6fb
fix iOS crash: replace await import() with require() in _getNative
chrisnojima May 18, 2026
b059b36
replace dynamic require() with static imports in app/main and chat me…
chrisnojima May 18, 2026
2e4c952
replace dynamic require() with static imports in chat audio/video/gip…
chrisnojima May 18, 2026
f7e77a8
replace dynamic require() with static imports in chat list/inbox/inpu…
chrisnojima May 18, 2026
d3f82a1
replace dynamic require() with static imports in common-adapters
chrisnojima May 18, 2026
a1d1531
replace dynamic requires with static imports in batch 5
chrisnojima May 18, 2026
e6bb846
add expo-clipboard and expo-image to native-only-modules for webpack
chrisnojima May 18, 2026
b50cfc5
replace dynamic requires with static imports in batch 6
chrisnojima May 18, 2026
de7a14b
replace dynamic requires with static imports in batch 7
chrisnojima May 18, 2026
18c598e
replace dynamic requires with static imports in batch 8
chrisnojima May 18, 2026
27618f8
replace dynamic requires with static imports in batch 9
chrisnojima May 18, 2026
c723d1c
WIP
chrisnojima May 18, 2026
17c3f0c
WIP
chrisnojima May 18, 2026
e611c92
plat merge 3 cross cleanup (#29214)
chrisnojima May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions shared/app/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {useConfigState} from '@/stores/config'
import {useShellState} from '@/stores/shell'
import * as Kb from '@/common-adapters'
import * as React from 'react'
import Main from './main.native'
import Main from './main'
import {KeyboardProvider} from 'react-native-keyboard-controller'
import Animated, {ReducedMotionConfig, ReduceMotion} from 'react-native-reanimated'
import {AppRegistry, AppState, Appearance, Keyboard} from 'react-native'
Expand All @@ -20,7 +20,7 @@ import {useUnmountAll} from '@/util/debug-react'
import {darkModeSupported, guiConfig} from 'react-native-kb'
import {install} from 'react-native-kb'
import * as DarkMode from '@/stores/darkmode'
import {initPlatformListener, onEngineConnected, onEngineDisconnected, onEngineIncoming} from '@/constants/init/index.native'
import {initPlatformListener, onEngineConnected, onEngineDisconnected, onEngineIncoming} from '@/constants/init/index'
import logger from '@/logger'

logger.info('INIT App index module load')
Expand Down
24 changes: 0 additions & 24 deletions shared/app/main.desktop.tsx

This file was deleted.

25 changes: 20 additions & 5 deletions shared/app/main.native.tsx → shared/app/main.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import Router from '@/router-v2/router'
import {PortalHost} from '@/common-adapters/portal.native'
import ResetModal from '../login/reset/modal'
import GlobalError from './global-errors'
import OutOfDate from './out-of-date'
import RuntimeStats from './runtime-stats'
import {BottomSheetModalProvider} from '@gorhom/bottom-sheet'
import {FsStatusProvider} from '@/fs/common/status'
import {SystemFileManagerIntegrationProvider} from '@/fs/common/sfmi'
import RemoteProxies from '../desktop/remote/proxies.desktop'
import {PortalHost} from '@/common-adapters/portal.native'
import RuntimeStats from './runtime-stats'
import {BottomSheetModalProvider} from '@gorhom/bottom-sheet'

const DesktopMain = function DesktopMain() {
return (
<FsStatusProvider>
<SystemFileManagerIntegrationProvider>
<RemoteProxies />
<Router />
<ResetModal />
<GlobalError />
<OutOfDate />
</SystemFileManagerIntegrationProvider>
</FsStatusProvider>
)
}

const Main = () => {
const NativeMain = () => {
return (
<FsStatusProvider>
<SystemFileManagerIntegrationProvider>
Expand All @@ -25,4 +40,4 @@ const Main = () => {
)
}

export default Main
export default isMobile ? NativeMain : DesktopMain
15 changes: 5 additions & 10 deletions shared/chat/audio/audio-video.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import * as React from 'react'
import type * as ExpoAudioModule from 'expo-audio'
import type * as ExpoModule from 'expo'
import {useAudioPlayer} from 'expo-audio'
import {useEventListener} from 'expo'
import type {Props} from './audio-video.shared'

const MobileAudioVideo = (props: Props) => {

const {useAudioPlayer} = require('expo-audio') as typeof ExpoAudioModule

const {useEventListener} = require('expo') as typeof ExpoModule

const {url, paused, onPositionUpdated, onEnded} = props
const player = useAudioPlayer(url)

Expand Down Expand Up @@ -55,9 +50,9 @@ const DesktopAudioVideo = (props: Props) => {
}
}, [paused])

const onTimeUpdate = (e: React.SyntheticEvent<{currentTime: number; duration: number}>) => {
const ct = e.currentTarget.currentTime
const dur = e.currentTarget.duration
const onTimeUpdate = () => {
const ct = vidRef.current?.currentTime ?? 0
const dur = vidRef.current?.duration ?? 0
if (dur === 0) return
onPositionUpdated(ct / dur)
}
Expand Down
63 changes: 9 additions & 54 deletions shared/chat/conversation/attachment-fullscreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import logger from '@/logger'
import type {Props} from './index.shared'
import {useData, usePreviewFallback} from './hooks'
import type {StyleOverride} from '@/common-adapters/markdown'
import {ShowToastAfterSaving} from '../messages/attachment/shared'
import {Animated, View} from 'react-native'
import {useSafeAreaFrame} from 'react-native-safe-area-context'
import {Image} from 'expo-image'
import {useVideoPlayer, VideoView} from 'expo-video'

// Stub type to avoid DOM lib dependency in native tsconfig
type VideoRef = {pause?: () => void}
Expand Down Expand Up @@ -168,10 +173,6 @@ const DesktopFullscreen = (p: Props) => {
type GestureEvent = {
nativeEvent: {touches: Array<unknown>; pageX: number}
}
type AnimatedValue = {
addListener?: (cb: (v: {value: number}) => void) => string
removeAllListeners?: () => void
}

const NativeFullscreenVideo = (p: {
path: string
Expand All @@ -182,29 +183,6 @@ const NativeFullscreenVideo = (p: {
}) => {
const {path, previewHeight, onTouchStart, onTouchEnd, onLoaded} = p

const {useVideoPlayer, VideoView} = require('expo-video') as {
useVideoPlayer: (uri: string) => {
addListener: (
event: string,
cb: (e: {status?: string; error?: unknown}) => void
) => {remove: () => void}
}
VideoView: React.ComponentType<{
player: unknown
nativeControls?: boolean
contentFit?: string
style?: Kb.Styles.StylesCrossPlatform
}>
}
const {View} = require('react-native') as {
View: React.ComponentType<{
style?: Kb.Styles.StylesCrossPlatform
onTouchStart?: (e: GestureEvent) => void
onTouchEnd?: (e: GestureEvent) => void
children?: React.ReactNode
}>
}

const sourceUri = `${path}&contentforce=true`
const player = useVideoPlayer(sourceUri)

Expand Down Expand Up @@ -243,36 +221,13 @@ const NativeFullscreen = (p: Props) => {
const {hasMessageID} = data
const [loaded, setLoaded] = React.useState(false)
const [showHeader, setShowHeader] = React.useState(_showHeader)
const fadeAnimRef = React.useRef<AnimatedValue | null>(null)
const [fadeAnim, setFadeAnim] = React.useState<AnimatedValue | null>(null)

const {ShowToastAfterSaving} = require('../messages/attachment/shared') as {
ShowToastAfterSaving: React.ComponentType<{transferState: unknown}>
}
const {Animated} = require('react-native') as {
Animated: {
Value: new (v: number) => AnimatedValue
View: React.ComponentType<{
style?: Array<Kb.Styles.StylesCrossPlatform | {opacity: AnimatedValue | number}>
children?: React.ReactNode
}>
timing: (
val: AnimatedValue,
opts: {toValue: number; duration: number; useNativeDriver: boolean}
) => {start: () => void}
}
}
const {useSafeAreaFrame} = require('react-native-safe-area-context') as {
useSafeAreaFrame: () => {width: number; height: number}
}
const {Image} = require('expo-image') as {
Image: {prefetch: (path: string) => Promise<void>}
}
const fadeAnimRef = React.useRef<Animated.Value | null>(null)
const [fadeAnim, setFadeAnim] = React.useState<Animated.Value | null>(null)

React.useEffect(() => {
fadeAnimRef.current = new Animated.Value(1)
setFadeAnim(fadeAnimRef.current)
}, [Animated])
}, [])

React.useEffect(() => {
if (fadeAnim) {
Expand All @@ -282,7 +237,7 @@ const NativeFullscreen = (p: Props) => {
useNativeDriver: true,
}).start()
}
}, [showHeader, fadeAnim, Animated])
}, [showHeader, fadeAnim])

const preload = (src: string, onLoad: () => void, onError: () => void) => {
const f = async () => {
Expand Down
47 changes: 0 additions & 47 deletions shared/chat/conversation/giphy/index.native.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import * as React from 'react'
import * as Kb from '@/common-adapters'
import UnfurlImage from '../messages/text/unfurl/unfurl-list/image'
import {getMargins, scaledWidth} from './width'
import * as React from 'react'
import {useHooks} from './hooks'
import {getMargins, scaledWidth} from './width'
import UnfurlImage from '../messages/text/unfurl/unfurl-list/image'
import {colors, darkColors} from '@/styles/colors'
import {WebView} from 'react-native-webview'
import {useColorScheme} from 'react-native'
import noop from 'lodash/noop'

const gridHeight = 100
// Stub type to avoid dom lib dependency in native tsconfig
type DivRef = {
getBoundingClientRect: () => DOMRect
clientWidth: number
}

const GiphySearch = () => {
const DesktopGiphySearch = () => {
const gridHeight = 100
const props = useHooks()
const [width, setWidth] = React.useState<number | undefined>(undefined)
const divRef = React.useRef<HTMLDivElement>(null)
const divRef = React.useRef<DivRef | null>(null)
const learnMoreUrlProps = Kb.useClickURL('https://keybase.io/docs/chat/linkpreviews')

React.useEffect(() => {
if (!divRef.current) return
const cs = getComputedStyle(divRef.current)
setWidth(divRef.current.clientWidth - parseFloat(cs.paddingLeft) - parseFloat(cs.paddingRight))
const gc = (globalThis as {getComputedStyle?: (el: unknown) => {paddingLeft: string; paddingRight: string}}).getComputedStyle
const cs = gc?.(divRef.current)
setWidth(divRef.current.clientWidth - parseFloat(cs?.paddingLeft ?? '0') - parseFloat(cs?.paddingRight ?? '0'))
}, [])

let margins: Array<number> = []
Expand All @@ -32,7 +42,7 @@ const GiphySearch = () => {
<Kb.Box2 direction="vertical" relative={true} style={styles.outerContainer}>
<Kb.Box2
direction="vertical"
ref={divRef}
ref={divRef as React.RefObject<DivRef>}
style={Kb.Styles.collapseStyles([
styles.scrollContainer,
Kb.Styles.platformStyles({isElectron: {overflowY: width ? 'auto' : 'scroll'}}),
Expand Down Expand Up @@ -89,6 +99,38 @@ const GiphySearch = () => {
)
}

const NativeGiphySearch = () => {
const p = useHooks()
const source = {uri: p.galleryURL}
const darkMode = useColorScheme() === 'dark'
const injectedJavaScript = `
(function() {
window.document.querySelector("body").style.backgroundColor = "${
darkMode ? darkColors['white'] : colors['white']
}";
})();
`

return (
<Kb.Box2 direction="horizontal" fullWidth={true} style={nativeStyles.container}>
{p.previews ? (
<WebView
onMessage={noop}
injectedJavaScript={injectedJavaScript}
allowsInlineMediaPlayback={true}
source={source}
automaticallyAdjustContentInsets={false}
mediaPlaybackRequiresUserAction={false}
/>
) : (
<Kb.Box2 direction="vertical" centerChildren={true} fullWidth={true} fullHeight={true}>
<Kb.ProgressIndicator />
</Kb.Box2>
)}
</Kb.Box2>
)
}

const styles = Kb.Styles.styleSheetCreate(
() =>
({
Expand Down Expand Up @@ -116,7 +158,6 @@ const styles = Kb.Styles.styleSheetCreate(
lineHeight: 17,
},
}),

loadingContainer: {
minHeight: 200,
},
Expand Down Expand Up @@ -144,4 +185,11 @@ const styles = Kb.Styles.styleSheetCreate(
}) as const
)

export default GiphySearch
const nativeStyles = Kb.Styles.styleSheetCreate(
() =>
({
container: {height: 80},
}) as const
)

export default isMobile ? NativeGiphySearch : DesktopGiphySearch
5 changes: 2 additions & 3 deletions shared/chat/conversation/input-area/location-popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as T from '@/constants/types'
import LocationMap from '@/chat/location-map'
import {useCurrentUserState} from '@/stores/current-user'
import {requestLocationPermission} from '@/util/platform-specific'
import type * as ExpoLocationModule from 'expo-location'
import * as ExpoLocation from 'expo-location'
import {ignorePromise} from '@/constants/utils'
import {openAppSettings} from '@/util/storeless-actions'
import {setThreadInputCommandStatus} from '@/constants/router'
Expand Down Expand Up @@ -63,13 +63,12 @@ const useWatchPosition = (
logger.info('[location] perms check due to map')
const f = async () => {
try {
const ExpoLocation = require('expo-location') as typeof ExpoLocationModule
await (requestLocationPermission as (mode?: T.RPCChat.UIWatchPositionPerm) => Promise<void>)(
T.RPCChat.UIWatchPositionPerm.base
)
const sub = await ExpoLocation.watchPositionAsync(
{accuracy: ExpoLocation.LocationAccuracy.Highest},
(location: ExpoLocationModule.LocationObject) => {
(location: ExpoLocation.LocationObject) => {
const coord = {
accuracy: Math.floor(location.coords.accuracy ?? 0),
lat: location.coords.latitude,
Expand Down
Loading