Browse Source

feature/copy-user-info (#12)

Reviewed-on: #12
Co-authored-by: YaroslavBerkuta <yaroslavberkuta@gmail.com>
Co-committed-by: YaroslavBerkuta <yaroslavberkuta@gmail.com>
pull/16/head
YaroslavBerkuta 9 months ago committed by Vitalik Yatsenko
parent
commit
af6cd225d1
  1. BIN
      android/app/src/main/assets/fonts/fontello.ttf
  2. 4
      ios/taskme2.xcodeproj/project.pbxproj
  3. BIN
      src/assets/fonts/fontello.ttf
  4. 14
      src/config/fontello.json
  5. 3
      src/modules/account/components/change-date-of-birthday-moda.component.tsx
  6. 5
      src/modules/account/components/fake-date-input-with-modal.component.tsx
  7. 4
      src/modules/account/hooks/use-account-editor.hook.ts
  8. 13
      src/modules/contacts/enums/contact-field-key.enum.ts
  9. 3
      src/modules/contacts/enums/index.ts
  10. 1
      src/modules/contacts/hooks/index.ts
  11. 49
      src/modules/contacts/hooks/use-contact-copy.hook.ts
  12. 14
      src/modules/contacts/hooks/use-contact-detail.hook.ts
  13. 5
      src/modules/contacts/screens/contact-detail.screen.tsx
  14. 73
      src/shared/components/elements/icon.component.tsx
  15. 26
      src/shared/components/forms/form-text-input-with-icon.component.tsx
  16. 26
      src/shared/components/forms/touchable-fake-input.atom.tsx
  17. 28
      src/shared/components/texts/avatar-title.component.tsx
  18. 11
      src/shared/helpers/copy-to-buffer.helper.ts
  19. 1
      src/shared/helpers/index.ts
  20. 20
      src/shared/interfaces/contact.interfaces.ts

BIN
android/app/src/main/assets/fonts/fontello.ttf

Binary file not shown.

4
ios/taskme2.xcodeproj/project.pbxproj

@ -909,7 +909,7 @@ @@ -909,7 +909,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2.entitlements;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
DEVELOPMENT_TEAM = VCUZPZ9254;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = taskme2/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Task me ;)";
@ -946,7 +946,7 @@ @@ -946,7 +946,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2.entitlements;
CURRENT_PROJECT_VERSION = 9;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
DEVELOPMENT_TEAM = VCUZPZ9254;
INFOPLIST_FILE = taskme2/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Task me ;)";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";

BIN
src/assets/fonts/fontello.ttf

Binary file not shown.

14
src/config/fontello.json

@ -1041,6 +1041,20 @@ @@ -1041,6 +1041,20 @@
"search": [
"zip"
]
},
{
"uid": "cbc6e7a91680faee971b2dd7dc856852",
"css": "copy",
"code": 59466,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M272.7 127C263.9 129.4 254.3 139.3 252 148.5 248.8 161.3 253 172.4 264.4 181.1L270.6 185.8 542 186.3 813.3 186.7 813.7 458.2 814.2 729.7 819.3 736.1C826.5 745 833.2 748.5 843.1 748.6 854.4 748.7 864 743.2 869.7 733.3L874.2 725.8V149.2L870.5 142.2A29.4 29.4 0 0 0 850.8 126.9C841.8 124.4 282.1 124.5 272.7 127M147.7 252C137.9 254.7 129 264.5 126.7 275.3 125.3 281.8 125 358.8 125.3 567.5L125.8 850.8 130.3 858.3A33.6 33.6 0 0 0 141.9 870L149.2 874.2H725.8L733.1 870A33.6 33.6 0 0 0 744.8 858.3L749.2 850.8V274.2L745.5 267.2A29.4 29.4 0 0 0 725.8 251.9C716.8 249.4 157.1 249.5 147.7 252M688.3 562.5V813.3H186.7V311.7H688.3V562.5",
"width": 1000
},
"search": [
"copy"
]
}
]
}

3
src/modules/account/components/change-date-of-birthday-moda.component.tsx

@ -2,7 +2,6 @@ import React, { FC, useEffect, useState } from 'react' @@ -2,7 +2,6 @@ import React, { FC, useEffect, useState } from 'react'
import { $size, BottomModal, Button } from '@/shared'
import RBSheet from 'react-native-raw-bottom-sheet'
import { Platform, StyleSheet, View } from 'react-native'
import { PartialTheme } from '@/shared/themes/interfaces'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import DatePicker from 'react-native-date-picker'
@ -53,7 +52,7 @@ export const ChangeDateOfBirthdayModal: FC<IProps> = ({ @@ -53,7 +52,7 @@ export const ChangeDateOfBirthdayModal: FC<IProps> = ({
)
}
const createStyles = (theme: PartialTheme) =>
const createStyles = () =>
StyleSheet.create({
container: {
width: '100%',

5
src/modules/account/components/fake-date-input-with-modal.component.tsx

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
import { $size, TouchableFakeInput } from '@/shared'
import { TouchableFakeInput } from '@/shared'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { PartialTheme } from '@/shared/themes/interfaces'
import moment from 'moment'
import 'moment/locale/uk'
import React, { FC, useRef } from 'react'
@ -52,7 +51,7 @@ export const FakeDateInputWithModal: FC<IProps> = ({ @@ -52,7 +51,7 @@ export const FakeDateInputWithModal: FC<IProps> = ({
)
}
const createStyles = (theme: PartialTheme) =>
const createStyles = () =>
StyleSheet.create({
container: {
width: '100%',

4
src/modules/account/hooks/use-account-editor.hook.ts

@ -128,7 +128,9 @@ export const useAccountEditor = () => { @@ -128,7 +128,9 @@ export const useAccountEditor = () => {
onPressOk: () => {},
})
} catch (e: any) {
const message = e.response?.data?.key ? getMessageByExceptionKey(e.response?.data?.key) : 'Спробуйте будь-ласка пізніше.'
const message = e.response?.data?.key
? getMessageByExceptionKey(e.response?.data?.key)
: 'Спробуйте будь-ласка пізніше.'
appEvents.emit('openInfoModal', {
title: 'Сталась помилка!',
message,

13
src/modules/contacts/enums/contact-field-key.enum.ts

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
export enum ContactFieldKeyEnum {
firstName = "Ім'я",
lastName = 'Фамілія',
fullName = "Повне ім'я",
position = 'Посада',
factoryName = 'Місце роботи',
avatarUrl = 'Фото профілю',
dateOfBirthday = 'День народження',
workPhoneNumber = 'Робочий номер телефону',
personalPhoneNumber = 'Персональний номер',
innerPhoneNumber = 'Внутрішній номер телефону',
email = 'Електрона пошта',
}

3
src/modules/contacts/enums/index.ts

@ -1 +1,2 @@ @@ -1 +1,2 @@
export * from './contact-detail.enums'
export * from './contact-detail.enums'
export * from './contact-field-key.enum'

1
src/modules/contacts/hooks/index.ts

@ -1,2 +1,3 @@ @@ -1,2 +1,3 @@
export * from './use-fetch-contacts.hook'
export * from './use-contact-detail.hook'
export * from './use-contact-copy.hook'

49
src/modules/contacts/hooks/use-contact-copy.hook.ts

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
import { IContacForCopy, appEvents } from '@/shared'
import { CopyToBuffer } from '@/shared/helpers'
import * as _ from 'lodash'
import { ContactFieldKeyEnum } from '../enums'
export const useContactCopy = (contact: IContacForCopy) => {
const formatContactForCopy = (contacData: IContacForCopy): string => {
const formatData = _.omit(contacData, [
'userId',
'isSoonBirthday',
'chatId',
'status',
])
let resultString = ''
for (const key in formatData) {
resultString += `${ContactFieldKeyEnum[key]}: ${formatData[key]}\n`
}
return resultString
}
const actionCopy = () => {
appEvents.emit('openActionSheet', {
items: [
{
name: "Скопіювати І'мя та Прізвище",
onPress: () => CopyToBuffer(`${contact.fullName}`),
},
{
name: 'Скопіювати Посаду',
onPress: () =>
CopyToBuffer(
`${contact.factoryName}, ${contact.position}`,
),
},
{
name: 'Скопіювати всі данні користувача',
onPress: () => CopyToBuffer(formatContactForCopy(contact)),
},
],
})
}
return {
actionCopy,
}
}

14
src/modules/contacts/hooks/use-contact-detail.hook.ts

@ -35,15 +35,19 @@ export const useContactDetail = (id: number) => { @@ -35,15 +35,19 @@ export const useContactDetail = (id: number) => {
if (contactDetail) {
const { info: contactDetailInfo } = contactDetail
const dateToRender = contactDetailInfo?.dateOfBirth ? moment(contactDetailInfo?.dateOfBirth).format(
'DD MMMM',
) : ''
const dateToRender = contactDetailInfo?.dateOfBirth
? moment(contactDetailInfo?.dateOfBirth).format('DD MMMM')
: ''
return {
userId: contactDetailInfo?.userId,
firstName: contactDetailInfo.firstName,
lastName: contactDetailInfo?.lastName,
fullName: createContactName(contactDetailInfo.firstName, contactDetailInfo.middleName, contactDetailInfo.lastName),
fullName: createContactName(
contactDetailInfo.firstName,
contactDetailInfo.middleName,
contactDetailInfo.lastName,
),
position: contactDetailInfo?.position,
factoryName: contactDetail?.factoryName,
avatarUrl: hasImageUrl(
@ -57,7 +61,7 @@ export const useContactDetail = (id: number) => { @@ -57,7 +61,7 @@ export const useContactDetail = (id: number) => {
isSoonBirthday: isSoonBirthday(contactDetailInfo?.dateOfBirth),
email: contactDetail?.email,
chatId: contactDetail?.chatId,
status: contactDetail?.status
status: contactDetail?.status,
}
}
}, [contactDetail])

5
src/modules/contacts/screens/contact-detail.screen.tsx

@ -14,7 +14,7 @@ import { ContactsSpeakings } from '../components' @@ -14,7 +14,7 @@ import { ContactsSpeakings } from '../components'
import { BirthdayIndicator } from '../atoms'
import { PartialTheme } from '@/shared/themes/interfaces'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { useContactDetail } from '../hooks'
import { useContactCopy, useContactDetail } from '../hooks'
import { ContactDetailFieldType } from '../enums'
import { useSelector } from 'react-redux'
import { selectId } from '@/store/account'
@ -35,6 +35,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => { @@ -35,6 +35,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => {
const { contactId } = route.params
const { contact, isLoading, infoFieldsConfig, onPress } =
useContactDetail(contactId)
const { actionCopy } = useContactCopy(contact)
const onPressMessage = async () => {
try {
@ -74,6 +75,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => { @@ -74,6 +75,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => {
if (contact?.status !== EUserStatus.Deleted && it.value)
onPress(type, it.value)
}}
needCopy
/>
)),
[contact],
@ -115,6 +117,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => { @@ -115,6 +117,7 @@ export const ContactDetailScreen: FC<IProps> = ({ navigation, route }) => {
indicator={avatarBirthdayIndicator}
containerStyle={styles.avatarImageWrap}
style={styles.avatarContainer}
onPressHeader={actionCopy}
/>
{contact?.status !== EUserStatus.Deleted && (

73
src/shared/components/elements/icon.component.tsx

@ -1,45 +1,38 @@ @@ -1,45 +1,38 @@
import React from 'react';
import { createIconSetFromFontello } from 'react-native-vector-icons';
import { fontelloConfig } from '@/config/fontello.config';
import { StyleSheet, TouchableOpacity, ViewStyle } from 'react-native';
const Icon = createIconSetFromFontello(fontelloConfig);
import React from 'react'
import { createIconSetFromFontello } from 'react-native-vector-icons'
import { fontelloConfig } from '@/config/fontello.config'
import { TouchableOpacity, ViewStyle } from 'react-native'
const Icon = createIconSetFromFontello(fontelloConfig)
interface IProps {
name: string
size: number
color?: string
style?: any
onPress?: () => void
btnStyle?: ViewStyle
name: string
size: number
color?: string
style?: any
onPress?: () => void
btnStyle?: ViewStyle
}
export const IconComponent = ({
onPress,
...props
}: IProps) => {
if (onPress) {
return (
<TouchableOpacity onPress={onPress} style={[ props.btnStyle]}>
<Icon
name={props.name}
size={props.size}
color={props.color}
style={props.style}
/>
</TouchableOpacity>
)
} else {
return (
<Icon
name={props.name}
size={props.size}
color={props.color}
style={props.style}
/>
)
}
export const IconComponent = ({ onPress, ...props }: IProps) => {
if (onPress) {
return (
<TouchableOpacity onPress={onPress} style={[props.btnStyle]}>
<Icon
name={props.name}
size={props.size}
color={props.color}
style={props.style}
/>
</TouchableOpacity>
)
} else {
return (
<Icon
name={props.name}
size={props.size}
color={props.color}
style={props.style}
/>
)
}
}
const styles = StyleSheet.create({
})

26
src/shared/components/forms/form-text-input-with-icon.component.tsx

@ -1,5 +1,10 @@ @@ -1,5 +1,10 @@
import React, { FC } from 'react'
import { $size, callPhoneNumber, sendEmail } from '@/shared/helpers'
import {
$size,
CopyToBuffer,
callPhoneNumber,
sendEmail,
} from '@/shared/helpers'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { PartialTheme } from '@/shared/themes/interfaces'
import {
@ -26,6 +31,7 @@ interface IProps { @@ -26,6 +31,7 @@ interface IProps {
keybordType?: KeyboardTypeOptions
inputProps?: TextInputProps
error?: string
needCopy?: boolean
}
export const TextInputWithIcon: FC<IProps> = ({
@ -40,6 +46,7 @@ export const TextInputWithIcon: FC<IProps> = ({ @@ -40,6 +46,7 @@ export const TextInputWithIcon: FC<IProps> = ({
keybordType,
inputProps = {},
error,
needCopy = false,
}) => {
const isDefaultType = type === 'default'
const viewPointerEventType = isDefaultType ? 'auto' : 'none'
@ -59,6 +66,14 @@ export const TextInputWithIcon: FC<IProps> = ({ @@ -59,6 +66,14 @@ export const TextInputWithIcon: FC<IProps> = ({
<View
pointerEvents={viewPointerEventType}
style={onCls(Boolean(error), 'inputWrap')}>
{needCopy && (
<IconComponent
name={iconName}
size={$size(20, 18)}
style={styles.iconLeft}
color={theme.iconComponent.$primaryColor}
/>
)}
<TextInput
editable={editable}
style={styles.input}
@ -68,12 +83,12 @@ export const TextInputWithIcon: FC<IProps> = ({ @@ -68,12 +83,12 @@ export const TextInputWithIcon: FC<IProps> = ({
keyboardType={keybordType}
{...inputProps}
/>
<IconComponent
name={iconName}
name={needCopy ? 'copy' : iconName}
size={$size(20, 18)}
style={styles.icon}
btnStyle={styles.icon}
color={theme.iconComponent.$primaryColor}
onPress={() => CopyToBuffer(value)}
/>
</View>
</Pressable>
@ -123,4 +138,7 @@ const createStyles = (theme: PartialTheme) => @@ -123,4 +138,7 @@ const createStyles = (theme: PartialTheme) =>
color: theme.$errorText,
fontSize: $size(12),
},
iconLeft: {
marginLeft: $size(15),
},
})

26
src/shared/components/forms/touchable-fake-input.atom.tsx

@ -11,7 +11,7 @@ import { @@ -11,7 +11,7 @@ import {
ViewStyle,
} from 'react-native'
import { IconComponent, Txt } from '../elements'
import { $size } from '@/shared/helpers'
import { $size, CopyToBuffer } from '@/shared/helpers'
interface IProps {
label: string
@ -22,6 +22,7 @@ interface IProps { @@ -22,6 +22,7 @@ interface IProps {
txtStyle?: TextStyle
error?: string
disabled?: boolean
needCopy?: boolean
}
export const TouchableFakeInput: FC<IProps> = ({
@ -33,6 +34,7 @@ export const TouchableFakeInput: FC<IProps> = ({ @@ -33,6 +34,7 @@ export const TouchableFakeInput: FC<IProps> = ({
txtStyle,
disabled,
error,
needCopy = false,
}) => {
const { styles, theme, onCls } = useTheme(createStyles)
@ -44,13 +46,22 @@ export const TouchableFakeInput: FC<IProps> = ({ @@ -44,13 +46,22 @@ export const TouchableFakeInput: FC<IProps> = ({
disabled={disabled}
style={onCls(Boolean(error), 'inputWrap')}
onPress={onPress}>
{needCopy && (
<IconComponent
name={iconName}
size={$size(20, 18)}
style={styles.iconLeft}
color={theme.iconComponent.$primaryColor}
/>
)}
<Text style={[styles.value, txtStyle]}>{value}</Text>
<IconComponent
name={iconName}
name={needCopy ? 'copy' : iconName}
size={$size(20, 18)}
style={styles.icon}
btnStyle={styles.icon}
color={theme.iconComponent.$primaryColor}
onPress={() => CopyToBuffer(value)}
/>
</TouchableOpacity>
@ -66,9 +77,8 @@ const createStyles = (theme: PartialTheme) => @@ -66,9 +77,8 @@ const createStyles = (theme: PartialTheme) =>
marginBottom: $size(10, 8),
},
inputWrap: {
alignItems: 'flex-start',
justifyContent: 'center',
// flexDirection: 'row',
alignItems: 'center',
flexDirection: 'row',
backgroundColor: theme.input.$bg,
borderRadius: 10,
@ -101,4 +111,8 @@ const createStyles = (theme: PartialTheme) => @@ -101,4 +111,8 @@ const createStyles = (theme: PartialTheme) =>
color: theme.$errorText,
fontSize: $size(12),
},
iconLeft: {
marginTop: $size(-5),
paddingRight: $size(15),
},
})

28
src/shared/components/texts/avatar-title.component.tsx

@ -2,7 +2,13 @@ import React, { FC } from 'react' @@ -2,7 +2,13 @@ import React, { FC } from 'react'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { PartialTheme } from '@/shared/themes/interfaces'
import { StyleSheet, TextStyle, View, ViewStyle } from 'react-native'
import {
StyleSheet,
TextStyle,
TouchableOpacity,
View,
ViewStyle,
} from 'react-native'
import { Avatar, Txt } from '../elements'
import { $size } from '@/shared/helpers'
@ -18,6 +24,7 @@ interface IProps { @@ -18,6 +24,7 @@ interface IProps {
indicator?: () => JSX.Element
containerStyle?: ViewStyle
style?: ViewStyle
onPressHeader: () => void
}
export const AvatarTitle: FC<IProps> = ({
@ -32,6 +39,7 @@ export const AvatarTitle: FC<IProps> = ({ @@ -32,6 +39,7 @@ export const AvatarTitle: FC<IProps> = ({
indicator,
containerStyle,
style,
onPressHeader,
}) => {
const { styles } = useTheme(createStyles)
@ -49,13 +57,19 @@ export const AvatarTitle: FC<IProps> = ({ @@ -49,13 +57,19 @@ export const AvatarTitle: FC<IProps> = ({
{indicator ? indicator() : null}
</View>
<Txt numberOfLines={2} style={[styles.title, textStyle]}>
{text}
</Txt>
<TouchableOpacity
onPress={onPressHeader}
style={{ alignItems: 'center' }}>
<Txt numberOfLines={2} style={[styles.title, textStyle]}>
{text}
</Txt>
{subTitle ? (
<Txt style={[styles.subTitle, subTitleStyle]}>{subTitle}</Txt>
) : null}
{subTitle ? (
<Txt style={[styles.subTitle, subTitleStyle]}>
{subTitle}
</Txt>
) : null}
</TouchableOpacity>
</View>
)
}

11
src/shared/helpers/copy-to-buffer.helper.ts

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
import Clipboard from '@react-native-community/clipboard'
import { appEvents } from '../events'
export const CopyToBuffer = (value: string) => {
Clipboard.setString(value)
appEvents.emit('openInfoModal', {
title: '',
message: 'Данні скопійовано в буфер обміну',
pressButtonText: 'Продовжити',
})
}

1
src/shared/helpers/index.ts

@ -20,3 +20,4 @@ export * from './transforms.helpers' @@ -20,3 +20,4 @@ export * from './transforms.helpers'
export * from './url.helpers'
export * from './versions.helper'
export * from './configs.helpers'
export * from './copy-to-buffer.helper'

20
src/shared/interfaces/contact.interfaces.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { EUserStatus } from "../enums"
import { EUserStatus } from '../enums'
export interface IContact {
userId: number
@ -28,3 +28,21 @@ export interface IContactDetail { @@ -28,3 +28,21 @@ export interface IContactDetail {
innerPhoneNumber?: string
}
}
export interface IContacForCopy {
userId?: number
firstName: string
lastName: string
fullName: string
position: string
factoryName: string
avatarUrl: string
dateOfBirthday: string
workPhoneNumber: string
personalPhoneNumber: string
innerPhoneNumber: string
isSoonBirthday?: boolean
email: string
chatId?: number
status?: string
}

Loading…
Cancel
Save