Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BA-2403: logo override for native #247

Merged
merged 10 commits into from
Apr 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import useChangePassword from '../index'

describe('useChangePassword', () => {
const currentPassword = '1234'
const password = '12345#Abcde'
const password = 'abcABC@123456'
const changePasswordUrl = '/users/change-password'

afterEach(() => {
Expand Down
7 changes: 7 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @baseapp-frontend/components

## 1.0.29

### Patch Changes

- Updated dependencies
- @baseapp-frontend/design-system@1.0.13

## 1.0.28

### Patch Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const BottomDrawer: FC<BottomDrawerProps> = ({
<Pressable onPress={() => handleRemoveImage(type)} style={styles.modalItem}>
<TrashIcon width={20} height={20} color={theme.colors.error.main} />
<Text variant="body2" style={{ color: theme.colors.error.main }}>
Remove {type}
Remove {type === 'image' ? 'image' : 'banner'}
</Text>
</Pressable>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { BottomSheetModal } from '@gorhom/bottom-sheet'
export type BottomDrawerProps = {
bottomDrawerRef: React.RefObject<BottomSheetModal>
handleSheetChanges: (index: number) => void
type?: 'image' | 'banner'
type?: 'image' | 'bannerImage'
handleViewPhotoLibrary: () => void
handleTakePhoto: () => void
handleRemoveImage: (type: 'image' | 'banner') => void
handleRemoveImage: (type: 'image' | 'bannerImage') => void
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Button } from '@baseapp-frontend/design-system/components/native/button
import { CameraIcon, ImageIcon } from '@baseapp-frontend/design-system/components/native/icons'
import { TextInput } from '@baseapp-frontend/design-system/components/native/inputs'
import { View } from '@baseapp-frontend/design-system/components/native/views'
import { useViewPhotoLibrary } from '@baseapp-frontend/design-system/hooks/native'
import { useTheme } from '@baseapp-frontend/design-system/providers/native'
import {
ACCESS_KEY_NAME,
Expand Down Expand Up @@ -41,7 +42,7 @@ const ProfileSettingsComponent: FC<ProfileSettingsComponentProps> = ({ profile:
const profile = useFragment(ProfileComponentFragment, profileRef)
const bottomDrawerRef = useRef<BottomSheetModal>(null)
const [commitMutation, isMutationInFlight] = useProfileMutation()
const [fieldType, setFieldType] = useState<'image' | 'banner'>('image')
const [fieldType, setFieldType] = useState<'image' | 'bannerImage'>('image')
// const { updateProfileIfActive } = useCurrentProfile()
const { sendToast } = useNotification()

Expand Down Expand Up @@ -153,52 +154,18 @@ const ProfileSettingsComponent: FC<ProfileSettingsComponentProps> = ({ profile:
onSubmit(data)
}

const handleImageSelection = async (
result: ImagePicker.ImagePickerResult,
type: 'image' | 'banner',
) => {
if (!result.canceled) {
const loadedImage = result.assets[0]?.uri
const fieldName = type === 'image' ? PROFILE_FORM_VALUE.image : PROFILE_FORM_VALUE.bannerImage

setValue(fieldName, loadedImage, { shouldValidate: true, shouldDirty: true })
}
bottomDrawerRef.current?.close()
}
const aspectRatio = fieldType === 'image' ? ([1, 1] as const) : ([16, 9] as const)

const handleViewPhotoLibrary = async () => {
const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync()
if (permissionResult.granted === false) {
sendToast('Permission to access photo library is required!', { type: 'error' })
return
}

const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: 'images',
allowsEditing: Platform.OS === 'android',
aspect: [aspectRatio[0], aspectRatio[1]],
quality: 1,
})

handleImageSelection(result, fieldType)
}

const handleTakePhoto = async () => {
const permissionResult = await ImagePicker.requestCameraPermissionsAsync()
if (permissionResult.granted === false) {
sendToast('Permission to access camera is required!', { type: 'error' })
return
}

const result = await ImagePicker.launchCameraAsync({
allowsEditing: Platform.OS === 'android',
aspect: [aspectRatio[0], aspectRatio[1]],
quality: 1,
})

handleImageSelection(result, fieldType)
}
const { handleViewPhotoLibrary, handleTakePhoto } = useViewPhotoLibrary({
allowsEditing: Platform.OS === 'android',
isBase64: false,
onResult: (image?: ImagePicker.ImagePickerAsset) => {
const imageUri = image?.uri
setValue(fieldType, imageUri, { shouldValidate: true, shouldDirty: true })
},
onFinally: () => {
bottomDrawerRef.current?.close()
},
type: fieldType,
})

// TODO: add this back when support multiple profiles
// useEffect(() => {
Expand All @@ -212,7 +179,7 @@ const ProfileSettingsComponent: FC<ProfileSettingsComponentProps> = ({ profile:
// }
// }, [profile?.id, profile?.name, profile?.urlPath?.path, profile?.image?.url])

const handleRemoveImage = (type: 'image' | 'banner') => {
const handleRemoveImage = (type: 'image' | 'bannerImage') => {
clearErrors(type === 'image' ? 'image' : 'bannerImage')
setValue(type === 'image' ? 'image' : 'bannerImage', undefined, {
shouldValidate: false,
Expand All @@ -232,7 +199,7 @@ const ProfileSettingsComponent: FC<ProfileSettingsComponentProps> = ({ profile:
}, [])

const handleEditBanner = () => {
setFieldType('banner')
setFieldType('bannerImage')
handlePresentModalPress()
}

Expand Down
2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@baseapp-frontend/components",
"description": "BaseApp components modules such as comments, notifications, messages, and more.",
"version": "1.0.28",
"version": "1.0.29",
"sideEffects": false,
"scripts": {
"babel:transpile": "babel modules -d tmp-babel --extensions .ts,.tsx --ignore '**/__tests__/**','**/__storybook__/**'",
Expand Down
6 changes: 6 additions & 0 deletions packages/design-system/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @baseapp-frontend/design-system

## 1.0.13

### Patch Changes

- Added `useLogoOverride` for native

## 1.0.12

### Patch Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import {
createContainedStyles,
createOutlinedStyles,
createSoftStyles,
createTextStyles,
largeSizeStyles,
mediumSizeStyles,
smallSizeStyles,
textStyles,
} from './styles'
import { ButtonProps, StyleSheetSchema } from './types'

Expand All @@ -33,7 +33,7 @@ const Button: FC<ButtonProps> = ({

const buttonVariant = {
contained: createContainedStyles(theme, { variant: color, disabled }),
text: textStyles,
text: createTextStyles(theme, { variant: color, disabled }),
outlined: createOutlinedStyles(theme, { variant: color, disabled }),
soft: createSoftStyles(theme, { variant: color, disabled }),
} as const
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,17 @@ export const createContainedStyles = (
})
}

export const textStyles = StyleSheet.create(baseButtonStyles)
export const createTextStyles = ({ colors }: Theme, { disabled, variant }: ButtonStylesOptions) => {
const textColor = variant === 'inherit' ? colors.object.high : colors[variant].main

return StyleSheet.create({
...baseButtonStyles,
text: {
...baseButtonStyles.text,
color: disabled ? colors.object.disabled : textColor,
},
})
}

export const createOutlinedStyles = (
{ colors }: Theme,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type ButtonColor = ColorVariant | 'inherit'
export type ButtonStylesOptions = {
variant: ButtonColor
disabled?: boolean
mode?: ButtonMode
}

export type StyleSheetSchema = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { FC } from 'react'

import Svg, { Path } from 'react-native-svg'

import { useTheme } from '../../../../providers/native'
import { SvgIconProps } from '../types'

const VectorEditIcon: FC<SvgIconProps> = ({
isActive = false,
color,
width = '20',
height = '19',
...props
}) => {
const { colors } = useTheme()

const defaultColor = color ?? colors.object.high
const svgColor = isActive ? colors.primary.high : defaultColor

return (
<Svg width={width} height={height} viewBox="0 0 20 19" color={svgColor} fill="none" {...props}>
<Path
opacity="0.48"
d="M14.3117 15.4544C13.6885 14.8313 13.2638 14.0376 13.0911 13.1734L12.555 10.2527C12.4875 10.2358 10.3223 9.80078 10.3223 9.80078L10.7278 11.8298C10.9006 12.6939 11.3253 13.4876 11.9484 14.1108L15.2108 17.3732L15.7206 16.8633L14.3117 15.4544Z"
fill="currentColor"
/>
<Path
d="M7.17126 1.74479H5.00707C4.71141 0.91127 3.91541 0.3125 2.98181 0.3125C1.79716 0.3125 0.833374 1.27629 0.833374 2.46094C0.833374 3.39454 1.43214 4.19054 2.26567 4.4862V6.61458C2.26567 7.01011 2.58628 7.33073 2.98181 7.33073C3.37734 7.33073 3.69796 7.01011 3.69796 6.61458V4.4862C4.30733 4.27007 4.79094 3.78649 5.00707 3.17708H7.17126C7.56679 3.17708 7.88741 2.85646 7.88741 2.46094C7.88741 2.06541 7.56679 1.74479 7.17126 1.74479ZM2.98181 3.17708C2.58693 3.17708 2.26567 2.85582 2.26567 2.46094C2.26567 2.06605 2.58693 1.74479 2.98181 1.74479C3.37669 1.74479 3.69796 2.06605 3.69796 2.46094C3.69796 2.85582 3.37669 3.17708 2.98181 3.17708ZM12.793 3.17708H14.993C15.2091 3.78645 15.6927 4.27007 16.3021 4.4862V6.61458C16.3021 7.01011 16.6227 7.33073 17.0183 7.33073C17.4138 7.33073 17.7344 7.01011 17.7344 6.61458V4.4862C18.5679 4.19057 19.1667 3.39454 19.1667 2.46094C19.1667 1.27629 18.2029 0.3125 17.0183 0.3125C16.0847 0.3125 15.2887 0.91127 14.993 1.74479H12.793C12.3975 1.74479 12.0769 2.06541 12.0769 2.46094C12.0769 2.85646 12.3975 3.17708 12.793 3.17708ZM17.0183 1.74479C17.4132 1.74479 17.7344 2.06605 17.7344 2.46094C17.7344 2.85582 17.4132 3.17708 17.0183 3.17708C16.6234 3.17708 16.3021 2.85582 16.3021 2.46094C16.3021 2.06605 16.6234 1.74479 17.0183 1.74479ZM7.17126 15.7813H5.00707C4.79094 15.1719 4.30736 14.6883 3.69796 14.4721V12.3079C3.69796 11.9124 3.37734 11.5918 2.98181 11.5918C2.58628 11.5918 2.26567 11.9124 2.26567 12.3079V14.4721C1.43214 14.7678 0.833374 15.5638 0.833374 16.4974C0.833374 17.682 1.79716 18.6458 2.98181 18.6458C3.91541 18.6458 4.71141 18.0471 5.00707 17.2135H7.17126C7.56679 17.2135 7.88741 16.8929 7.88741 16.4974C7.88741 16.1019 7.56679 15.7813 7.17126 15.7813ZM2.98181 17.2135C2.58693 17.2135 2.26567 16.8923 2.26567 16.4974C2.26567 16.1025 2.58693 15.7813 2.98181 15.7813C3.37669 15.7813 3.69796 16.1025 3.69796 16.4974C3.69796 16.8923 3.37669 17.2135 2.98181 17.2135Z"
fill="currentColor"
/>
<Path
d="M15.1388 10.9218C14.4132 10.1962 13.4978 9.70647 12.4917 9.50534L10.4627 9.09978C10.228 9.05277 9.98529 9.12632 9.81596 9.29565C9.64667 9.46498 9.57319 9.70765 9.6201 9.94244L10.0256 11.9714C10.2268 12.9776 10.7166 13.8929 11.4421 14.6185L14.7045 17.8808C15.1982 18.3746 15.8546 18.6465 16.5528 18.6465C17.251 18.6465 17.9074 18.3746 18.4012 17.8808C18.8949 17.3871 19.1668 16.7307 19.1668 16.0325C19.1668 15.3343 18.8949 14.6779 18.4012 14.1842L15.1388 10.9218ZM17.3883 16.8681C17.1652 17.0913 16.8684 17.2142 16.5528 17.2142C16.2372 17.2142 15.9404 17.0913 15.7173 16.8681L12.4549 13.6057C11.93 13.0808 11.5757 12.4186 11.4302 11.6907L11.2351 10.7149L12.211 10.9099C12.9389 11.0554 13.6011 11.4097 14.126 11.9346L17.3884 15.197C17.6115 15.4202 17.7345 15.7169 17.7345 16.0325C17.7345 16.3481 17.6115 16.6449 17.3883 16.8681Z"
fill="currentColor"
/>
<Path
d="M10.8595 2.46159C10.8595 2.95598 10.4587 3.35677 9.96427 3.35677C9.46988 3.35677 9.06909 2.95598 9.06909 2.46159C9.06909 1.9672 9.46988 1.56641 9.96427 1.56641C10.4587 1.56641 10.8595 1.9672 10.8595 2.46159ZM9.96427 15.6029C9.46988 15.6029 9.06909 16.0037 9.06909 16.498C9.06909 16.9924 9.46988 17.3932 9.96427 17.3932C10.4587 17.3932 10.8595 16.9924 10.8595 16.498C10.8595 16.0037 10.4587 15.6029 9.96427 15.6029ZM17.0183 8.65625C16.5239 8.65625 16.1231 9.05704 16.1231 9.55143C16.1231 10.0458 16.5239 10.4466 17.0183 10.4466C17.5127 10.4466 17.9135 10.0458 17.9135 9.55143C17.9135 9.05704 17.5127 8.65625 17.0183 8.65625ZM2.98185 8.65625C2.48746 8.65625 2.08667 9.05704 2.08667 9.55143C2.08667 10.0458 2.48746 10.4466 2.98185 10.4466C3.47624 10.4466 3.87703 10.0458 3.87703 9.55143C3.87703 9.05704 3.47624 8.65625 2.98185 8.65625Z"
fill="currentColor"
/>
</Svg>
)
}

export default VectorEditIcon
1 change: 1 addition & 0 deletions packages/design-system/components/native/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ export { default as SecurityIcon } from './SecurityIcon'
export { default as SettingsIcon } from './SettingsIcon'
export { default as ShareIcon } from './ShareIcon'
export { default as TrashIcon } from './TrashIcon'
export { default as VectorEditIcon } from './VectorEditIcon'
export type * from './types'
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import Image from 'next/image'

import { useLogoOverrides } from '../../../../hooks/web'

// TODO: move this to @baseapp-frontend/design-system/components/web/logos
const CustomLogoCondensed: FC = () => {
const { logos } = useLogoOverrides()
if (!logos?.square) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { Box, Link } from '@mui/material'

import { LogoProps } from './types'

// TODO: move this to @baseapp-frontend/design-system/components/web/logos
const Logo = forwardRef<HTMLDivElement, LogoProps>(
({ children, disabledLink = false, sx, ...other }, ref) => {
const logo = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Image from 'next/image'
import { useLogoOverrides } from '../../../../hooks/web'
import { ProjectLogoProps } from './types'

// TODO: move this to @baseapp-frontend/design-system/components/web/logos
const ProjectLogo: FC<ProjectLogoProps> = ({ src, alt, width, height, priority, className }) => {
const { logos } = useLogoOverrides()
const { isSSR } = useSSR()
Expand Down
6 changes: 6 additions & 0 deletions packages/design-system/hooks/native/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// exports native design-system hooks

export { default as useColorMode } from './useColorMode'

export { default as useLogoOverrides } from './useLogoOverrides'
export type { LogoOverrides, LogoOverridesKeys } from './useLogoOverrides/types'

export { default as useViewPhotoLibrary } from './useViewPhotoLibrary'
export type { ViewPhotoLibraryProps } from './useViewPhotoLibrary/types'
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { LogoOverrides } from './types'

export const DEFAULT_LOGO_KEY = 'logo-overrides'

export const DEFAULT_LOGO_SETTINGS: LogoOverrides = {
default: undefined,
square: undefined,
}
36 changes: 36 additions & 0 deletions packages/design-system/hooks/native/useLogoOverrides/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client'

import AsyncStorage from '@react-native-async-storage/async-storage'
import { useAtom } from 'jotai'
import { atomWithStorage, createJSONStorage } from 'jotai/utils'

import { DEFAULT_LOGO_KEY, DEFAULT_LOGO_SETTINGS } from './constants'
import { LogoOverrides } from './types'

const asyncStorageSync = createJSONStorage<LogoOverrides>(() => AsyncStorage)

const logosAtom = atomWithStorage<LogoOverrides>(
DEFAULT_LOGO_KEY,
DEFAULT_LOGO_SETTINGS,
asyncStorageSync,
{ getOnInit: true },
)

const useLogoOverrides = () => {
const [logos, setLogos] = useAtom(logosAtom)

const handleSetLogoOverrides = (newLogos: Partial<LogoOverrides>) => {
setLogos(async (prev: LogoOverrides | Promise<LogoOverrides>) => {
const prevLogos = await prev
const updatedLogoOverrides = { ...prevLogos, ...newLogos }
return updatedLogoOverrides
})
}

return {
logos,
setLogos: handleSetLogoOverrides,
}
}

export default useLogoOverrides
21 changes: 21 additions & 0 deletions packages/design-system/hooks/native/useLogoOverrides/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export type LogoOverrideObject = {
base64: string
mimeType: string
}

export type LogoOverrides = {
default?: LogoOverrideObject
square?: LogoOverrideObject
}

export type LogoOverridesKeys = keyof LogoOverrides

export type LogoOverrideState = {
settings: LogoOverrides
}

type LogoOverrideFunctions = {
setLogoOverride: (newLogos: Partial<LogoOverrides>) => void
}

export type UseLogoOverrides = LogoOverrideState & LogoOverrideFunctions
Loading
Loading