Rework web shell ui
parent
9bfffadd88
commit
c5f28376c8
|
@ -0,0 +1,6 @@
|
||||||
|
import {useColorScheme} from 'react-native'
|
||||||
|
|
||||||
|
export function useColorSchemeStyle(lightStyle: any, darkStyle: any) {
|
||||||
|
const colorScheme = useColorScheme()
|
||||||
|
return colorScheme === 'dark' ? darkStyle : lightStyle
|
||||||
|
}
|
|
@ -527,3 +527,29 @@ export function RectTallIcon({
|
||||||
</Svg>
|
</Svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
export function ComposeIcon({
|
||||||
|
style,
|
||||||
|
size,
|
||||||
|
strokeWidth = 1.5,
|
||||||
|
}: {
|
||||||
|
style?: StyleProp<TextStyle>
|
||||||
|
size?: string | number
|
||||||
|
strokeWidth?: number
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Svg
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
stroke="currentColor"
|
||||||
|
width={size || 24}
|
||||||
|
height={size || 24}
|
||||||
|
style={style}>
|
||||||
|
<Path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import _omit from 'lodash.omit'
|
import _omit from 'lodash.omit'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
|
@ -99,6 +98,7 @@ const User = ({
|
||||||
onPressUnfollow: (item: SuggestedActor) => void
|
onPressUnfollow: (item: SuggestedActor) => void
|
||||||
}) => {
|
}) => {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
const palInverted = usePalette('inverted')
|
||||||
return (
|
return (
|
||||||
<View style={[styles.actor]}>
|
<View style={[styles.actor]}>
|
||||||
<View style={styles.actorMeta}>
|
<View style={styles.actorMeta}>
|
||||||
|
@ -121,23 +121,19 @@ const User = ({
|
||||||
<View style={styles.actorBtn}>
|
<View style={styles.actorBtn}>
|
||||||
{follow ? (
|
{follow ? (
|
||||||
<TouchableOpacity onPress={() => onPressUnfollow(item)}>
|
<TouchableOpacity onPress={() => onPressUnfollow(item)}>
|
||||||
<View style={[styles.btn, styles.secondaryBtn, pal.btn]}>
|
<View style={[styles.btn, pal.btn]}>
|
||||||
<Text type="button" style={pal.text}>
|
<Text type="button" style={pal.text}>
|
||||||
Unfollow
|
Unfollow
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
) : (
|
) : (
|
||||||
<TouchableOpacity onPress={() => onPressFollow(item)}>
|
<TouchableOpacity
|
||||||
<LinearGradient
|
onPress={() => onPressFollow(item)}
|
||||||
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
style={[styles.btn, palInverted.view]}>
|
||||||
start={{x: 0, y: 0}}
|
<Text type="sm-medium" style={palInverted.text}>
|
||||||
end={{x: 1, y: 1}}
|
Follow
|
||||||
style={[styles.btn, styles.gradientBtn]}>
|
</Text>
|
||||||
<Text type="sm-medium" style={s.white}>
|
|
||||||
Follow
|
|
||||||
</Text>
|
|
||||||
</LinearGradient>
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
@ -187,6 +183,7 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingVertical: 7,
|
paddingVertical: 7,
|
||||||
|
paddingHorizontal: 14,
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
marginLeft: 6,
|
marginLeft: 6,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Pressable, StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {Pressable, StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
|
||||||
import {Link} from '../../com/util/Link'
|
import {Link} from '../../com/util/Link'
|
||||||
import {Text} from '../../com/util/text/Text'
|
import {Text} from '../../com/util/text/Text'
|
||||||
import {UserAvatar} from '../../com/util/UserAvatar'
|
import {UserAvatar} from '../../com/util/UserAvatar'
|
||||||
import {s, colors, gradients} from 'lib/styles'
|
import {colors} from 'lib/styles'
|
||||||
import {useStores} from 'state/index'
|
import {useStores} from 'state/index'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
||||||
import {
|
import {
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
HomeIconSolid,
|
HomeIconSolid,
|
||||||
|
@ -15,6 +15,7 @@ import {
|
||||||
BellIconSolid,
|
BellIconSolid,
|
||||||
MagnifyingGlassIcon,
|
MagnifyingGlassIcon,
|
||||||
CogIcon,
|
CogIcon,
|
||||||
|
ComposeIcon,
|
||||||
} from 'lib/icons'
|
} from 'lib/icons'
|
||||||
|
|
||||||
interface NavItemProps {
|
interface NavItemProps {
|
||||||
|
@ -28,15 +29,18 @@ interface NavItemProps {
|
||||||
export const NavItem = observer(
|
export const NavItem = observer(
|
||||||
({label, count, href, icon, iconFilled, isProfile}: NavItemProps) => {
|
({label, count, href, icon, iconFilled, isProfile}: NavItemProps) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const pal = usePalette('default')
|
const hoverBg = useColorSchemeStyle(
|
||||||
|
styles.navItemHoverBgLight,
|
||||||
|
styles.navItemHoverBgDark,
|
||||||
|
)
|
||||||
const isCurrent = store.nav.tab.current.url === href
|
const isCurrent = store.nav.tab.current.url === href
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
style={state => [
|
style={state => [
|
||||||
// @ts-ignore Pressable state differs for RNW -prf
|
// @ts-ignore Pressable state differs for RNW -prf
|
||||||
state.hovered && {backgroundColor: pal.colors.backgroundLight},
|
state.hovered && hoverBg,
|
||||||
]}>
|
]}>
|
||||||
<Link style={styles.navItem} href={href}>
|
<Link style={[styles.navItem, isCurrent && hoverBg]} href={href}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.navItemIconWrapper,
|
styles.navItemIconWrapper,
|
||||||
|
@ -50,8 +54,7 @@ export const NavItem = observer(
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<Text
|
<Text
|
||||||
type={isCurrent ? 'xl-bold' : 'xl'}
|
type={isCurrent || isProfile ? 'xl' : 'xl-thin'}
|
||||||
style={styles.navItemLabel}
|
|
||||||
numberOfLines={1}>
|
numberOfLines={1}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -63,6 +66,14 @@ export const NavItem = observer(
|
||||||
|
|
||||||
export const DesktopLeftColumn = observer(() => {
|
export const DesktopLeftColumn = observer(() => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
const containerBg = useColorSchemeStyle(
|
||||||
|
styles.containerBgLight,
|
||||||
|
styles.containerBgDark,
|
||||||
|
)
|
||||||
|
const hoverBg = useColorSchemeStyle(
|
||||||
|
styles.navItemHoverBgLight,
|
||||||
|
styles.navItemHoverBgDark,
|
||||||
|
)
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const onPressCompose = () => store.shell.openComposer({})
|
const onPressCompose = () => store.shell.openComposer({})
|
||||||
const avi = (
|
const avi = (
|
||||||
|
@ -70,81 +81,152 @@ export const DesktopLeftColumn = observer(() => {
|
||||||
handle={store.me.handle}
|
handle={store.me.handle}
|
||||||
displayName={store.me.displayName}
|
displayName={store.me.displayName}
|
||||||
avatar={store.me.avatar}
|
avatar={store.me.avatar}
|
||||||
size={40}
|
size={30}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, pal.border]}>
|
<View style={[styles.container, containerBg, pal.border]}>
|
||||||
<NavItem
|
<View style={styles.main}>
|
||||||
isProfile
|
<Link style={styles.logo} href="/">
|
||||||
href={`/profile/${store.me.handle}`}
|
<Text type="title-xl">Bluesky</Text>
|
||||||
label={store.me.displayName || store.me.handle}
|
</Link>
|
||||||
icon={avi}
|
<Link href="/search" style={[pal.view, pal.borderDark, styles.search]}>
|
||||||
iconFilled={avi}
|
<MagnifyingGlassIcon
|
||||||
/>
|
size={18}
|
||||||
<NavItem
|
style={[pal.textLight, styles.searchIconWrapper]}
|
||||||
href="/"
|
/>
|
||||||
label="Home"
|
<Text type="md-thin" style={pal.textLight}>
|
||||||
icon={<HomeIcon />}
|
Search
|
||||||
iconFilled={<HomeIconSolid />}
|
|
||||||
/>
|
|
||||||
<NavItem
|
|
||||||
href="/search"
|
|
||||||
label="Search"
|
|
||||||
icon={<MagnifyingGlassIcon />}
|
|
||||||
iconFilled={<MagnifyingGlassIcon strokeWidth={4} />}
|
|
||||||
/>
|
|
||||||
<NavItem
|
|
||||||
href="/notifications"
|
|
||||||
label="Notifications"
|
|
||||||
count={store.me.notifications.unreadCount}
|
|
||||||
icon={<BellIcon />}
|
|
||||||
iconFilled={<BellIconSolid />}
|
|
||||||
/>
|
|
||||||
<NavItem
|
|
||||||
href="/settings"
|
|
||||||
label="Settings"
|
|
||||||
icon={<CogIcon strokeWidth={1.5} />}
|
|
||||||
iconFilled={<CogIcon strokeWidth={2} />}
|
|
||||||
/>
|
|
||||||
<TouchableOpacity onPress={onPressCompose}>
|
|
||||||
<LinearGradient
|
|
||||||
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
|
||||||
start={{x: 0, y: 0}}
|
|
||||||
end={{x: 1, y: 1}}
|
|
||||||
style={styles.composeBtn}>
|
|
||||||
<Text type="xl-medium" style={[s.white, s.textCenter]}>
|
|
||||||
New Post
|
|
||||||
</Text>
|
</Text>
|
||||||
</LinearGradient>
|
</Link>
|
||||||
</TouchableOpacity>
|
<NavItem
|
||||||
|
href="/"
|
||||||
|
label="Home"
|
||||||
|
icon={<HomeIcon size={21} />}
|
||||||
|
iconFilled={<HomeIconSolid size={21} />}
|
||||||
|
/>
|
||||||
|
<NavItem
|
||||||
|
href="/search"
|
||||||
|
label="Explore"
|
||||||
|
icon={<MagnifyingGlassIcon size={21} />}
|
||||||
|
iconFilled={<MagnifyingGlassIcon strokeWidth={3} size={21} />}
|
||||||
|
/>
|
||||||
|
<NavItem
|
||||||
|
href="/notifications"
|
||||||
|
label="Notifications"
|
||||||
|
count={store.me.notifications.unreadCount}
|
||||||
|
icon={<BellIcon size={21} />}
|
||||||
|
iconFilled={<BellIconSolid size={21} />}
|
||||||
|
/>
|
||||||
|
<NavItem
|
||||||
|
href="/settings"
|
||||||
|
label="Settings"
|
||||||
|
icon={<CogIcon strokeWidth={2} size={21} />}
|
||||||
|
iconFilled={<CogIcon strokeWidth={2.5} size={21} />}
|
||||||
|
/>
|
||||||
|
<View style={[pal.border, styles.separator]} />
|
||||||
|
<Pressable
|
||||||
|
style={state => [
|
||||||
|
// @ts-ignore Pressable state differs for RNW -prf
|
||||||
|
state.hovered && hoverBg,
|
||||||
|
]}>
|
||||||
|
<TouchableOpacity style={styles.navItem} onPress={onPressCompose}>
|
||||||
|
<View style={styles.navItemIconWrapper}>
|
||||||
|
<ComposeIcon size={21} />
|
||||||
|
</View>
|
||||||
|
<Text type="xl-thin">New Post</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
<View style={[styles.footer, pal.borderDark]}>
|
||||||
|
<NavItem
|
||||||
|
isProfile
|
||||||
|
href={`/profile/${store.me.handle}`}
|
||||||
|
label={store.me.displayName || store.me.handle}
|
||||||
|
icon={avi}
|
||||||
|
iconFilled={avi}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
containerBgLight: {
|
||||||
|
backgroundColor: '#f9f9fd',
|
||||||
|
},
|
||||||
|
containerBgDark: {
|
||||||
|
backgroundColor: '#f9f9fd', // TODO
|
||||||
|
},
|
||||||
|
|
||||||
container: {
|
container: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: 'calc(50vw - 530px)',
|
left: 0,
|
||||||
width: '230px',
|
width: '300px',
|
||||||
height: '100%',
|
height: '100vh',
|
||||||
borderRightWidth: 1,
|
borderRightWidth: 1,
|
||||||
paddingTop: 5,
|
paddingTop: 5,
|
||||||
},
|
},
|
||||||
navItem: {
|
main: {
|
||||||
paddingVertical: 10,
|
flex: 1,
|
||||||
paddingHorizontal: 10,
|
paddingHorizontal: 16,
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
borderTopWidth: 1,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 8,
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
borderTopWidth: 1,
|
||||||
|
marginVertical: 12,
|
||||||
|
marginHorizontal: 8,
|
||||||
|
},
|
||||||
|
|
||||||
|
logo: {
|
||||||
|
paddingTop: 6,
|
||||||
|
paddingBottom: 12,
|
||||||
|
},
|
||||||
|
|
||||||
|
search: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
borderRadius: 8,
|
||||||
|
paddingVertical: 8,
|
||||||
|
paddingHorizontal: 6,
|
||||||
|
marginBottom: 10,
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
searchIconWrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
width: 30,
|
||||||
|
justifyContent: 'center',
|
||||||
|
marginRight: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
navItem: {
|
||||||
|
paddingVertical: 8,
|
||||||
|
paddingHorizontal: 6,
|
||||||
|
marginBottom: 2,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 6,
|
||||||
|
},
|
||||||
|
navItemHoverBgLight: {
|
||||||
|
backgroundColor: '#ebebf0',
|
||||||
|
borderRadius: 6,
|
||||||
|
},
|
||||||
|
navItemHoverBgDark: {
|
||||||
|
backgroundColor: colors.gray2, // TODO
|
||||||
|
borderRadius: 6,
|
||||||
},
|
},
|
||||||
navItemIconWrapper: {
|
navItemIconWrapper: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
width: 30,
|
width: 30,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
marginRight: 5,
|
marginRight: 8,
|
||||||
},
|
},
|
||||||
navItemProfile: {
|
navItemProfile: {
|
||||||
width: 40,
|
width: 30,
|
||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
},
|
},
|
||||||
navItemCount: {
|
navItemCount: {
|
||||||
|
@ -158,9 +240,6 @@ const styles = StyleSheet.create({
|
||||||
paddingHorizontal: 4,
|
paddingHorizontal: 4,
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
},
|
},
|
||||||
navItemLabel: {
|
|
||||||
fontSize: 19,
|
|
||||||
},
|
|
||||||
composeBtn: {
|
composeBtn: {
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
|
|
|
@ -1,25 +1,15 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {View, StyleSheet} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {Link} from '../../com/util/Link'
|
|
||||||
import {Text} from '../../com/util/text/Text'
|
import {Text} from '../../com/util/text/Text'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {MagnifyingGlassIcon} from 'lib/icons'
|
|
||||||
import {LiteSuggestedFollows} from '../../com/discover/LiteSuggestedFollows'
|
import {LiteSuggestedFollows} from '../../com/discover/LiteSuggestedFollows'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
|
import {useStores} from 'state/index'
|
||||||
|
|
||||||
export const DesktopRightColumn: React.FC = () => {
|
export const DesktopRightColumn: React.FC = () => {
|
||||||
const pal = usePalette('default')
|
const store = useStores()
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, pal.border]}>
|
<View style={styles.container}>
|
||||||
<Link href="/search" style={[pal.btn, styles.searchContainer]}>
|
<Text type="lg-bold" style={s.mb10}>
|
||||||
<View style={styles.searchIcon}>
|
|
||||||
<MagnifyingGlassIcon style={pal.textLight} />
|
|
||||||
</View>
|
|
||||||
<Text type="lg" style={pal.textLight}>
|
|
||||||
Search
|
|
||||||
</Text>
|
|
||||||
</Link>
|
|
||||||
<Text type="xl-bold" style={s.mb10}>
|
|
||||||
Suggested Follows
|
Suggested Follows
|
||||||
</Text>
|
</Text>
|
||||||
<LiteSuggestedFollows />
|
<LiteSuggestedFollows />
|
||||||
|
@ -30,23 +20,10 @@ export const DesktopRightColumn: React.FC = () => {
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: 'calc(50vw - 650px)',
|
right: 0,
|
||||||
width: '350px',
|
width: '400px',
|
||||||
height: '100%',
|
paddingHorizontal: 16,
|
||||||
borderLeftWidth: 1,
|
paddingRight: 32,
|
||||||
overscrollBehavior: 'auto',
|
paddingTop: 20,
|
||||||
paddingLeft: 30,
|
|
||||||
paddingTop: 10,
|
|
||||||
},
|
|
||||||
searchContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingHorizontal: 14,
|
|
||||||
paddingVertical: 10,
|
|
||||||
borderRadius: 20,
|
|
||||||
marginBottom: 20,
|
|
||||||
},
|
|
||||||
searchIcon: {
|
|
||||||
marginRight: 5,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue