From ba8a3c100bdd759d7ac318a340b1893055061f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Mon, 4 Sep 2023 19:01:36 +0100 Subject: [PATCH 001/205] First version of styles refactor --- src/styles/addOutlineWidth/types.ts | 3 +- src/styles/codeStyles/types.ts | 9 +- src/styles/editedLabelStyles/index.ts | 8 +- src/styles/editedLabelStyles/types.ts | 3 +- src/styles/getModalStyles.ts | 9 +- .../getNavigationModalCardStyles/index.ts | 6 +- .../index.website.ts | 6 +- .../getNavigationModalCardStyles/types.ts | 3 +- .../getReportActionContextMenuStyles.ts | 11 +- src/styles/italic/types.ts | 3 +- src/styles/optionRowStyles/types.ts | 3 +- src/styles/overflowXHidden/types.ts | 4 +- src/styles/pointerEventsAuto/types.ts | 4 +- src/styles/pointerEventsNone/types.ts | 4 +- src/styles/utilities/cursor/types.ts | 4 +- src/styles/utilities/overflow.ts | 4 +- src/styles/utilities/overflowAuto/index.ts | 5 +- src/styles/utilities/overflowAuto/types.ts | 4 +- src/styles/utilities/textUnderline/types.ts | 6 +- src/styles/utilities/userSelect/types.ts | 4 +- src/styles/utilities/visibility/types.ts | 4 +- src/styles/utilities/whiteSpace/types.ts | 4 +- src/styles/utilities/wordBreak/types.ts | 4 +- src/types/modules/react-native.d.ts | 148 ++++++++++++++++++ 24 files changed, 210 insertions(+), 53 deletions(-) create mode 100644 src/types/modules/react-native.d.ts diff --git a/src/styles/addOutlineWidth/types.ts b/src/styles/addOutlineWidth/types.ts index 422d135ef759..fe5d1f5719b2 100644 --- a/src/styles/addOutlineWidth/types.ts +++ b/src/styles/addOutlineWidth/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type AddOutlineWidth = (obj: TextStyle | CSSProperties, val?: number, error?: boolean) => TextStyle | CSSProperties; +type AddOutlineWidth = (obj: TextStyle, val?: number, error?: boolean) => TextStyle; export default AddOutlineWidth; diff --git a/src/styles/codeStyles/types.ts b/src/styles/codeStyles/types.ts index 7397b3c6543c..25e35f492089 100644 --- a/src/styles/codeStyles/types.ts +++ b/src/styles/codeStyles/types.ts @@ -1,8 +1,7 @@ -import {CSSProperties} from 'react'; import {TextStyle, ViewStyle} from 'react-native'; -type CodeWordWrapperStyles = ViewStyle | CSSProperties; -type CodeWordStyles = TextStyle | CSSProperties; -type CodeTextStyles = TextStyle | CSSProperties; +type CodeWordWrapperStyles = ViewStyle; +type CodeWordStyles = TextStyle; +type CodeTextStyles = TextStyle; -export type {CodeWordWrapperStyles, CodeWordStyles, CodeTextStyles}; +export type {CodeTextStyles, CodeWordStyles, CodeWordWrapperStyles}; diff --git a/src/styles/editedLabelStyles/index.ts b/src/styles/editedLabelStyles/index.ts index 6e6164810b52..b3962e507757 100644 --- a/src/styles/editedLabelStyles/index.ts +++ b/src/styles/editedLabelStyles/index.ts @@ -1,7 +1,11 @@ -import EditedLabelStyles from './types'; +import {TextStyle} from 'react-native'; import display from '../utilities/display'; import flex from '../utilities/flex'; +import EditedLabelStyles from './types'; -const editedLabelStyles: EditedLabelStyles = {...display.dInlineFlex, ...flex.alignItemsBaseline}; +const editedLabelStyles: EditedLabelStyles = { + ...(display.dInlineFlex as TextStyle), + ...flex.alignItemsBaseline, +}; export default editedLabelStyles; diff --git a/src/styles/editedLabelStyles/types.ts b/src/styles/editedLabelStyles/types.ts index 8d96c9216ed2..20bcc8c55f15 100644 --- a/src/styles/editedLabelStyles/types.ts +++ b/src/styles/editedLabelStyles/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type EditedLabelStyles = CSSProperties | TextStyle; +type EditedLabelStyles = TextStyle; export default EditedLabelStyles; diff --git a/src/styles/getModalStyles.ts b/src/styles/getModalStyles.ts index 32e6cf6f98e7..ceea37ddb85b 100644 --- a/src/styles/getModalStyles.ts +++ b/src/styles/getModalStyles.ts @@ -1,11 +1,10 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -import {ValueOf} from 'type-fest'; import {ModalProps} from 'react-native-modal'; +import {ValueOf} from 'type-fest'; import CONST from '../CONST'; -import variables from './variables'; -import themeColors from './themes/default'; import styles from './styles'; +import themeColors from './themes/default'; +import variables from './variables'; function getCenteredModalStyles(windowWidth: number, isSmallScreenWidth: boolean, isFullScreenWhenSmall = false): ViewStyle { return { @@ -26,7 +25,7 @@ type WindowDimensions = { type GetModalStyles = { modalStyle: ViewStyle; - modalContainerStyle: ViewStyle | Pick; + modalContainerStyle: ViewStyle; swipeDirection: ModalProps['swipeDirection']; animationIn: ModalProps['animationIn']; animationOut: ModalProps['animationOut']; diff --git a/src/styles/getNavigationModalCardStyles/index.ts b/src/styles/getNavigationModalCardStyles/index.ts index 8e2bffc1ecfc..d3cf49c6c89c 100644 --- a/src/styles/getNavigationModalCardStyles/index.ts +++ b/src/styles/getNavigationModalCardStyles/index.ts @@ -1,3 +1,7 @@ -export default () => ({ +import GetNavigationModalCardStyles from './types'; + +const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ height: '100%', }); + +export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.website.ts b/src/styles/getNavigationModalCardStyles/index.website.ts index 9879e4c1567b..ea76825e5bba 100644 --- a/src/styles/getNavigationModalCardStyles/index.website.ts +++ b/src/styles/getNavigationModalCardStyles/index.website.ts @@ -1,3 +1,4 @@ +import {ViewStyle} from 'react-native'; import GetNavigationModalCardStyles from './types'; const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ @@ -8,7 +9,10 @@ const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ // https://github.com/Expensify/App/issues/20709 width: '100%', height: '100%', - position: 'fixed', + + // NOTE: asserting "fixed" TS type to a valid type, because isn't possible + // to augment "position". + position: 'fixed' as ViewStyle['position'], }); export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/types.ts b/src/styles/getNavigationModalCardStyles/types.ts index 504b659c87b7..68453dc3c3de 100644 --- a/src/styles/getNavigationModalCardStyles/types.ts +++ b/src/styles/getNavigationModalCardStyles/types.ts @@ -1,9 +1,8 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; import {Merge} from 'type-fest'; type GetNavigationModalCardStylesParams = {isSmallScreenWidth: number}; -type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => Merge>; +type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => Merge>; export default GetNavigationModalCardStyles; diff --git a/src/styles/getReportActionContextMenuStyles.ts b/src/styles/getReportActionContextMenuStyles.ts index 17f0828ab80c..792213f2b54b 100644 --- a/src/styles/getReportActionContextMenuStyles.ts +++ b/src/styles/getReportActionContextMenuStyles.ts @@ -1,12 +1,11 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; import styles from './styles'; -import variables from './variables'; import themeColors from './themes/default'; +import variables from './variables'; -type StylesArray = Array; +type StylesArray = ViewStyle[]; -const defaultWrapperStyle: ViewStyle | CSSProperties = { +const defaultWrapperStyle: ViewStyle = { backgroundColor: themeColors.componentBG, }; @@ -18,7 +17,9 @@ const miniWrapperStyle: StylesArray = [ borderWidth: 1, borderColor: themeColors.border, // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style. - transform: 'translateZ(0)', + // NOTE: asserting "transform" TS type to a valid type, because isn't possible + // to augment "transform". + transform: 'translateZ(0)' as unknown as ViewStyle['transform'], }, ]; diff --git a/src/styles/italic/types.ts b/src/styles/italic/types.ts index 61e0328e52b6..0935c5844bb3 100644 --- a/src/styles/italic/types.ts +++ b/src/styles/italic/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type ItalicStyles = (TextStyle | CSSProperties)['fontStyle']; +type ItalicStyles = TextStyle['fontStyle']; export default ItalicStyles; diff --git a/src/styles/optionRowStyles/types.ts b/src/styles/optionRowStyles/types.ts index c08174470701..f645c6038397 100644 --- a/src/styles/optionRowStyles/types.ts +++ b/src/styles/optionRowStyles/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -type OptionRowStyles = CSSProperties | ViewStyle; +type OptionRowStyles = ViewStyle; export default OptionRowStyles; diff --git a/src/styles/overflowXHidden/types.ts b/src/styles/overflowXHidden/types.ts index 7ac572f0e651..0f13ba552c0c 100644 --- a/src/styles/overflowXHidden/types.ts +++ b/src/styles/overflowXHidden/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type OverflowXHiddenStyles = Partial>; +type OverflowXHiddenStyles = Pick; export default OverflowXHiddenStyles; diff --git a/src/styles/pointerEventsAuto/types.ts b/src/styles/pointerEventsAuto/types.ts index 7c9f0164c936..0ecbc851e000 100644 --- a/src/styles/pointerEventsAuto/types.ts +++ b/src/styles/pointerEventsAuto/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type PointerEventsAutoStyles = Partial>; +type PointerEventsAutoStyles = Pick; export default PointerEventsAutoStyles; diff --git a/src/styles/pointerEventsNone/types.ts b/src/styles/pointerEventsNone/types.ts index f2f5a0d88a41..766ac3f94349 100644 --- a/src/styles/pointerEventsNone/types.ts +++ b/src/styles/pointerEventsNone/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type PointerEventsNone = Partial>; +type PointerEventsNone = Pick; export default PointerEventsNone; diff --git a/src/styles/utilities/cursor/types.ts b/src/styles/utilities/cursor/types.ts index 9ca20c5ae123..98d661491c4b 100644 --- a/src/styles/utilities/cursor/types.ts +++ b/src/styles/utilities/cursor/types.ts @@ -1,4 +1,4 @@ -import {CSSProperties} from 'react'; +import {TextStyle, ViewStyle} from 'react-native'; type CursorStylesKeys = | 'cursorDefault' @@ -13,6 +13,6 @@ type CursorStylesKeys = | 'cursorInitial' | 'cursorText'; -type CursorStyles = Record>>; +type CursorStyles = Record>; export default CursorStyles; diff --git a/src/styles/utilities/overflow.ts b/src/styles/utilities/overflow.ts index 3961758e40bf..ac28df1c944f 100644 --- a/src/styles/utilities/overflow.ts +++ b/src/styles/utilities/overflow.ts @@ -1,4 +1,4 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; import overflowAuto from './overflowAuto'; /** @@ -24,4 +24,4 @@ export default { }, overflowAuto, -} satisfies Record; +} satisfies Record; diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utilities/overflowAuto/index.ts index 0eb19068738f..f958d8000c12 100644 --- a/src/styles/utilities/overflowAuto/index.ts +++ b/src/styles/utilities/overflowAuto/index.ts @@ -1,7 +1,10 @@ +import { ViewStyle } from 'react-native'; import OverflowAutoStyles from './types'; const overflowAuto: OverflowAutoStyles = { - overflow: 'auto', + // NOTE: asserting "auto" TS type to a valid type, because isn't possible + // to augment "overflow". + overflow: 'auto' as ViewStyle['overflow'], }; export default overflowAuto; diff --git a/src/styles/utilities/overflowAuto/types.ts b/src/styles/utilities/overflowAuto/types.ts index faba7c2cbdb8..da7548d49e7b 100644 --- a/src/styles/utilities/overflowAuto/types.ts +++ b/src/styles/utilities/overflowAuto/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {ViewStyle} from 'react-native'; -type OverflowAutoStyles = Pick; +type OverflowAutoStyles = Pick; export default OverflowAutoStyles; diff --git a/src/styles/utilities/textUnderline/types.ts b/src/styles/utilities/textUnderline/types.ts index ecc09ed0fe09..f71d2bfdaa9a 100644 --- a/src/styles/utilities/textUnderline/types.ts +++ b/src/styles/utilities/textUnderline/types.ts @@ -1,8 +1,8 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; type TextUnderlineStyles = { - textUnderlinePositionUnder: Pick; - textDecorationSkipInkNone: Pick; + textUnderlinePositionUnder: Pick; + textDecorationSkipInkNone: Pick; }; export default TextUnderlineStyles; diff --git a/src/styles/utilities/userSelect/types.ts b/src/styles/utilities/userSelect/types.ts index 67a8c9c7b9b6..a177bac5a3e7 100644 --- a/src/styles/utilities/userSelect/types.ts +++ b/src/styles/utilities/userSelect/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; -type UserSelectStyles = Record<'userSelectText' | 'userSelectNone', Partial>>; +type UserSelectStyles = Record<'userSelectText' | 'userSelectNone', Pick>; export default UserSelectStyles; diff --git a/src/styles/utilities/visibility/types.ts b/src/styles/utilities/visibility/types.ts index 9dab3d7c752e..872e35195edd 100644 --- a/src/styles/utilities/visibility/types.ts +++ b/src/styles/utilities/visibility/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle, ViewStyle} from 'react-native'; -type VisibilityStyles = Record<'visible' | 'hidden', Partial>>; +type VisibilityStyles = Record<'visible' | 'hidden', Pick>; export default VisibilityStyles; diff --git a/src/styles/utilities/whiteSpace/types.ts b/src/styles/utilities/whiteSpace/types.ts index c42dc14080b1..b10671f04977 100644 --- a/src/styles/utilities/whiteSpace/types.ts +++ b/src/styles/utilities/whiteSpace/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; -type WhiteSpaceStyles = Record<'noWrap' | 'preWrap' | 'pre', Partial>>; +type WhiteSpaceStyles = Record<'noWrap' | 'preWrap' | 'pre', Pick>; export default WhiteSpaceStyles; diff --git a/src/styles/utilities/wordBreak/types.ts b/src/styles/utilities/wordBreak/types.ts index 003a5ca1c869..0ed520ae119d 100644 --- a/src/styles/utilities/wordBreak/types.ts +++ b/src/styles/utilities/wordBreak/types.ts @@ -1,5 +1,5 @@ -import {CSSProperties} from 'react'; +import {TextStyle} from 'react-native'; -type WordBreakStyles = Record<'breakWord' | 'breakAll', Partial>>; +type WordBreakStyles = Record<'breakWord' | 'breakAll', Pick>; export default WordBreakStyles; diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts new file mode 100644 index 000000000000..1fc0617331ce --- /dev/null +++ b/src/types/modules/react-native.d.ts @@ -0,0 +1,148 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ +import {CSSProperties} from 'react'; +import 'react-native'; +import {Merge, MergeDeep, OverrideProperties} from 'type-fest'; + +declare module 'react-native' { + // interface ViewStyle extends CSSProperties {} + // interface ViewProps { + // style?: number; // Doesn't make any difference + // onLayout?: number; + // href?: string; // It appear new prop as suggestion + // focusable?: string; + // } + type NumberOrString = number | string; + + type AnimationDirection = 'alternate' | 'alternate-reverse' | 'normal' | 'reverse'; + type AnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both'; + type AnimationIterationCount = number | 'infinite'; + type AnimationKeyframes = string | object; + type AnimationPlayState = 'paused' | 'running'; + + type AnimationStyles = { + animationDelay?: string | string[]; + animationDirection?: AnimationDirection | AnimationDirection[]; + animationDuration?: string | string[]; + animationFillMode?: AnimationFillMode | AnimationFillMode[]; + animationIterationCount?: AnimationIterationCount | AnimationIterationCount[]; + animationKeyframes?: AnimationKeyframes | AnimationKeyframes[]; + animationPlayState?: AnimationPlayState | AnimationPlayState[]; + animationTimingFunction?: string | string[]; + transitionDelay?: string | string[]; + transitionDuration?: string | string[]; + transitionProperty?: string | string[]; + transitionTimingFunction?: string | string[]; + }; + + /** + * Image + */ + interface WebImageProps { + draggable?: boolean; + } + interface ImageProps extends WebImageProps {} + + /** + * Pressable + */ + interface WebPressableProps { + delayPressIn?: number; + delayPressOut?: number; + onPressMove?: null | ((event: GestureResponderEvent) => void); + onPressEnd?: null | ((event: GestureResponderEvent) => void); + } + interface WebPressableStateCallbackType { + readonly focused: boolean; + readonly hovered: boolean; + readonly pressed: boolean; + } + interface PressableProps extends WebPressableProps {} + interface PressableStateCallbackType extends WebPressableStateCallbackType {} + + /** + * Text + */ + interface WebTextProps { + dataSet?: Record; + dir?: 'auto' | 'ltr' | 'rtl'; + href?: string; + hrefAttrs?: { + download?: boolean; + rel?: string; + target?: string; + }; + lang?: string; + } + interface TextProps extends WebTextProps {} + + /** + * TextInput + */ + interface WebTextInputProps { + dataSet?: Record; + dir?: 'auto' | 'ltr' | 'rtl'; + lang?: string; + disabled?: boolean; + } + interface TextInputProps extends WebTextInputProps {} + + /** + * View + */ + interface WebViewProps { + dataSet?: Record; + dir?: 'ltr' | 'rtl'; + href?: string; + hrefAttrs?: { + download?: boolean; + rel?: string; + target?: string; + }; + tabIndex?: 0 | -1; + } + interface ViewProps extends WebViewProps {} + + interface WebStyle { + wordBreak?: CSSProperties['wordBreak']; + whiteSpace?: CSSProperties['whiteSpace']; + visibility?: CSSProperties['visibility']; + userSelect?: CSSProperties['userSelect']; + WebkitUserSelect?: CSSProperties['WebkitUserSelect']; + textUnderlinePosition?: CSSProperties['textUnderlinePosition']; + textDecorationSkipInk?: CSSProperties['textDecorationSkipInk']; + cursor?: CSSProperties['cursor']; + outlineWidth?: CSSProperties['outlineWidth']; + outlineStyle?: CSSProperties['outlineStyle']; + boxShadow?: CSSProperties['boxShadow']; + } + + interface WebViewStyle { + overscrollBehaviorX?: CSSProperties['overscrollBehaviorX']; + overflowX?: CSSProperties['overflowX']; + } + + // interface FlexStyle { + // overflow?: string; + // } + interface ViewStyle extends WebStyle, WebViewStyle {} + interface TextStyle extends WebStyle {} + interface ImageStyle extends WebStyle {} +} + +interface A { + overflow?: string; +} + +interface B { + overflow?: number; +} + +declare const a: A; + +// interface A extends B {} + +interface C extends Omit { + overflow?: number; +} +declare const a: C; From b1ee74b72cc5bad57fe70dc1be9740933ecefc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Tue, 5 Sep 2023 16:12:01 +0100 Subject: [PATCH 002/205] Add more typings --- src/types/modules/react-native.d.ts | 197 +++++++++++++++++++++------- 1 file changed, 148 insertions(+), 49 deletions(-) diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 1fc0617331ce..498529cf124a 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -1,40 +1,159 @@ +/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/no-empty-interface */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import {CSSProperties} from 'react'; +import {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventHandler} from 'react'; import 'react-native'; -import {Merge, MergeDeep, OverrideProperties} from 'type-fest'; declare module 'react-native' { - // interface ViewStyle extends CSSProperties {} - // interface ViewProps { - // style?: number; // Doesn't make any difference - // onLayout?: number; - // href?: string; // It appear new prop as suggestion - // focusable?: string; - // } + // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js type NumberOrString = number | string; + type OverscrollBehaviorValue = 'auto' | 'contain' | 'none'; + type idRef = string; + type idRefList = idRef | idRef[]; + + // https://necolas.github.io/react-native-web/docs/accessibility/#accessibility-props-api + // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js + type AccessibilityProps = { + 'aria-activedescendant'?: idRef; + 'aria-atomic'?: boolean; + 'aria-autocomplete'?: 'none' | 'list' | 'inline' | 'both'; + 'aria-busy'?: boolean; + 'aria-checked'?: boolean | 'mixed'; + 'aria-colcount'?: number; + 'aria-colindex'?: number; + 'aria-colspan'?: number; + 'aria-controls'?: idRef; + 'aria-current'?: boolean | 'page' | 'step' | 'location' | 'date' | 'time'; + 'aria-describedby'?: idRef; + 'aria-details'?: idRef; + 'aria-disabled'?: boolean; + 'aria-errormessage'?: idRef; + 'aria-expanded'?: boolean; + 'aria-flowto'?: idRef; + 'aria-haspopup'?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false; + 'aria-hidden'?: boolean; + 'aria-invalid'?: boolean; + 'aria-keyshortcuts'?: string[]; + 'aria-label'?: string; + 'aria-labelledby'?: idRef; + 'aria-level'?: number; + 'aria-live'?: 'assertive' | 'none' | 'polite'; + 'aria-modal'?: boolean; + 'aria-multiline'?: boolean; + 'aria-multiselectable'?: boolean; + 'aria-orientation'?: 'horizontal' | 'vertical'; + 'aria-owns'?: idRef; + 'aria-placeholder'?: string; + 'aria-posinset'?: number; + 'aria-pressed'?: boolean | 'mixed'; + 'aria-readonly'?: boolean; + 'aria-required'?: boolean; + 'aria-roledescription'?: string; + 'aria-rowcount'?: number; + 'aria-rowindex'?: number; + 'aria-rowspan'?: number; + 'aria-selected'?: boolean; + 'aria-setsize'?: number; + 'aria-sort'?: 'ascending' | 'descending' | 'none' | 'other'; + 'aria-valuemax'?: number; + 'aria-valuemin'?: number; + 'aria-valuenow'?: number; + 'aria-valuetext'?: string; + role?: string; + + // @deprecated + accessibilityActiveDescendant?: idRef; + accessibilityAtomic?: boolean; + accessibilityAutoComplete?: 'none' | 'list' | 'inline' | 'both'; + accessibilityBusy?: boolean; + accessibilityChecked?: boolean | 'mixed'; + accessibilityColumnCount?: number; + accessibilityColumnIndex?: number; + accessibilityColumnSpan?: number; + accessibilityControls?: idRefList; + accessibilityCurrent?: boolean | 'page' | 'step' | 'location' | 'date' | 'time'; + accessibilityDescribedBy?: idRefList; + accessibilityDetails?: idRef; + accessibilityDisabled?: boolean; + accessibilityErrorMessage?: idRef; + accessibilityExpanded?: boolean; + accessibilityFlowTo?: idRefList; + accessibilityHasPopup?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false; + accessibilityHidden?: boolean; + accessibilityInvalid?: boolean; + accessibilityKeyShortcuts?: string[]; + accessibilityLabel?: string; + accessibilityLabelledBy?: idRefList; + accessibilityLevel?: number; + accessibilityLiveRegion?: 'assertive' | 'none' | 'polite'; + accessibilityModal?: boolean; + accessibilityMultiline?: boolean; + accessibilityMultiSelectable?: boolean; + accessibilityOrientation?: 'horizontal' | 'vertical'; + accessibilityOwns?: idRefList; + accessibilityPlaceholder?: string; + accessibilityPosInSet?: number; + accessibilityPressed?: boolean | 'mixed'; + accessibilityReadOnly?: boolean; + accessibilityRequired?: boolean; + accessibilityRole?: string; + accessibilityRoleDescription?: string; + accessibilityRowCount?: number; + accessibilityRowIndex?: number; + accessibilityRowSpan?: number; + accessibilitySelected?: boolean; + accessibilitySetSize?: number; + accessibilitySort?: 'ascending' | 'descending' | 'none' | 'other'; + accessibilityValueMax?: number; + accessibilityValueMin?: number; + accessibilityValueNow?: number; + accessibilityValueText?: string; + }; + + // https://necolas.github.io/react-native-web/docs/interactions/#pointerevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js + // Extracted from @types/react, index.d.ts + type PointerProps = { + onClick?: MouseEventHandler; + onClickCapture?: MouseEventHandler; + onContextMenu?: MouseEventHandler; + }; - type AnimationDirection = 'alternate' | 'alternate-reverse' | 'normal' | 'reverse'; - type AnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both'; - type AnimationIterationCount = number | 'infinite'; - type AnimationKeyframes = string | object; - type AnimationPlayState = 'paused' | 'running'; - - type AnimationStyles = { - animationDelay?: string | string[]; - animationDirection?: AnimationDirection | AnimationDirection[]; - animationDuration?: string | string[]; - animationFillMode?: AnimationFillMode | AnimationFillMode[]; - animationIterationCount?: AnimationIterationCount | AnimationIterationCount[]; - animationKeyframes?: AnimationKeyframes | AnimationKeyframes[]; - animationPlayState?: AnimationPlayState | AnimationPlayState[]; - animationTimingFunction?: string | string[]; - transitionDelay?: string | string[]; - transitionDuration?: string | string[]; - transitionProperty?: string | string[]; - transitionTimingFunction?: string | string[]; + // TODO: Confirm + type FocusProps = { + onBlur?: FocusEventHandler; + onFocus?: FocusEventHandler; }; + // TODO: Confirm + type KeyboardProps = { + onKeyDown?: KeyboardEventHandler; + onKeyDownCapture?: KeyboardEventHandler; + onKeyUp?: KeyboardEventHandler; + onKeyUpCapture?: KeyboardEventHandler; + }; + + // type AnimationDirection = 'alternate' | 'alternate-reverse' | 'normal' | 'reverse'; + // type AnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both'; + // type AnimationIterationCount = number | 'infinite'; + // type AnimationKeyframes = string | object; + // type AnimationPlayState = 'paused' | 'running'; + + // type AnimationStyles = { + // animationDelay?: string | string[]; + // animationDirection?: AnimationDirection | AnimationDirection[]; + // animationDuration?: string | string[]; + // animationFillMode?: AnimationFillMode | AnimationFillMode[]; + // animationIterationCount?: AnimationIterationCount | AnimationIterationCount[]; + // animationKeyframes?: AnimationKeyframes | AnimationKeyframes[]; + // animationPlayState?: AnimationPlayState | AnimationPlayState[]; + // animationTimingFunction?: string | string[]; + // transitionDelay?: string | string[]; + // transitionDuration?: string | string[]; + // transitionProperty?: string | string[]; + // transitionTimingFunction?: string | string[]; + // }; + /** * Image */ @@ -90,7 +209,7 @@ declare module 'react-native' { /** * View */ - interface WebViewProps { + interface WebViewProps extends AccessibilityProps, PointerProps, FocusProps, KeyboardProps { dataSet?: Record; dir?: 'ltr' | 'rtl'; href?: string; @@ -122,27 +241,7 @@ declare module 'react-native' { overflowX?: CSSProperties['overflowX']; } - // interface FlexStyle { - // overflow?: string; - // } interface ViewStyle extends WebStyle, WebViewStyle {} interface TextStyle extends WebStyle {} interface ImageStyle extends WebStyle {} } - -interface A { - overflow?: string; -} - -interface B { - overflow?: number; -} - -declare const a: A; - -// interface A extends B {} - -interface C extends Omit { - overflow?: number; -} -declare const a: C; From fabe55fdbbec43e1b8a872d538afb42949277c3b Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 11 Sep 2023 15:48:37 -0700 Subject: [PATCH 003/205] WIP render an eReceipt for distance requests --- .../Attachments/AttachmentView/index.js | 20 +++++++ src/components/EReceipt.js | 54 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/components/EReceipt.js diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 47353d915060..584827f499c6 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -17,6 +17,11 @@ import AttachmentViewPdf from './AttachmentViewPdf'; import addEncryptedAuthTokenToURL from '../../../libs/addEncryptedAuthTokenToURL'; import * as StyleUtils from '../../../styles/StyleUtils'; import {attachmentViewPropTypes, attachmentViewDefaultProps} from './propTypes'; +import {useRoute} from '@react-navigation/native'; +import * as ReportUtils from '../../../libs/ReportUtils'; +import * as TransactionUtils from '../../../libs/TransactionUtils'; +import EReceipt from '../../EReceipt'; +import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; const propTypes = { ...attachmentViewPropTypes, @@ -107,6 +112,21 @@ function AttachmentView({ ); } + const currentRoute = useRoute(); + const reportID = _.get(currentRoute, ['params', 'reportID']); + const report = ReportUtils.getReport(reportID); + + // Get the money request transaction + const parentReportAction = ReportActionsUtils.getParentReportAction(report); + const transactionID = _.get(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); + const transaction = TransactionUtils.getTransaction(transactionID); + console.log('transaction', transaction); + + const shouldShowEReceipt = TransactionUtils.isDistanceRequest(transaction); + if (shouldShowEReceipt) { + return + } + // For this check we use both source and file.name since temporary file source is a blob // both PDFs and images will appear as images when pasted into the the text field const isImage = Str.isImage(source); diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js new file mode 100644 index 000000000000..406825926820 --- /dev/null +++ b/src/components/EReceipt.js @@ -0,0 +1,54 @@ +import React, {useEffect} from 'react'; +import PropTypes from 'prop-types'; +import {View} from 'react-native'; + +import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; +import _ from 'underscore'; +import ONYXKEYS from '../ONYXKEYS'; +import CONST from '../CONST'; +import * as MapboxToken from '../libs/actions/MapboxToken'; +import * as TransactionUtils from '../libs/TransactionUtils'; +import * as Expensicons from './Icon/Expensicons'; +import theme from '../styles/themes/default'; +import styles from '../styles/styles'; +import transactionPropTypes from './transactionPropTypes'; +import BlockingView from './BlockingViews/BlockingView'; +import useNetwork from '../hooks/useNetwork'; +import useLocalize from '../hooks/useLocalize'; + +const propTypes = { + /** The transaction for the eReceipt */ + transaction: transactionPropTypes, +}; + +const defaultProps = { + transaction: {}, +}; + + +function EReceipt({transaction}) { + const {isOffline} = useNetwork(); + const {translate} = useLocalize(); + const {route0: route} = transaction.routes || {}; + + useEffect(() => { + MapboxToken.init(); + return MapboxToken.stop; + }, []); + + return ( + <> + + ); +} + +export default withOnyx({ + transaction: { + key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + }, +})(EReceipt); + +EReceipt.displayName = 'EReceipt'; +EReceipt.propTypes = propTypes; +EReceipt.defaultProps = defaultProps; From a6b83fedb4f1f1380cb433ee216da7169453a470 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 11 Sep 2023 16:26:00 -0700 Subject: [PATCH 004/205] Render the receipt image --- src/components/EReceipt.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 406825926820..20d4c3789838 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -16,6 +16,8 @@ import transactionPropTypes from './transactionPropTypes'; import BlockingView from './BlockingViews/BlockingView'; import useNetwork from '../hooks/useNetwork'; import useLocalize from '../hooks/useLocalize'; +import * as ReceiptUtils from '../libs/ReceiptUtils'; +import Image from './Image'; const propTypes = { /** The transaction for the eReceipt */ @@ -32,13 +34,19 @@ function EReceipt({transaction}) { const {translate} = useLocalize(); const {route0: route} = transaction.routes || {}; - useEffect(() => { - MapboxToken.init(); - return MapboxToken.stop; - }, []); - + const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); return ( <> + + + ); } From 9d7538c13ae968c886d6b266f91974a88fc7ef7f Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 11 Sep 2023 16:33:08 -0700 Subject: [PATCH 005/205] Style and clean up --- .../Attachments/AttachmentView/index.js | 2 +- src/components/EReceipt.js | 31 ++----------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 584827f499c6..e7d1dd5abb6b 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -124,7 +124,7 @@ function AttachmentView({ const shouldShowEReceipt = TransactionUtils.isDistanceRequest(transaction); if (shouldShowEReceipt) { - return + return ; } // For this check we use both source and file.name since temporary file source is a blob diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 20d4c3789838..2068a5e50f12 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -1,21 +1,8 @@ -import React, {useEffect} from 'react'; -import PropTypes from 'prop-types'; +import React from 'react'; import {View} from 'react-native'; - -import {withOnyx} from 'react-native-onyx'; -import lodashGet from 'lodash/get'; import _ from 'underscore'; -import ONYXKEYS from '../ONYXKEYS'; -import CONST from '../CONST'; -import * as MapboxToken from '../libs/actions/MapboxToken'; -import * as TransactionUtils from '../libs/TransactionUtils'; -import * as Expensicons from './Icon/Expensicons'; -import theme from '../styles/themes/default'; import styles from '../styles/styles'; import transactionPropTypes from './transactionPropTypes'; -import BlockingView from './BlockingViews/BlockingView'; -import useNetwork from '../hooks/useNetwork'; -import useLocalize from '../hooks/useLocalize'; import * as ReceiptUtils from '../libs/ReceiptUtils'; import Image from './Image'; @@ -28,18 +15,11 @@ const defaultProps = { transaction: {}, }; - function EReceipt({transaction}) { - const {isOffline} = useNetwork(); - const {translate} = useLocalize(); - const {route0: route} = transaction.routes || {}; - const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); return ( <> - + `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - }, -})(EReceipt); - +export default EReceipt; EReceipt.displayName = 'EReceipt'; EReceipt.propTypes = propTypes; EReceipt.defaultProps = defaultProps; From 4aef004335b01a621e6aba97119eacebeea64229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Wed, 13 Sep 2023 15:14:07 +0100 Subject: [PATCH 006/205] Remove more CSSProperties usage from styles --- src/styles/StyleUtils.ts | 310 +++++++++--------- src/styles/cardStyles/index.ts | 4 +- src/styles/cardStyles/types.ts | 3 +- src/styles/editedLabelStyles/index.ts | 3 +- src/styles/fontWeight/bold/types.ts | 3 +- .../index.website.ts | 3 +- .../getReportActionContextMenuStyles.ts | 3 +- src/styles/getTooltipStyles.ts | 19 +- src/styles/utilities/display.ts | 12 +- src/styles/utilities/overflowAuto/index.ts | 5 +- 10 files changed, 185 insertions(+), 180 deletions(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 8945bc0be058..708a428b5633 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1,19 +1,18 @@ +import {Animated, FlexStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; import {EdgeInsets} from 'react-native-safe-area-context'; -import {Animated, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; -import {CSSProperties} from 'react'; import {ValueOf} from 'type-fest'; import CONST from '../CONST'; +import * as Browser from '../libs/Browser'; +import * as NumberUtils from '../libs/NumberUtils'; +import * as UserUtils from '../libs/UserUtils'; +import colors from './colors'; import fontFamily from './fontFamily'; +import styles from './styles'; import themeColors from './themes/default'; -import variables from './variables'; -import colors from './colors'; +import cursor from './utilities/cursor'; import positioning from './utilities/positioning'; -import styles from './styles'; import spacing from './utilities/spacing'; -import * as UserUtils from '../libs/UserUtils'; -import * as Browser from '../libs/Browser'; -import cursor from './utilities/cursor'; -import * as NumberUtils from '../libs/NumberUtils'; +import variables from './variables'; type ColorValue = ValueOf; type AvatarSizeName = ValueOf; @@ -38,7 +37,7 @@ type ButtonSizeValue = ValueOf; type EmptyAvatarSizeName = ValueOf>; type ButtonStateName = ValueOf; type AvatarSize = {width: number}; -type ParsableStyle = ViewStyle | CSSProperties | ((state: PressableStateCallbackType) => ViewStyle | CSSProperties); +type ParsableStyle = ViewStyle | ((state: PressableStateCallbackType) => ViewStyle); type WorkspaceColorStyle = {backgroundColor: ColorValue; fill: ColorValue}; @@ -115,7 +114,7 @@ const avatarSizes: Record = { [CONST.AVATAR_SIZE.SMALL_NORMAL]: variables.avatarSizeSmallNormal, }; -const emptyAvatarStyles: Record = { +const emptyAvatarStyles: Record = { [CONST.AVATAR_SIZE.SMALL]: styles.emptyAvatarSmall, [CONST.AVATAR_SIZE.MEDIUM]: styles.emptyAvatarMedium, [CONST.AVATAR_SIZE.LARGE]: styles.emptyAvatarLarge, @@ -155,21 +154,21 @@ function getAvatarSize(size: AvatarSizeName): number { /** * Return the height of magic code input container */ -function getHeightOfMagicCodeInput(): ViewStyle | CSSProperties { +function getHeightOfMagicCodeInput(): ViewStyle { return {height: styles.magicCodeInputContainer.minHeight - styles.textInputContainer.borderBottomWidth}; } /** * Return the style from an empty avatar size constant */ -function getEmptyAvatarStyle(size: EmptyAvatarSizeName): ViewStyle | CSSProperties | undefined { +function getEmptyAvatarStyle(size: EmptyAvatarSizeName): ViewStyle | undefined { return emptyAvatarStyles[size]; } /** * Return the width style from an avatar size constant */ -function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle | CSSProperties { +function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle { const avatarSize = getAvatarSize(size); return { width: avatarSize, @@ -179,7 +178,7 @@ function getAvatarWidthStyle(size: AvatarSizeName): ViewStyle | CSSProperties { /** * Return the style from an avatar size constant */ -function getAvatarStyle(size: AvatarSizeName): ViewStyle | CSSProperties { +function getAvatarStyle(size: AvatarSizeName): ViewStyle { const avatarSize = getAvatarSize(size); return { height: avatarSize, @@ -192,7 +191,7 @@ function getAvatarStyle(size: AvatarSizeName): ViewStyle | CSSProperties { /** * Get Font size of '+1' text on avatar overlay */ -function getAvatarExtraFontSizeStyle(size: AvatarSizeName): TextStyle | CSSProperties { +function getAvatarExtraFontSizeStyle(size: AvatarSizeName): TextStyle { return { fontSize: avatarFontSizes[size], }; @@ -201,7 +200,7 @@ function getAvatarExtraFontSizeStyle(size: AvatarSizeName): TextStyle | CSSPrope /** * Get Bordersize of Avatar based on avatar size */ -function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle | CSSProperties { +function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle { return { borderWidth: avatarBorderWidths[size], }; @@ -210,7 +209,7 @@ function getAvatarBorderWidth(size: AvatarSizeName): ViewStyle | CSSProperties { /** * Return the border radius for an avatar */ -function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle | CSSProperties { +function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle { if (type === CONST.ICON_TYPE_WORKSPACE) { return {borderRadius: avatarBorderSizes[size]}; } @@ -222,7 +221,7 @@ function getAvatarBorderRadius(size: AvatarSizeName, type: string): ViewStyle | /** * Return the border style for an avatar */ -function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle | CSSProperties { +function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle { return { overflow: 'hidden', ...getAvatarBorderRadius(size, type), @@ -232,7 +231,7 @@ function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle | C /** * Helper method to return old dot default avatar associated with login */ -function getDefaultWorkspaceAvatarColor(workspaceName: string): ViewStyle | CSSProperties { +function getDefaultWorkspaceAvatarColor(workspaceName: string): ViewStyle { const colorHash = UserUtils.hashText(workspaceName.trim(), workspaceColorOptions.length); return workspaceColorOptions[colorHash]; @@ -241,7 +240,7 @@ function getDefaultWorkspaceAvatarColor(workspaceName: string): ViewStyle | CSSP /** * Takes safe area insets and returns padding to use for a View */ -function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): ViewStyle | CSSProperties { +function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): ViewStyle { return { paddingTop: insets?.top, paddingBottom: (insets?.bottom ?? 0) * insetsPercentage, @@ -253,11 +252,11 @@ function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = vari /** * Takes safe area insets and returns margin to use for a View */ -function getSafeAreaMargins(insets?: EdgeInsets): ViewStyle | CSSProperties { +function getSafeAreaMargins(insets?: EdgeInsets): ViewStyle { return {marginBottom: (insets?.bottom ?? 0) * variables.safeInsertPercentage}; } -function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle | CSSProperties { +function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle { if (!isZoomed) { return styles.cursorZoomIn; } @@ -265,6 +264,7 @@ function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle | return isDragging ? styles.cursorGrabbing : styles.cursorZoomOut; } +// NOTE: asserting some web style properties to a valid type, because isn't possible to augment them. function getZoomSizingStyle( isZoomed: boolean, imgWidth: number, @@ -273,28 +273,28 @@ function getZoomSizingStyle( containerHeight: number, containerWidth: number, isLoading: boolean, -): ViewStyle | CSSProperties | undefined { +): ViewStyle | undefined { // Hide image until finished loading to prevent showing preview with wrong dimensions if (isLoading || imgWidth === 0 || imgHeight === 0) { return undefined; } - const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px`; - const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px`; + const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px` as ViewStyle['top']; + const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px` as ViewStyle['left']; // Return different size and offset style based on zoomScale and isZoom. if (isZoomed) { // When both width and height are smaller than container(modal) size, set the height by multiplying zoomScale if it is zoomed in. if (zoomScale >= 1) { return { - height: `${imgHeight * zoomScale}px`, - width: `${imgWidth * zoomScale}px`, + height: `${imgHeight * zoomScale}px` as FlexStyle['height'], + width: `${imgWidth * zoomScale}px` as FlexStyle['width'], }; } // If image height and width are bigger than container size, display image with original size because original size is bigger and position absolute. return { - height: `${imgHeight}px`, - width: `${imgWidth}px`, + height: `${imgHeight}px` as FlexStyle['height'], + width: `${imgWidth}px` as FlexStyle['width'], top, left, }; @@ -303,8 +303,8 @@ function getZoomSizingStyle( // If image is not zoomed in and image size is smaller than container size, display with original size based on offset and position absolute. if (zoomScale > 1) { return { - height: `${imgHeight}px`, - width: `${imgWidth}px`, + height: `${imgHeight}px` as FlexStyle['height'], + width: `${imgWidth}px` as FlexStyle['width'], top, left, }; @@ -312,11 +312,11 @@ function getZoomSizingStyle( // If image is bigger than container size, display full image in the screen with scaled size (fit by container size) and position absolute. // top, left offset should be different when displaying long or wide image. - const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px`; - const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px`; + const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px` as ViewStyle['top']; + const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px` as ViewStyle['left']; return { - height: `${imgHeight * zoomScale}px`, - width: `${imgWidth * zoomScale}px`, + height: `${imgHeight * zoomScale}px` as FlexStyle['height'], + width: `${imgWidth * zoomScale}px` as FlexStyle['width'], top: scaledTop, left: scaledLeft, }; @@ -325,7 +325,7 @@ function getZoomSizingStyle( /** * Returns auto grow text input style */ -function getWidthStyle(width: number): ViewStyle | CSSProperties { +function getWidthStyle(width: number): ViewStyle { return { width, }; @@ -334,7 +334,7 @@ function getWidthStyle(width: number): ViewStyle | CSSProperties { /** * Returns auto grow height text input style */ -function getAutoGrowHeightInputStyle(textInputHeight: number, maxHeight: number): ViewStyle | CSSProperties { +function getAutoGrowHeightInputStyle(textInputHeight: number, maxHeight: number): ViewStyle { if (textInputHeight > maxHeight) { return { ...styles.pr0, @@ -354,7 +354,7 @@ function getAutoGrowHeightInputStyle(textInputHeight: number, maxHeight: number) /** * Returns a style with backgroundColor and borderColor set to the same color */ -function getBackgroundAndBorderStyle(backgroundColor: string): ViewStyle | CSSProperties { +function getBackgroundAndBorderStyle(backgroundColor: string): ViewStyle { return { backgroundColor, borderColor: backgroundColor, @@ -364,7 +364,7 @@ function getBackgroundAndBorderStyle(backgroundColor: string): ViewStyle | CSSPr /** * Returns a style with the specified backgroundColor */ -function getBackgroundColorStyle(backgroundColor: string): ViewStyle | CSSProperties { +function getBackgroundColorStyle(backgroundColor: string): ViewStyle { return { backgroundColor, }; @@ -373,7 +373,7 @@ function getBackgroundColorStyle(backgroundColor: string): ViewStyle | CSSProper /** * Returns a style for text color */ -function getTextColorStyle(color: string): TextStyle | CSSProperties { +function getTextColorStyle(color: string): TextStyle { return { color, }; @@ -382,7 +382,7 @@ function getTextColorStyle(color: string): TextStyle | CSSProperties { /** * Returns a style with the specified borderColor */ -function getBorderColorStyle(borderColor: string): ViewStyle | CSSProperties { +function getBorderColorStyle(borderColor: string): ViewStyle { return { borderColor, }; @@ -391,7 +391,7 @@ function getBorderColorStyle(borderColor: string): ViewStyle | CSSProperties { /** * Returns the width style for the wordmark logo on the sign in page */ -function getSignInWordmarkWidthStyle(environment: string, isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getSignInWordmarkWidthStyle(environment: string, isSmallScreenWidth: boolean): ViewStyle { if (environment === CONST.ENVIRONMENT.DEV) { return isSmallScreenWidth ? {width: variables.signInLogoWidthPill} : {width: variables.signInLogoWidthLargeScreenPill}; } @@ -423,7 +423,7 @@ function hexadecimalToRGBArray(hexadecimal: string): number[] | undefined { /** * Returns a background color with opacity style */ -function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: number): ViewStyle | CSSProperties { +function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: number): ViewStyle { const result = hexadecimalToRGBArray(backgroundColor); if (result !== undefined) { return { @@ -436,7 +436,7 @@ function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: nu /** * Generate a style for the background color of the Badge */ -function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, isAdHoc = false): ViewStyle | CSSProperties { +function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, isAdHoc = false): ViewStyle { if (success) { if (isAdHoc) { return isPressed ? styles.badgeAdHocSuccessPressed : styles.badgeAdHocSuccess; @@ -455,7 +455,7 @@ function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, * @param buttonState - One of {'default', 'hovered', 'pressed'} * @param isMenuItem - whether this button is apart of a list */ -function getButtonBackgroundColorStyle(buttonState: ButtonStateName = CONST.BUTTON_STATES.DEFAULT, isMenuItem = false): ViewStyle | CSSProperties { +function getButtonBackgroundColorStyle(buttonState: ButtonStateName = CONST.BUTTON_STATES.DEFAULT, isMenuItem = false): ViewStyle { switch (buttonState) { case CONST.BUTTON_STATES.PRESSED: return {backgroundColor: themeColors.buttonPressedBG}; @@ -498,7 +498,7 @@ function getAnimatedFABStyle(rotate: Animated.Value, backgroundColor: Animated.V }; } -function getWidthAndHeightStyle(width: number, height: number | undefined = undefined): ViewStyle | CSSProperties { +function getWidthAndHeightStyle(width: number, height: number | undefined = undefined): ViewStyle { return { width, height: height ?? width, @@ -519,7 +519,7 @@ function getModalPaddingStyles({ modalContainerStylePaddingTop, modalContainerStylePaddingBottom, insets, -}: ModalPaddingStylesArgs): ViewStyle | CSSProperties { +}: ModalPaddingStylesArgs): ViewStyle { // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top. // More info: issue #17376 const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop || 0 : safeAreaPaddingBottom; @@ -547,7 +547,7 @@ function getFontFamilyMonospace({fontStyle, fontWeight}: TextStyle): string { /** * Gives the width for Emoji picker Widget */ -function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { width: CONST.SMALL_EMOJI_PICKER_SIZE.WIDTH, @@ -562,7 +562,7 @@ function getEmojiPickerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProper /** * Get the random promo color and image for Login page */ -function getLoginPagePromoStyle(): ViewStyle | CSSProperties { +function getLoginPagePromoStyle(): ViewStyle { const promos = [ { backgroundColor: colors.green, @@ -592,7 +592,7 @@ function getLoginPagePromoStyle(): ViewStyle | CSSProperties { /** * Generate the styles for the ReportActionItem wrapper view. */ -function getReportActionItemStyle(isHovered = false, isLoading = false): ViewStyle | CSSProperties { +function getReportActionItemStyle(isHovered = false, isLoading = false): ViewStyle { return { display: 'flex', justifyContent: 'space-between', @@ -608,7 +608,7 @@ function getReportActionItemStyle(isHovered = false, isLoading = false): ViewSty /** * Generate the wrapper styles for the mini ReportActionContextMenu. */ -function getMiniReportActionContextMenuWrapperStyle(isReportActionItemGrouped: boolean): ViewStyle | CSSProperties { +function getMiniReportActionContextMenuWrapperStyle(isReportActionItemGrouped: boolean): ViewStyle { return { ...(isReportActionItemGrouped ? positioning.tn8 : positioning.tn4), ...positioning.r4, @@ -618,7 +618,7 @@ function getMiniReportActionContextMenuWrapperStyle(isReportActionItemGrouped: b }; } -function getPaymentMethodMenuWidth(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getPaymentMethodMenuWidth(isSmallScreenWidth: boolean): ViewStyle { const margin = 20; return {width: !isSmallScreenWidth ? variables.sideBarWidth - margin * 2 : undefined}; } @@ -704,14 +704,14 @@ function getThemeBackgroundColor(bgColor: string = themeColors.appBG): string { /** * Parse styleParam and return Styles array */ -function parseStyleAsArray(styleParam: ViewStyle | CSSProperties | Array): Array { +function parseStyleAsArray(styleParam: ViewStyle | ViewStyle[]): ViewStyle[] { return Array.isArray(styleParam) ? styleParam : [styleParam]; } /** * Parse style function and return Styles object */ -function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): Array { +function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): ViewStyle[] { const functionAppliedStyle = typeof style === 'function' ? style(state) : style; return parseStyleAsArray(functionAppliedStyle); } @@ -719,8 +719,8 @@ function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallb /** * Receives any number of object or array style objects and returns them all as an array */ -function combineStyles(...allStyles: Array>) { - let finalStyles: Array> = []; +function combineStyles(...allStyles: Array) { + let finalStyles: ViewStyle[][] = []; allStyles.forEach((style) => { finalStyles = finalStyles.concat(parseStyleAsArray(style)); }); @@ -730,7 +730,7 @@ function combineStyles(...allStyles: Array 0 ? -overlapSize : 0, zIndex: index + 2, @@ -826,7 +826,7 @@ function getHorizontalStackedAvatarStyle(index: number, overlapSize: number): Vi /** * Get computed avatar styles of '+1' overlay based on size */ -function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAvatarBorderWidth: number): ViewStyle | CSSProperties { +function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAvatarBorderWidth: number): ViewStyle { return { borderWidth: oneAvatarBorderWidth, borderRadius: oneAvatarSize.width, @@ -836,7 +836,7 @@ function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAv }; } -function getErrorPageContainerStyle(safeAreaPaddingBottom = 0): ViewStyle | CSSProperties { +function getErrorPageContainerStyle(safeAreaPaddingBottom = 0): ViewStyle { return { backgroundColor: themeColors.componentBG, paddingBottom: 40 + safeAreaPaddingBottom, @@ -846,7 +846,7 @@ function getErrorPageContainerStyle(safeAreaPaddingBottom = 0): ViewStyle | CSSP /** * Gets the correct size for the empty state background image based on screen dimensions */ -function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { height: CONST.EMPTY_STATE_BACKGROUND.SMALL_SCREEN.IMAGE_HEIGHT, @@ -865,7 +865,7 @@ function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): View /** * Gets the correct top margin size for the chat welcome message based on screen dimensions */ -function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { marginTop: CONST.EMPTY_STATE_BACKGROUND.SMALL_SCREEN.VIEW_HEIGHT, @@ -880,7 +880,7 @@ function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean): ViewStyle /** * Returns fontSize style */ -function getFontSizeStyle(fontSize: number): TextStyle | CSSProperties { +function getFontSizeStyle(fontSize: number): TextStyle { return { fontSize, }; @@ -889,7 +889,7 @@ function getFontSizeStyle(fontSize: number): TextStyle | CSSProperties { /** * Returns lineHeight style */ -function getLineHeightStyle(lineHeight: number): TextStyle | CSSProperties { +function getLineHeightStyle(lineHeight: number): TextStyle { return { lineHeight, }; @@ -898,7 +898,7 @@ function getLineHeightStyle(lineHeight: number): TextStyle | CSSProperties { /** * Gets the correct size for the empty state container based on screen dimensions */ -function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle { if (isSmallScreenWidth) { return { minHeight: CONST.EMPTY_STATE_BACKGROUND.SMALL_SCREEN.CONTAINER_MINHEIGHT, @@ -917,7 +917,7 @@ function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle /** * Gets styles for AutoCompleteSuggestion row */ -function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, hovered: boolean, currentEmojiIndex: number): Array { +function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, hovered: boolean, currentEmojiIndex: number): ViewStyle[] { let backgroundColor; if (currentEmojiIndex === highlightedEmojiIndex) { @@ -942,14 +942,20 @@ function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHe /** * Gets the correct position for the base auto complete suggestion container */ -function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: {left: number; bottom: number; width: number}): ViewStyle | CSSProperties { - return {position: 'fixed', bottom, left, width}; +function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: {left: number; bottom: number; width: number}): ViewStyle { + return { + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], + bottom, + left, + width, + }; } /** * Gets the correct position for auto complete suggestion container */ -function getAutoCompleteSuggestionContainerStyle(itemsHeight: number, shouldIncludeReportRecipientLocalTimeHeight: boolean): ViewStyle | CSSProperties { +function getAutoCompleteSuggestionContainerStyle(itemsHeight: number, shouldIncludeReportRecipientLocalTimeHeight: boolean): ViewStyle { 'worklet'; const optionalPadding = shouldIncludeReportRecipientLocalTimeHeight ? CONST.RECIPIENT_LOCAL_TIME_HEIGHT : 0; @@ -969,11 +975,11 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number, shouldIncl /** * Select the correct color for text. */ -function getColoredBackgroundStyle(isColored: boolean): ViewStyle | CSSProperties { +function getColoredBackgroundStyle(isColored: boolean): ViewStyle { return {backgroundColor: isColored ? colors.blueLink : undefined}; } -function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean, isContextMenu = false): ViewStyle | CSSProperties { +function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean, isContextMenu = false): ViewStyle { let backgroundColor = themeColors.border; if (isHovered) { @@ -999,7 +1005,7 @@ function getEmojiReactionBubbleStyle(isHovered: boolean, hasUserReacted: boolean }; } -function getEmojiReactionBubbleTextStyle(isContextMenu = false): TextStyle | CSSProperties { +function getEmojiReactionBubbleTextStyle(isContextMenu = false): TextStyle { if (isContextMenu) { return { fontSize: 17, @@ -1013,7 +1019,7 @@ function getEmojiReactionBubbleTextStyle(isContextMenu = false): TextStyle | CSS }; } -function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle | CSSProperties { +function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle { if (hasUserReacted) { return {color: themeColors.reactionActiveText}; } @@ -1026,7 +1032,7 @@ function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle | * * @param direction - The direction of the rotation (CONST.DIRECTION.LEFT or CONST.DIRECTION.RIGHT). */ -function getDirectionStyle(direction: string): ViewStyle | CSSProperties { +function getDirectionStyle(direction: string): ViewStyle { if (direction === CONST.DIRECTION.LEFT) { return {transform: [{rotate: '180deg'}]}; } @@ -1037,11 +1043,11 @@ function getDirectionStyle(direction: string): ViewStyle | CSSProperties { /** * Returns a style object with display flex or none basing on the condition value. */ -function displayIfTrue(condition: boolean): ViewStyle | CSSProperties { +function displayIfTrue(condition: boolean): ViewStyle { return {display: condition ? 'flex' : 'none'}; } -function getGoogleListViewStyle(shouldDisplayBorder: boolean): ViewStyle | CSSProperties { +function getGoogleListViewStyle(shouldDisplayBorder: boolean): ViewStyle { if (shouldDisplayBorder) { return { ...styles.borderTopRounded, @@ -1059,7 +1065,7 @@ function getGoogleListViewStyle(shouldDisplayBorder: boolean): ViewStyle | CSSPr /** * Gets the correct height for emoji picker list based on screen dimensions */ -function getEmojiPickerListHeight(hasAdditionalSpace: boolean, windowHeight: number): ViewStyle | CSSProperties { +function getEmojiPickerListHeight(hasAdditionalSpace: boolean, windowHeight: number): ViewStyle { const style = { ...spacing.ph4, height: hasAdditionalSpace ? CONST.NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT + CONST.CATEGORY_SHORTCUT_BAR_HEIGHT : CONST.NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT, @@ -1079,7 +1085,7 @@ function getEmojiPickerListHeight(hasAdditionalSpace: boolean, windowHeight: num /** * Returns style object for the user mention component based on whether the mention is ours or not. */ -function getMentionStyle(isOurMention: boolean): ViewStyle | CSSProperties { +function getMentionStyle(isOurMention: boolean): ViewStyle { const backgroundColor = isOurMention ? themeColors.ourMentionBG : themeColors.mentionBG; return { backgroundColor, @@ -1098,7 +1104,7 @@ function getMentionTextColor(isOurMention: boolean): string { /** * Returns padding vertical based on number of lines */ -function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: boolean): ViewStyle | CSSProperties { +function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: boolean): ViewStyle { let paddingValue = 5; // Issue #26222: If isComposerFullSize paddingValue will always be 5 to prevent padding jumps when adding multiple lines. if (!isComposerFullSize) { @@ -1119,14 +1125,14 @@ function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: bo /** * Returns style object for the mobile on WEB */ -function getOuterModalStyle(windowHeight: number, viewportOffsetTop: number): ViewStyle | CSSProperties { +function getOuterModalStyle(windowHeight: number, viewportOffsetTop: number): ViewStyle { return Browser.isMobile() ? {maxHeight: windowHeight, marginTop: viewportOffsetTop} : {}; } /** * Returns style object for flexWrap depending on the screen size */ -function getWrappingStyle(isExtraSmallScreenWidth: boolean): ViewStyle | CSSProperties { +function getWrappingStyle(isExtraSmallScreenWidth: boolean): ViewStyle { return { flexWrap: isExtraSmallScreenWidth ? 'wrap' : 'nowrap', }; @@ -1135,7 +1141,7 @@ function getWrappingStyle(isExtraSmallScreenWidth: boolean): ViewStyle | CSSProp /** * Returns the text container styles for menu items depending on if the menu item container a small avatar */ -function getMenuItemTextContainerStyle(isSmallAvatarSubscriptMenu: boolean): ViewStyle | CSSProperties { +function getMenuItemTextContainerStyle(isSmallAvatarSubscriptMenu: boolean): ViewStyle { return { minHeight: isSmallAvatarSubscriptMenu ? variables.avatarSizeSubscript : variables.componentSizeNormal, }; @@ -1144,7 +1150,7 @@ function getMenuItemTextContainerStyle(isSmallAvatarSubscriptMenu: boolean): Vie /** * Returns link styles based on whether the link is disabled or not */ -function getDisabledLinkStyles(isDisabled = false): ViewStyle | CSSProperties { +function getDisabledLinkStyles(isDisabled = false): ViewStyle { const disabledLinkStyles = { color: themeColors.textSupporting, ...cursor.cursorDisabled, @@ -1159,7 +1165,7 @@ function getDisabledLinkStyles(isDisabled = false): ViewStyle | CSSProperties { /** * Returns the checkbox container style */ -function getCheckboxContainerStyle(size: number, borderRadius: number): ViewStyle | CSSProperties { +function getCheckboxContainerStyle(size: number, borderRadius: number): ViewStyle { return { backgroundColor: themeColors.componentBG, height: size, @@ -1176,7 +1182,7 @@ function getCheckboxContainerStyle(size: number, borderRadius: number): ViewStyl /** * Returns style object for the dropbutton height */ -function getDropDownButtonHeight(buttonSize: ButtonSizeValue): ViewStyle | CSSProperties { +function getDropDownButtonHeight(buttonSize: ButtonSizeValue): ViewStyle { if (buttonSize === CONST.DROPDOWN_BUTTON_SIZE.LARGE) { return { height: variables.componentSizeLarge, @@ -1190,7 +1196,7 @@ function getDropDownButtonHeight(buttonSize: ButtonSizeValue): ViewStyle | CSSPr /** * Returns fitting fontSize and lineHeight values in order to prevent large amounts from being cut off on small screen widths. */ -function getAmountFontSizeAndLineHeight(baseFontSize: number, baseLineHeight: number, isSmallScreenWidth: boolean, windowWidth: number): ViewStyle | CSSProperties { +function getAmountFontSizeAndLineHeight(baseFontSize: number, baseLineHeight: number, isSmallScreenWidth: boolean, windowWidth: number): TextStyle { let toSubtract = 0; if (isSmallScreenWidth) { @@ -1224,81 +1230,81 @@ function getTransparentColor(color: string) { } export { + combineStyles, + displayIfTrue, + fade, + getAmountFontSizeAndLineHeight, + getAnimatedFABStyle, + getAutoCompleteSuggestionContainerStyle, + getAutoCompleteSuggestionItemStyle, + getAutoGrowHeightInputStyle, + getAvatarBorderRadius, + getAvatarBorderStyle, + getAvatarBorderWidth, + getAvatarExtraFontSizeStyle, getAvatarSize, - getAvatarWidthStyle, getAvatarStyle, - getAvatarExtraFontSizeStyle, - getAvatarBorderWidth, - getAvatarBorderStyle, - getEmptyAvatarStyle, - getErrorPageContainerStyle, - getSafeAreaPadding, - getSafeAreaMargins, - getZoomCursorStyle, - getZoomSizingStyle, - getWidthStyle, - getAutoGrowHeightInputStyle, + getAvatarWidthStyle, getBackgroundAndBorderStyle, getBackgroundColorStyle, - getTextColorStyle, - getBorderColorStyle, getBackgroundColorWithOpacityStyle, getBadgeColorStyle, - getButtonBackgroundColorStyle, - getIconFillColor, - getAnimatedFABStyle, - getWidthAndHeightStyle, - getModalPaddingStyles, - getFontFamilyMonospace, - getEmojiPickerStyle, - getLoginPagePromoStyle, - getReportActionItemStyle, - getMiniReportActionContextMenuWrapperStyle, - getKeyboardShortcutsModalWidth, - getPaymentMethodMenuWidth, - getThemeBackgroundColor, - parseStyleAsArray, - parseStyleFromFunction, - combineStyles, - getPaddingLeft, - hasSafeAreas, - getHeight, - getMinimumHeight, - getMaximumHeight, - getMaximumWidth, - fade, - getHorizontalStackedAvatarBorderStyle, - getHorizontalStackedAvatarStyle, - getHorizontalStackedOverlayAvatarStyle, - getReportWelcomeBackgroundImageStyle, - getReportWelcomeTopMarginStyle, - getReportWelcomeContainerStyle, getBaseAutoCompleteSuggestionContainerStyle, - getAutoCompleteSuggestionItemStyle, - getAutoCompleteSuggestionContainerStyle, + getBorderColorStyle, + getButtonBackgroundColorStyle, + getCheckboxContainerStyle, getColoredBackgroundStyle, + getComposeTextAreaPadding, getDefaultWorkspaceAvatarColor, - getAvatarBorderRadius, + getDirectionStyle, + getDisabledLinkStyles, + getDropDownButtonHeight, + getEmojiPickerListHeight, + getEmojiPickerStyle, getEmojiReactionBubbleStyle, getEmojiReactionBubbleTextStyle, getEmojiReactionCounterTextStyle, - getDirectionStyle, - displayIfTrue, + getEmptyAvatarStyle, + getErrorPageContainerStyle, + getFontFamilyMonospace, getFontSizeStyle, - getLineHeightStyle, - getSignInWordmarkWidthStyle, getGoogleListViewStyle, - getEmojiPickerListHeight, + getHeight, + getHeightOfMagicCodeInput, + getHorizontalStackedAvatarBorderStyle, + getHorizontalStackedAvatarStyle, + getHorizontalStackedOverlayAvatarStyle, + getIconFillColor, + getKeyboardShortcutsModalWidth, + getLineHeightStyle, + getLoginPagePromoStyle, + getMaximumHeight, + getMaximumWidth, getMentionStyle, getMentionTextColor, - getComposeTextAreaPadding, - getHeightOfMagicCodeInput, - getOuterModalStyle, - getWrappingStyle, getMenuItemTextContainerStyle, - getDisabledLinkStyles, - getCheckboxContainerStyle, - getDropDownButtonHeight, - getAmountFontSizeAndLineHeight, + getMiniReportActionContextMenuWrapperStyle, + getMinimumHeight, + getModalPaddingStyles, + getOuterModalStyle, + getPaddingLeft, + getPaymentMethodMenuWidth, + getReportActionItemStyle, + getReportWelcomeBackgroundImageStyle, + getReportWelcomeContainerStyle, + getReportWelcomeTopMarginStyle, + getSafeAreaMargins, + getSafeAreaPadding, + getSignInWordmarkWidthStyle, + getTextColorStyle, + getThemeBackgroundColor, getTransparentColor, + getWidthAndHeightStyle, + getWidthStyle, + getWrappingStyle, + getZoomCursorStyle, + getZoomSizingStyle, + hasSafeAreas, + parseStyleAsArray, + parseStyleFromFunction, }; diff --git a/src/styles/cardStyles/index.ts b/src/styles/cardStyles/index.ts index 823081b62904..b2ba8d7c24f1 100644 --- a/src/styles/cardStyles/index.ts +++ b/src/styles/cardStyles/index.ts @@ -1,10 +1,12 @@ +import {ViewStyle} from 'react-native'; import GetCardStyles from './types'; /** * Get card style for cardStyleInterpolator */ const getCardStyles: GetCardStyles = (screenWidth) => ({ - position: 'fixed', + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], width: screenWidth, height: '100%', }); diff --git a/src/styles/cardStyles/types.ts b/src/styles/cardStyles/types.ts index e1598b7696ff..517ab76811bc 100644 --- a/src/styles/cardStyles/types.ts +++ b/src/styles/cardStyles/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; -type GetCardStyles = (screenWidth: number) => Partial>; +type GetCardStyles = (screenWidth: number) => Pick; export default GetCardStyles; diff --git a/src/styles/editedLabelStyles/index.ts b/src/styles/editedLabelStyles/index.ts index b3962e507757..5764735d0dea 100644 --- a/src/styles/editedLabelStyles/index.ts +++ b/src/styles/editedLabelStyles/index.ts @@ -1,10 +1,9 @@ -import {TextStyle} from 'react-native'; import display from '../utilities/display'; import flex from '../utilities/flex'; import EditedLabelStyles from './types'; const editedLabelStyles: EditedLabelStyles = { - ...(display.dInlineFlex as TextStyle), + ...display.dInlineFlex, ...flex.alignItemsBaseline, }; diff --git a/src/styles/fontWeight/bold/types.ts b/src/styles/fontWeight/bold/types.ts index 3c9930a63d87..67258eee719c 100644 --- a/src/styles/fontWeight/bold/types.ts +++ b/src/styles/fontWeight/bold/types.ts @@ -1,6 +1,5 @@ -import {CSSProperties} from 'react'; import {TextStyle} from 'react-native'; -type FontWeightBoldStyles = (TextStyle | CSSProperties)['fontWeight']; +type FontWeightBoldStyles = TextStyle['fontWeight']; export default FontWeightBoldStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.website.ts b/src/styles/getNavigationModalCardStyles/index.website.ts index ea76825e5bba..422a17d0a9a8 100644 --- a/src/styles/getNavigationModalCardStyles/index.website.ts +++ b/src/styles/getNavigationModalCardStyles/index.website.ts @@ -10,8 +10,7 @@ const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ width: '100%', height: '100%', - // NOTE: asserting "fixed" TS type to a valid type, because isn't possible - // to augment "position". + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". position: 'fixed' as ViewStyle['position'], }); diff --git a/src/styles/getReportActionContextMenuStyles.ts b/src/styles/getReportActionContextMenuStyles.ts index 792213f2b54b..33f04b723e25 100644 --- a/src/styles/getReportActionContextMenuStyles.ts +++ b/src/styles/getReportActionContextMenuStyles.ts @@ -17,8 +17,7 @@ const miniWrapperStyle: StylesArray = [ borderWidth: 1, borderColor: themeColors.border, // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style. - // NOTE: asserting "transform" TS type to a valid type, because isn't possible - // to augment "transform". + // NOTE: asserting "transform" to a valid type, because isn't possible to augment "transform". transform: 'translateZ(0)' as unknown as ViewStyle['transform'], }, ]; diff --git a/src/styles/getTooltipStyles.ts b/src/styles/getTooltipStyles.ts index 3f9de9c78b97..1e0b41d59aa4 100644 --- a/src/styles/getTooltipStyles.ts +++ b/src/styles/getTooltipStyles.ts @@ -1,12 +1,11 @@ -import {CSSProperties} from 'react'; import {TextStyle, View, ViewStyle} from 'react-native'; -import spacing from './utilities/spacing'; -import styles from './styles'; import colors from './colors'; -import themeColors from './themes/default'; import fontFamily from './fontFamily'; -import variables from './variables'; import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour'; +import styles from './styles'; +import themeColors from './themes/default'; +import spacing from './utilities/spacing'; +import variables from './variables'; /** This defines the proximity with the edge of the window in which tooltips should not be displayed. * If a tooltip is too close to the edge of the screen, we'll shift it towards the center. */ @@ -96,9 +95,9 @@ function isOverlappingAtTop(tooltip: View | HTMLDivElement, xOffset: number, yOf type TooltipStyles = { animationStyle: ViewStyle; - rootWrapperStyle: ViewStyle | CSSProperties; + rootWrapperStyle: ViewStyle; textStyle: TextStyle; - pointerWrapperStyle: ViewStyle | CSSProperties; + pointerWrapperStyle: ViewStyle; pointerStyle: ViewStyle; }; @@ -238,7 +237,8 @@ export default function getTooltipStyles( transform: [{scale}], }, rootWrapperStyle: { - position: 'fixed', + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], backgroundColor: themeColors.heading, borderRadius: variables.componentBorderRadiusSmall, ...tooltipVerticalPadding, @@ -260,7 +260,8 @@ export default function getTooltipStyles( lineHeight: variables.lineHeightSmall, }, pointerWrapperStyle: { - position: 'fixed', + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], top: pointerWrapperTop, left: pointerWrapperLeft, }, diff --git a/src/styles/utilities/display.ts b/src/styles/utilities/display.ts index 868c2bdb0e3b..e236ad4ea14d 100644 --- a/src/styles/utilities/display.ts +++ b/src/styles/utilities/display.ts @@ -1,4 +1,3 @@ -import {CSSProperties} from 'react'; import {ViewStyle} from 'react-native'; /** @@ -21,14 +20,17 @@ export default { }, dInline: { - display: 'inline', + // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + display: 'inline' as ViewStyle['display'], }, dInlineFlex: { - display: 'inline-flex', + // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + display: 'inline-flex' as ViewStyle['display'], }, dBlock: { - display: 'block', + // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + display: 'block' as ViewStyle['display'], }, -} satisfies Record; +} satisfies Record; diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utilities/overflowAuto/index.ts index f958d8000c12..d73bee877c50 100644 --- a/src/styles/utilities/overflowAuto/index.ts +++ b/src/styles/utilities/overflowAuto/index.ts @@ -1,9 +1,8 @@ -import { ViewStyle } from 'react-native'; +import {ViewStyle} from 'react-native'; import OverflowAutoStyles from './types'; const overflowAuto: OverflowAutoStyles = { - // NOTE: asserting "auto" TS type to a valid type, because isn't possible - // to augment "overflow". + // NOTE: asserting "overflow" to a valid type, because isn't possible to augment "overflow". overflow: 'auto' as ViewStyle['overflow'], }; From 58d4a25f581df52c7a2077f9f3feabdc90b9ec46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Thu, 14 Sep 2023 01:12:35 +0100 Subject: [PATCH 007/205] Add more props and styles --- src/types/modules/react-native.d.ts | 276 ++++++++++++++++++---------- 1 file changed, 184 insertions(+), 92 deletions(-) diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index c06c09f38fe5..d0a2de964b8d 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -1,19 +1,18 @@ /* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/no-empty-interface */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventHandler} from 'react'; +import {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventHandler, PointerEventHandler, UIEventHandler, WheelEventHandler} from 'react'; import 'react-native'; declare module 'react-native' { + // <------ REACT NATIVE WEB (0.19.0) ------> // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - type NumberOrString = number | string; - type OverscrollBehaviorValue = 'auto' | 'contain' | 'none'; type idRef = string; type idRefList = idRef | idRef[]; // https://necolas.github.io/react-native-web/docs/accessibility/#accessibility-props-api // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - type AccessibilityProps = { + interface AccessibilityProps { 'aria-activedescendant'?: idRef; 'aria-atomic'?: boolean; 'aria-autocomplete'?: 'none' | 'list' | 'inline' | 'both'; @@ -108,142 +107,235 @@ declare module 'react-native' { accessibilityValueMin?: number; accessibilityValueNow?: number; accessibilityValueText?: string; - }; + } // https://necolas.github.io/react-native-web/docs/interactions/#pointerevent-props-api - // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js - // Extracted from @types/react, index.d.ts - type PointerProps = { + // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js + // Extracted types from @types/react, index.d.ts + interface PointerProps { + onAuxClick?: MouseEventHandler; onClick?: MouseEventHandler; - onClickCapture?: MouseEventHandler; onContextMenu?: MouseEventHandler; + onGotPointerCapture?: PointerEventHandler; + onLostPointerCapture?: PointerEventHandler; + onPointerCancel?: PointerEventHandler; + onPointerDown?: PointerEventHandler; + onPointerEnter?: PointerEventHandler; + onPointerMove?: PointerEventHandler; + onPointerLeave?: PointerEventHandler; + onPointerOut?: PointerEventHandler; + onPointerOver?: PointerEventHandler; + onPointerUp?: PointerEventHandler; + onMouseDown?: MouseEventHandler; + onMouseEnter?: MouseEventHandler; + onMouseLeave?: MouseEventHandler; + onMouseMove?: MouseEventHandler; + onMouseOver?: MouseEventHandler; + onMouseOut?: MouseEventHandler; + onMouseUp?: MouseEventHandler; + onScroll?: UIEventHandler; + onWheel?: WheelEventHandler; + } + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderTouchHistoryStore.js + type TouchRecord = { + currentPageX: number; + currentPageY: number; + currentTimeStamp: number; + previousPageX: number; + previousPageY: number; + previousTimeStamp: number; + startPageX: number; + startPageY: number; + startTimeStamp: number; + touchActive: boolean; + }; + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderTouchHistoryStore.js + type TouchHistory = Readonly<{ + indexOfSingleActiveTouch: number; + mostRecentTimeStamp: number; + numberActiveTouches: number; + touchBank: TouchRecord[]; + }>; + + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/createResponderEvent.js + type ResponderEvent = { + bubbles: boolean; + cancelable: boolean; + currentTarget?: unknown; // changed from "any" to "unknown" + defaultPrevented?: boolean; + dispatchConfig: { + registrationName?: string; + phasedRegistrationNames?: { + bubbled: string; + captured: string; + }; + }; + eventPhase?: number; + isDefaultPrevented: () => boolean; + isPropagationStopped: () => boolean; + isTrusted?: boolean; + preventDefault: () => void; + stopPropagation: () => void; + nativeEvent: TouchEvent; + persist: () => void; + target?: unknown; // changed from "any" to "unknown" + timeStamp: number; + touchHistory: TouchHistory; }; - // TODO: Confirm - type FocusProps = { + // https://necolas.github.io/react-native-web/docs/interactions/#responderevent-props-api + // Extracted from react-native-web, packages/react-native-web/src/modules/useResponderEvents/ResponderSystem.js + interface ResponderProps { + // Direct responder events dispatched directly to responder. Do not bubble. + onResponderEnd?: (e: ResponderEvent) => void; + onResponderGrant?: (e: ResponderEvent) => void | boolean; + onResponderMove?: (e: ResponderEvent) => void; + onResponderRelease?: (e: ResponderEvent) => void; + onResponderReject?: (e: ResponderEvent) => void; + onResponderStart?: (e: ResponderEvent) => void; + onResponderTerminate?: (e: ResponderEvent) => void; + onResponderTerminationRequest?: (e: ResponderEvent) => boolean; + + // On pointer down, should this element become the responder? + onStartShouldSetResponder?: (e: ResponderEvent) => boolean; + onStartShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + + // On pointer move, should this element become the responder? + onMoveShouldSetResponder?: (e: ResponderEvent) => boolean; + onMoveShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + + // On scroll, should this element become the responder? Do no bubble + onScrollShouldSetResponder?: (e: ResponderEvent) => boolean; + onScrollShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + + // On text selection change, should this element become the responder? + onSelectionChangeShouldSetResponder?: (e: ResponderEvent) => boolean; + onSelectionChangeShouldSetResponderCapture?: (e: ResponderEvent) => boolean; + } + + // https://necolas.github.io/react-native-web/docs/interactions/#focusevent-props-api + // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js + // Extracted types from @types/react, index.d.ts + interface FocusProps { onBlur?: FocusEventHandler; onFocus?: FocusEventHandler; - }; + } - // TODO: Confirm - type KeyboardProps = { + // https://necolas.github.io/react-native-web/docs/interactions/#keyboardevent-props-api + // Extracted properties from react-native-web, packages/react-native-web/src/exports/View/types.js and packages/react-native-web/src/modules/forwardedProps/index.js + // Extracted types from @types/react, index.d.ts + interface KeyboardProps { onKeyDown?: KeyboardEventHandler; onKeyDownCapture?: KeyboardEventHandler; onKeyUp?: KeyboardEventHandler; onKeyUpCapture?: KeyboardEventHandler; - }; - - // type AnimationDirection = 'alternate' | 'alternate-reverse' | 'normal' | 'reverse'; - // type AnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both'; - // type AnimationIterationCount = number | 'infinite'; - // type AnimationKeyframes = string | object; - // type AnimationPlayState = 'paused' | 'running'; - - // type AnimationStyles = { - // animationDelay?: string | string[]; - // animationDirection?: AnimationDirection | AnimationDirection[]; - // animationDuration?: string | string[]; - // animationFillMode?: AnimationFillMode | AnimationFillMode[]; - // animationIterationCount?: AnimationIterationCount | AnimationIterationCount[]; - // animationKeyframes?: AnimationKeyframes | AnimationKeyframes[]; - // animationPlayState?: AnimationPlayState | AnimationPlayState[]; - // animationTimingFunction?: string | string[]; - // transitionDelay?: string | string[]; - // transitionDuration?: string | string[]; - // transitionProperty?: string | string[]; - // transitionTimingFunction?: string | string[]; - // }; + } /** - * Image + * Shared props + * Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js */ - interface WebImageProps { - draggable?: boolean; + interface WebSharedProps extends AccessibilityProps, PointerProps, ResponderProps, FocusProps, KeyboardProps { + dataSet?: Record; + href?: string; + hrefAttrs?: { + download?: boolean; + rel?: string; + target?: string; + }; + tabIndex?: 0 | -1; + lang?: string; } - interface ImageProps extends WebImageProps {} /** - * Pressable + * View + * Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js */ - interface WebPressableProps { - delayPressIn?: number; - delayPressOut?: number; - onPressMove?: null | ((event: GestureResponderEvent) => void); - onPressEnd?: null | ((event: GestureResponderEvent) => void); - } - interface WebPressableStateCallbackType { - readonly focused: boolean; - readonly hovered: boolean; - readonly pressed: boolean; + interface WebViewProps extends WebSharedProps { + dir?: 'ltr' | 'rtl'; } - interface PressableProps extends WebPressableProps {} - interface PressableStateCallbackType extends WebPressableStateCallbackType {} + interface ViewProps extends WebViewProps {} /** * Text + * Extracted from react-native-web, packages/react-native-web/src/exports/Text/types.js */ - interface WebTextProps { - dataSet?: Record; + interface WebTextProps extends WebSharedProps { dir?: 'auto' | 'ltr' | 'rtl'; - href?: string; - hrefAttrs?: { - download?: boolean; - rel?: string; - target?: string; - }; - lang?: string; } interface TextProps extends WebTextProps {} /** * TextInput + * Extracted from react-native-web, packages/react-native-web/src/exports/TextInput/types.js */ - interface WebTextInputProps { - dataSet?: Record; + interface WebTextInputProps extends WebSharedProps { dir?: 'auto' | 'ltr' | 'rtl'; - lang?: string; disabled?: boolean; + enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'; + readOnly?: boolean; } interface TextInputProps extends WebTextInputProps {} /** - * View + * Image + * Extracted from react-native-web, packages/react-native-web/src/exports/Image/types.js */ - interface WebViewProps extends AccessibilityProps, PointerProps, FocusProps, KeyboardProps { - dataSet?: Record; + interface WebImageProps extends WebSharedProps { dir?: 'ltr' | 'rtl'; - href?: string; - hrefAttrs?: { - download?: boolean; - rel?: string; - target?: string; - }; - tabIndex?: 0 | -1; + draggable?: boolean; } - interface ViewProps extends WebViewProps {} + interface ImageProps extends WebImageProps {} + + /** + * ScrollView + * Extracted from react-native-web, packages/react-native-web/src/exports/ScrollView/ScrollViewBase.js + */ + interface WebScrollViewProps extends WebSharedProps {} + interface ScrollViewProps extends WebScrollViewProps {} + + /** + * Pressable + */ + // https://necolas.github.io/react-native-web/docs/pressable/#interactionstate + // Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js + interface WebPressableStateCallbackType { + readonly focused: boolean; + readonly hovered: boolean; + readonly pressed: boolean; + } + interface PressableStateCallbackType extends WebPressableStateCallbackType {} - interface WebStyle { - wordBreak?: CSSProperties['wordBreak']; - whiteSpace?: CSSProperties['whiteSpace']; - visibility?: CSSProperties['visibility']; - userSelect?: CSSProperties['userSelect']; - WebkitUserSelect?: CSSProperties['WebkitUserSelect']; - textUnderlinePosition?: CSSProperties['textUnderlinePosition']; - textDecorationSkipInk?: CSSProperties['textDecorationSkipInk']; - cursor?: CSSProperties['cursor']; - outlineWidth?: CSSProperties['outlineWidth']; - outlineStyle?: CSSProperties['outlineStyle']; - boxShadow?: CSSProperties['boxShadow']; + // Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js + interface WebPressableProps extends WebSharedProps { + delayPressIn?: number; + delayPressOut?: number; + onPressMove?: null | ((event: GestureResponderEvent) => void); + onPressEnd?: null | ((event: GestureResponderEvent) => void); } + interface PressableProps extends WebPressableProps {} - interface WebViewStyle { - overscrollBehaviorX?: CSSProperties['overscrollBehaviorX']; - overflowX?: CSSProperties['overflowX']; + /** + * Styles + */ + // We extend CSSProperties (alias to "csstype" library) which provides all CSS style properties for Web, + // but properties that are already defined on RN won't be overrided / augmented. + interface WebStyle extends CSSProperties { + // https://necolas.github.io/react-native-web/docs/styling/#non-standard-properties + // Exclusive to react-native-web, "pointerEvents" already included on RN + animationKeyframes?: string | Record; + writingDirection?: 'auto' | 'ltr' | 'rtl'; } - interface ViewStyle extends WebStyle, WebViewStyle {} + interface ViewStyle extends WebStyle {} interface TextStyle extends WebStyle {} interface ImageStyle extends WebStyle {} + // <------ REACT NATIVE WEB (0.19.0) ------> interface TextInput { // Typescript type declaration is missing in React Native for setting text selection. From 091104ee92a6f1f239849829b6d0ed3cae34b939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Thu, 14 Sep 2023 17:28:11 +0100 Subject: [PATCH 008/205] Refactor styles and implement missing ones --- src/styles/StyleUtils.ts | 14 +++++++------- src/styles/cardStyles/types.ts | 2 +- .../containerComposeStyles/index.native.ts | 4 ++-- src/styles/containerComposeStyles/index.ts | 4 ++-- src/styles/containerComposeStyles/types.ts | 5 +++++ src/styles/fontFamily/bold/index.android.js | 3 --- src/styles/fontFamily/bold/index.android.ts | 5 +++++ src/styles/fontFamily/bold/index.ios.js | 3 --- src/styles/fontFamily/bold/index.ios.ts | 5 +++++ src/styles/fontFamily/bold/index.js | 3 --- src/styles/fontFamily/bold/index.ts | 5 +++++ src/styles/fontFamily/bold/types.ts | 5 +++++ src/styles/fontFamily/types.ts | 4 +++- src/styles/fontWeight/bold/types.ts | 2 +- .../index.desktop.js | 10 ---------- .../index.desktop.ts | 17 +++++++++++++++++ .../getNavigationModalCardStyles/types.ts | 3 +-- src/styles/getReportActionContextMenuStyles.ts | 8 +++----- src/styles/italic/types.ts | 2 +- .../optionAlternateTextPlatformStyles/types.ts | 2 +- src/styles/optionRowStyles/index.native.ts | 5 ++--- src/styles/optionRowStyles/index.ts | 4 ++-- src/styles/optionRowStyles/types.ts | 4 ++-- src/styles/utilities/cursor/types.ts | 4 ++-- src/styles/utilities/visibility/types.ts | 4 ++-- 25 files changed, 74 insertions(+), 53 deletions(-) create mode 100644 src/styles/containerComposeStyles/types.ts delete mode 100644 src/styles/fontFamily/bold/index.android.js create mode 100644 src/styles/fontFamily/bold/index.android.ts delete mode 100644 src/styles/fontFamily/bold/index.ios.js create mode 100644 src/styles/fontFamily/bold/index.ios.ts delete mode 100644 src/styles/fontFamily/bold/index.js create mode 100644 src/styles/fontFamily/bold/index.ts create mode 100644 src/styles/fontFamily/bold/types.ts delete mode 100644 src/styles/getNavigationModalCardStyles/index.desktop.js create mode 100644 src/styles/getNavigationModalCardStyles/index.desktop.ts diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 3daea4555796..1f88829b0e23 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -535,14 +535,14 @@ function getModalPaddingStyles({ }: ModalPaddingStylesArgs): ViewStyle { // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top. // More info: issue #17376 - const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop || 0 : safeAreaPaddingBottom; + const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop ?? 0 : safeAreaPaddingBottom; return { - marginTop: (modalContainerStyleMarginTop || 0) + (shouldAddTopSafeAreaMargin ? safeAreaPaddingTop : 0), - marginBottom: (modalContainerStyleMarginBottom || 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottomWithFallback : 0), - paddingTop: shouldAddTopSafeAreaPadding ? (modalContainerStylePaddingTop || 0) + safeAreaPaddingTop : modalContainerStylePaddingTop || 0, - paddingBottom: shouldAddBottomSafeAreaPadding ? (modalContainerStylePaddingBottom || 0) + safeAreaPaddingBottomWithFallback : modalContainerStylePaddingBottom || 0, - paddingLeft: safeAreaPaddingLeft || 0, - paddingRight: safeAreaPaddingRight || 0, + marginTop: (modalContainerStyleMarginTop ?? 0) + (shouldAddTopSafeAreaMargin ? safeAreaPaddingTop : 0), + marginBottom: (modalContainerStyleMarginBottom ?? 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottomWithFallback : 0), + paddingTop: shouldAddTopSafeAreaPadding ? (modalContainerStylePaddingTop ?? 0) + safeAreaPaddingTop : modalContainerStylePaddingTop ?? 0, + paddingBottom: shouldAddBottomSafeAreaPadding ? (modalContainerStylePaddingBottom ?? 0) + safeAreaPaddingBottomWithFallback : modalContainerStylePaddingBottom ?? 0, + paddingLeft: safeAreaPaddingLeft ?? 0, + paddingRight: safeAreaPaddingRight ?? 0, }; } diff --git a/src/styles/cardStyles/types.ts b/src/styles/cardStyles/types.ts index 517ab76811bc..134b93eae32f 100644 --- a/src/styles/cardStyles/types.ts +++ b/src/styles/cardStyles/types.ts @@ -1,5 +1,5 @@ import {ViewStyle} from 'react-native'; -type GetCardStyles = (screenWidth: number) => Pick; +type GetCardStyles = (screenWidth: number) => ViewStyle; export default GetCardStyles; diff --git a/src/styles/containerComposeStyles/index.native.ts b/src/styles/containerComposeStyles/index.native.ts index 6b6bcf71cfcf..ea525dc652cf 100644 --- a/src/styles/containerComposeStyles/index.native.ts +++ b/src/styles/containerComposeStyles/index.native.ts @@ -1,6 +1,6 @@ -import {StyleProp, ViewStyle} from 'react-native'; import styles from '../styles'; +import ContainerComposeStyles from './types'; -const containerComposeStyles: StyleProp = [styles.textInputComposeSpacing]; +const containerComposeStyles: ContainerComposeStyles = [styles.textInputComposeSpacing]; export default containerComposeStyles; diff --git a/src/styles/containerComposeStyles/index.ts b/src/styles/containerComposeStyles/index.ts index 6968e23a507e..fbbf35a20818 100644 --- a/src/styles/containerComposeStyles/index.ts +++ b/src/styles/containerComposeStyles/index.ts @@ -1,7 +1,7 @@ -import {StyleProp, ViewStyle} from 'react-native'; import styles from '../styles'; +import ContainerComposeStyles from './types'; // We need to set paddingVertical = 0 on web to avoid displaying a normal pointer on some parts of compose box when not in focus -const containerComposeStyles: StyleProp = [styles.textInputComposeSpacing, {paddingVertical: 0}]; +const containerComposeStyles: ContainerComposeStyles = [styles.textInputComposeSpacing, {paddingVertical: 0}]; export default containerComposeStyles; diff --git a/src/styles/containerComposeStyles/types.ts b/src/styles/containerComposeStyles/types.ts new file mode 100644 index 000000000000..278039691b8a --- /dev/null +++ b/src/styles/containerComposeStyles/types.ts @@ -0,0 +1,5 @@ +import {ViewStyle} from 'react-native'; + +type ContainerComposeStyles = ViewStyle[]; + +export default ContainerComposeStyles; diff --git a/src/styles/fontFamily/bold/index.android.js b/src/styles/fontFamily/bold/index.android.js deleted file mode 100644 index 7473e4d7533c..000000000000 --- a/src/styles/fontFamily/bold/index.android.js +++ /dev/null @@ -1,3 +0,0 @@ -const bold = 'ExpensifyNeue-Bold'; - -export default bold; diff --git a/src/styles/fontFamily/bold/index.android.ts b/src/styles/fontFamily/bold/index.android.ts new file mode 100644 index 000000000000..563e063e1bd7 --- /dev/null +++ b/src/styles/fontFamily/bold/index.android.ts @@ -0,0 +1,5 @@ +import FontFamilyBoldStyles from './types'; + +const bold: FontFamilyBoldStyles = 'ExpensifyNeue-Bold'; + +export default bold; diff --git a/src/styles/fontFamily/bold/index.ios.js b/src/styles/fontFamily/bold/index.ios.js deleted file mode 100644 index 3ba35f200d3d..000000000000 --- a/src/styles/fontFamily/bold/index.ios.js +++ /dev/null @@ -1,3 +0,0 @@ -const bold = 'ExpensifyNeue-Regular'; - -export default bold; diff --git a/src/styles/fontFamily/bold/index.ios.ts b/src/styles/fontFamily/bold/index.ios.ts new file mode 100644 index 000000000000..f019dd47fc2a --- /dev/null +++ b/src/styles/fontFamily/bold/index.ios.ts @@ -0,0 +1,5 @@ +import FontFamilyBoldStyles from './types'; + +const bold: FontFamilyBoldStyles = 'ExpensifyNeue-Regular'; + +export default bold; diff --git a/src/styles/fontFamily/bold/index.js b/src/styles/fontFamily/bold/index.js deleted file mode 100644 index 66d3c64f3565..000000000000 --- a/src/styles/fontFamily/bold/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const bold = 'ExpensifyNeue-Regular, Segoe UI Emoji, Noto Color Emoji'; - -export default bold; diff --git a/src/styles/fontFamily/bold/index.ts b/src/styles/fontFamily/bold/index.ts new file mode 100644 index 000000000000..96c9846df985 --- /dev/null +++ b/src/styles/fontFamily/bold/index.ts @@ -0,0 +1,5 @@ +import FontFamilyBoldStyles from './types'; + +const bold: FontFamilyBoldStyles = 'ExpensifyNeue-Regular, Segoe UI Emoji, Noto Color Emoji'; + +export default bold; diff --git a/src/styles/fontFamily/bold/types.ts b/src/styles/fontFamily/bold/types.ts new file mode 100644 index 000000000000..258b23de94a2 --- /dev/null +++ b/src/styles/fontFamily/bold/types.ts @@ -0,0 +1,5 @@ +import {TextStyle} from 'react-native'; + +type FontFamilyBoldStyles = NonNullable; + +export default FontFamilyBoldStyles; diff --git a/src/styles/fontFamily/types.ts b/src/styles/fontFamily/types.ts index 4c9a121e80d7..c688f40927be 100644 --- a/src/styles/fontFamily/types.ts +++ b/src/styles/fontFamily/types.ts @@ -1,3 +1,5 @@ +import {TextStyle} from 'react-native'; + type FontFamilyKeys = | 'EXP_NEUE_ITALIC' | 'EXP_NEUE_BOLD' @@ -10,6 +12,6 @@ type FontFamilyKeys = | 'MONOSPACE_BOLD' | 'MONOSPACE_BOLD_ITALIC'; -type FontFamilyStyles = Record; +type FontFamilyStyles = Record>; export default FontFamilyStyles; diff --git a/src/styles/fontWeight/bold/types.ts b/src/styles/fontWeight/bold/types.ts index 67258eee719c..00e72d0e879c 100644 --- a/src/styles/fontWeight/bold/types.ts +++ b/src/styles/fontWeight/bold/types.ts @@ -1,5 +1,5 @@ import {TextStyle} from 'react-native'; -type FontWeightBoldStyles = TextStyle['fontWeight']; +type FontWeightBoldStyles = NonNullable; export default FontWeightBoldStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.desktop.js b/src/styles/getNavigationModalCardStyles/index.desktop.js deleted file mode 100644 index 54c9790253d6..000000000000 --- a/src/styles/getNavigationModalCardStyles/index.desktop.js +++ /dev/null @@ -1,10 +0,0 @@ -export default () => ({ - // position: fixed is set instead of position absolute to workaround Safari known issues of updating heights in DOM. - // Safari issues: - // https://github.com/Expensify/App/issues/12005 - // https://github.com/Expensify/App/issues/17824 - // https://github.com/Expensify/App/issues/20709 - width: '100%', - height: '100%', - position: 'fixed', -}); diff --git a/src/styles/getNavigationModalCardStyles/index.desktop.ts b/src/styles/getNavigationModalCardStyles/index.desktop.ts new file mode 100644 index 000000000000..422a17d0a9a8 --- /dev/null +++ b/src/styles/getNavigationModalCardStyles/index.desktop.ts @@ -0,0 +1,17 @@ +import {ViewStyle} from 'react-native'; +import GetNavigationModalCardStyles from './types'; + +const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ + // position: fixed is set instead of position absolute to workaround Safari known issues of updating heights in DOM. + // Safari issues: + // https://github.com/Expensify/App/issues/12005 + // https://github.com/Expensify/App/issues/17824 + // https://github.com/Expensify/App/issues/20709 + width: '100%', + height: '100%', + + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], +}); + +export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/types.ts b/src/styles/getNavigationModalCardStyles/types.ts index 68453dc3c3de..877981dd4dd2 100644 --- a/src/styles/getNavigationModalCardStyles/types.ts +++ b/src/styles/getNavigationModalCardStyles/types.ts @@ -1,8 +1,7 @@ import {ViewStyle} from 'react-native'; -import {Merge} from 'type-fest'; type GetNavigationModalCardStylesParams = {isSmallScreenWidth: number}; -type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => Merge>; +type GetNavigationModalCardStyles = (params: GetNavigationModalCardStylesParams) => ViewStyle; export default GetNavigationModalCardStyles; diff --git a/src/styles/getReportActionContextMenuStyles.ts b/src/styles/getReportActionContextMenuStyles.ts index 150c9786aaca..6b4ad8807552 100644 --- a/src/styles/getReportActionContextMenuStyles.ts +++ b/src/styles/getReportActionContextMenuStyles.ts @@ -3,13 +3,11 @@ import styles from './styles'; import themeColors from './themes/default'; import variables from './variables'; -type StylesArray = ViewStyle[]; - const defaultWrapperStyle: ViewStyle = { backgroundColor: themeColors.componentBG, }; -const miniWrapperStyle: StylesArray = [ +const miniWrapperStyle: ViewStyle[] = [ styles.flexRow, defaultWrapperStyle, { @@ -22,7 +20,7 @@ const miniWrapperStyle: StylesArray = [ }, ]; -const bigWrapperStyle: StylesArray = [styles.flexColumn, defaultWrapperStyle]; +const bigWrapperStyle: ViewStyle[] = [styles.flexColumn, defaultWrapperStyle]; /** * Generate the wrapper styles for the ReportActionContextMenu. @@ -30,7 +28,7 @@ const bigWrapperStyle: StylesArray = [styles.flexColumn, defaultWrapperStyle]; * @param isMini * @param isSmallScreenWidth */ -function getReportActionContextMenuStyles(isMini: boolean, isSmallScreenWidth: boolean): StylesArray { +function getReportActionContextMenuStyles(isMini: boolean, isSmallScreenWidth: boolean): ViewStyle[] { if (isMini) { return miniWrapperStyle; } diff --git a/src/styles/italic/types.ts b/src/styles/italic/types.ts index 0935c5844bb3..e9feedbdfac5 100644 --- a/src/styles/italic/types.ts +++ b/src/styles/italic/types.ts @@ -1,5 +1,5 @@ import {TextStyle} from 'react-native'; -type ItalicStyles = TextStyle['fontStyle']; +type ItalicStyles = NonNullable; export default ItalicStyles; diff --git a/src/styles/optionAlternateTextPlatformStyles/types.ts b/src/styles/optionAlternateTextPlatformStyles/types.ts index b2e8e4745fff..aacdef7e3501 100644 --- a/src/styles/optionAlternateTextPlatformStyles/types.ts +++ b/src/styles/optionAlternateTextPlatformStyles/types.ts @@ -1,5 +1,5 @@ import {TextStyle} from 'react-native'; -type OptionAlternateTextPlatformStyles = Partial>; +type OptionAlternateTextPlatformStyles = Pick; export default OptionAlternateTextPlatformStyles; diff --git a/src/styles/optionRowStyles/index.native.ts b/src/styles/optionRowStyles/index.native.ts index 11371509ce73..9c13fdd082a4 100644 --- a/src/styles/optionRowStyles/index.native.ts +++ b/src/styles/optionRowStyles/index.native.ts @@ -1,5 +1,5 @@ -import OptionRowStyles from './types'; import styles from '../styles'; +import CompactContentContainerStyles from './types'; /** * On native platforms, alignItemsBaseline does not work correctly @@ -7,8 +7,7 @@ import styles from '../styles'; * keeping compactContentContainerStyles as it is. * https://github.com/Expensify/App/issues/14148 */ - -const compactContentContainerStyles: OptionRowStyles = styles.alignItemsCenter; +const compactContentContainerStyles: CompactContentContainerStyles = styles.alignItemsCenter; export { // eslint-disable-next-line import/prefer-default-export diff --git a/src/styles/optionRowStyles/index.ts b/src/styles/optionRowStyles/index.ts index fbeca3c702d9..975f4243842e 100644 --- a/src/styles/optionRowStyles/index.ts +++ b/src/styles/optionRowStyles/index.ts @@ -1,7 +1,7 @@ -import OptionRowStyles from './types'; +import CompactContentContainerStyles from './types'; import styles from '../styles'; -const compactContentContainerStyles: OptionRowStyles = styles.alignItemsBaseline; +const compactContentContainerStyles: CompactContentContainerStyles = styles.alignItemsBaseline; export { // eslint-disable-next-line import/prefer-default-export diff --git a/src/styles/optionRowStyles/types.ts b/src/styles/optionRowStyles/types.ts index f645c6038397..fcce41f6bc28 100644 --- a/src/styles/optionRowStyles/types.ts +++ b/src/styles/optionRowStyles/types.ts @@ -1,5 +1,5 @@ import {ViewStyle} from 'react-native'; -type OptionRowStyles = ViewStyle; +type CompactContentContainerStyles = ViewStyle; -export default OptionRowStyles; +export default CompactContentContainerStyles; diff --git a/src/styles/utilities/cursor/types.ts b/src/styles/utilities/cursor/types.ts index 98d661491c4b..e9cfc120b161 100644 --- a/src/styles/utilities/cursor/types.ts +++ b/src/styles/utilities/cursor/types.ts @@ -1,4 +1,4 @@ -import {TextStyle, ViewStyle} from 'react-native'; +import {ViewStyle} from 'react-native'; type CursorStylesKeys = | 'cursorDefault' @@ -13,6 +13,6 @@ type CursorStylesKeys = | 'cursorInitial' | 'cursorText'; -type CursorStyles = Record>; +type CursorStyles = Record>; export default CursorStyles; diff --git a/src/styles/utilities/visibility/types.ts b/src/styles/utilities/visibility/types.ts index 872e35195edd..64bdbdd2cca6 100644 --- a/src/styles/utilities/visibility/types.ts +++ b/src/styles/utilities/visibility/types.ts @@ -1,5 +1,5 @@ -import {TextStyle, ViewStyle} from 'react-native'; +import {ViewStyle} from 'react-native'; -type VisibilityStyles = Record<'visible' | 'hidden', Pick>; +type VisibilityStyles = Record<'visible' | 'hidden', Pick>; export default VisibilityStyles; From e4a3313b9cd29ffe80589430c595bcf6b2bea605 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 15 Sep 2023 13:30:09 +0200 Subject: [PATCH 009/205] Migrate styles.ts --- src/styles/{styles.js => styles.ts} | 100 ++++++++----------- src/styles/themes/{default.js => default.ts} | 20 ++-- src/styles/themes/{light.js => light.ts} | 22 ++-- src/styles/themes/types.ts | 8 ++ src/types/utils/DeepRecord.ts | 7 ++ 5 files changed, 79 insertions(+), 78 deletions(-) rename src/styles/{styles.js => styles.ts} (97%) rename src/styles/themes/{default.js => default.ts} (88%) rename src/styles/themes/{light.js => light.ts} (86%) create mode 100644 src/styles/themes/types.ts create mode 100644 src/types/utils/DeepRecord.ts diff --git a/src/styles/styles.js b/src/styles/styles.ts similarity index 97% rename from src/styles/styles.js rename to src/styles/styles.ts index 0fba61f1e8d9..15c1a6b9269f 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.ts @@ -1,4 +1,3 @@ -import {defaultStyles as defaultPickerStyles} from 'react-native-picker-select/src/styles'; import lodashClamp from 'lodash/clamp'; import fontFamily from './fontFamily'; import addOutlineWidth from './addOutlineWidth'; @@ -26,11 +25,16 @@ import * as Browser from '../libs/Browser'; import cursor from './utilities/cursor'; import userSelect from './utilities/userSelect'; import textUnderline from './utilities/textUnderline'; +import {ThemeDefault} from './themes/types'; +import {AnimatableNumericValue, Animated, TransformsStyle} from 'react-native'; +import {ValueOf} from 'type-fest'; + +type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target const touchCalloutNone = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; -const picker = (theme) => ({ +const picker = (theme: ThemeDefault) => ({ backgroundColor: theme.transparent, color: theme.text, fontFamily: fontFamily.EXP_NEUE, @@ -45,13 +49,13 @@ const picker = (theme) => ({ textAlign: 'left', }); -const link = (theme) => ({ +const link = (theme: ThemeDefault) => ({ color: theme.link, textDecorationColor: theme.link, fontFamily: fontFamily.EXP_NEUE, }); -const baseCodeTagStyles = (theme) => ({ +const baseCodeTagStyles = (theme: ThemeDefault) => ({ borderWidth: 1, borderRadius: 5, borderColor: theme.border, @@ -61,9 +65,9 @@ const baseCodeTagStyles = (theme) => ({ const headlineFont = { fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, fontWeight: '500', -}; +} as const; -const webViewStyles = (theme) => ({ +const webViewStyles = (theme: ThemeDefault) => ({ // As of react-native-render-html v6, don't declare distinct styles for // custom renderers, the API for custom renderers has changed. Declare the // styles in the below "tagStyles" instead. If you need to reuse those @@ -156,7 +160,7 @@ const webViewStyles = (theme) => ({ }, }); -const styles = (theme) => ({ +const styles = (theme: ThemeDefault) => ({ // Add all of our utility and helper styles ...spacing, ...sizing, @@ -675,7 +679,7 @@ const styles = (theme) => ({ color: theme.text, }, doneDepressed: { - fontSize: defaultPickerStyles.done.fontSize, + fontSize: 17, }, modalViewMiddle: { backgroundColor: theme.border, @@ -792,7 +796,7 @@ const styles = (theme) => ({ color: theme.textSupporting, }, - uploadReceiptView: (isSmallScreenWidth) => ({ + uploadReceiptView: (isSmallScreenWidth: boolean) => ({ borderRadius: variables.componentBorderRadiusLarge, borderWidth: isSmallScreenWidth ? 0 : 2, borderColor: theme.borderFocus, @@ -922,18 +926,12 @@ const styles = (theme) => ({ backgroundColor: theme.buttonDefaultBG, }, - /** - * @param {number} textInputHeight - * @param {number} minHeight - * @param {number} maxHeight - * @returns {object} - */ - autoGrowHeightInputContainer: (textInputHeight, minHeight, maxHeight) => ({ + autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number) => ({ height: lodashClamp(textInputHeight, minHeight, maxHeight), minHeight, }), - autoGrowHeightHiddenInput: (maxWidth, maxHeight) => ({ + autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number) => ({ maxWidth, maxHeight: maxHeight && maxHeight + 1, overflow: 'hidden', @@ -971,7 +969,7 @@ const styles = (theme) => ({ transformOrigin: 'left center', }, - textInputLabelTransformation: (translateY, translateX, scale) => ({ + textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue) => ({ transform: [{translateY}, {translateX}, {scale}], }), @@ -1092,7 +1090,7 @@ const styles = (theme) => ({ color: theme.text, }, doneDepressed: { - fontSize: defaultPickerStyles.done.fontSize, + fontSize: 17, }, modalViewMiddle: { backgroundColor: theme.border, @@ -1358,7 +1356,7 @@ const styles = (theme) => ({ textDecorationLine: 'none', }, - RHPNavigatorContainer: (isSmallScreenWidth) => ({ + RHPNavigatorContainer: (isSmallScreenWidth: boolean) => ({ width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, position: 'absolute', right: 0, @@ -1374,17 +1372,17 @@ const styles = (theme) => ({ lineHeight: variables.fontSizeOnlyEmojisHeight, }, - createMenuPositionSidebar: (windowHeight) => ({ + createMenuPositionSidebar: (windowHeight: number) => ({ horizontal: 18, vertical: windowHeight - 100, }), - createMenuPositionProfile: (windowWidth) => ({ + createMenuPositionProfile: (windowWidth: number) => ({ horizontal: windowWidth - 355, ...getPopOverVerticalOffset(162), }), - createMenuPositionReportActionCompose: (windowHeight) => ({ + createMenuPositionReportActionCompose: (windowHeight: number) => ({ horizontal: 18 + variables.sideBarWidth, vertical: windowHeight - 83, }), @@ -1541,7 +1539,7 @@ const styles = (theme) => ({ height: variables.optionsListSectionHeaderHeight, }, - overlayStyles: (current) => ({ + overlayStyles: (current: {progress: Animated.AnimatedInterpolation}) => ({ position: 'fixed', // We need to stretch the overlay to cover the sidebar and the translate animation distance. @@ -2167,7 +2165,7 @@ const styles = (theme) => ({ outline: 'none', }, - getPDFPasswordFormStyle: (isSmallScreenWidth) => ({ + getPDFPasswordFormStyle: (isSmallScreenWidth: boolean) => ({ width: isSmallScreenWidth ? '100%' : 350, ...(isSmallScreenWidth && flex.flex1), }), @@ -2180,7 +2178,7 @@ const styles = (theme) => ({ backgroundColor: theme.modalBackdrop, }, - centeredModalStyles: (isSmallScreenWidth, isFullScreenWhenSmall) => ({ + centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean) => ({ borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, marginHorizontal: isSmallScreenWidth ? 0 : 20, }), @@ -2269,7 +2267,6 @@ const styles = (theme) => ({ }, reportDetailsTitleContainer: { - ...flex.dFlex, ...flex.flexColumn, ...flex.alignItemsCenter, paddingHorizontal: 20, @@ -2278,7 +2275,6 @@ const styles = (theme) => ({ reportDetailsRoomInfo: { ...flex.flex1, - ...flex.dFlex, ...flex.flexColumn, ...flex.alignItemsCenter, }, @@ -2315,15 +2311,15 @@ const styles = (theme) => ({ padding: 0, }, - twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}) => { - let paddingHorizontal = styles.ph9; + twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}) => { + let paddingHorizontal = spacing.ph9; if (isSmallScreenWidth) { - paddingHorizontal = styles.ph4; + paddingHorizontal = spacing.ph4; } if (isExtraSmallScreenWidth) { - paddingHorizontal = styles.ph2; + paddingHorizontal = spacing.ph2; } return { @@ -2373,7 +2369,7 @@ const styles = (theme) => ({ minWidth: 110, }, - anonymousRoomFooter: (isSmallSizeLayout) => ({ + anonymousRoomFooter: (isSmallSizeLayout: boolean) => ({ flexDirection: isSmallSizeLayout ? 'column' : 'row', ...(!isSmallSizeLayout && { alignItems: 'center', @@ -2384,7 +2380,7 @@ const styles = (theme) => ({ borderRadius: variables.componentBorderRadiusLarge, overflow: 'hidden', }), - anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout) => ({ + anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean) => ({ flexDirection: 'row', alignItems: 'center', ...(isSmallSizeLayout && { @@ -2433,8 +2429,8 @@ const styles = (theme) => ({ borderRadius: 88, }, - rootNavigatorContainerStyles: (isSmallScreenWidth) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), + rootNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), + RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), avatarInnerTextChat: { color: theme.textLight, @@ -2619,7 +2615,7 @@ const styles = (theme) => ({ backgroundColor: theme.appBG, }, - switchThumbTransformation: (translateX) => ({ + switchThumbTransformation: (translateX: AnimatableNumericValue) => ({ transform: [{translateX}], }), @@ -2831,11 +2827,11 @@ const styles = (theme) => ({ position: 'fixed', }, - growlNotificationTranslateY: (y) => ({ - transform: [{translateY: y}], + growlNotificationTranslateY: (translateY: AnimatableNumericValue) => ({ + transform: [{translateY}], }), - makeSlideInTranslation: (translationType, fromValue) => ({ + makeSlideInTranslation: (translationType: Translation, fromValue: number) => ({ from: { [translationType]: fromValue, }, @@ -3135,7 +3131,7 @@ const styles = (theme) => ({ ...visibility.visible, }, - floatingMessageCounterTransformation: (translateY) => ({ + floatingMessageCounterTransformation: (translateY: AnimatableNumericValue) => ({ transform: [{translateY}], }), @@ -3162,12 +3158,12 @@ const styles = (theme) => ({ flex: 1, }, - threeDotsPopoverOffset: (windowWidth) => ({ + threeDotsPopoverOffset: (windowWidth: number) => ({ ...getPopOverVerticalOffset(60), horizontal: windowWidth - 60, }), - threeDotsPopoverOffsetNoCloseButton: (windowWidth) => ({ + threeDotsPopoverOffsetNoCloseButton: (windowWidth: number) => ({ ...getPopOverVerticalOffset(60), horizontal: windowWidth - 10, }), @@ -3310,7 +3306,7 @@ const styles = (theme) => ({ zIndex: 2, }, - receiptImageWrapper: (receiptImageTopPosition) => ({ + receiptImageWrapper: (receiptImageTopPosition: number) => ({ position: 'absolute', top: receiptImageTopPosition, }), @@ -3579,8 +3575,6 @@ const styles = (theme) => ({ taskTitleMenuItem: { ...writingDirection.ltr, ...headlineFont, - ...spacing.flexWrap, - ...spacing.flex1, fontSize: variables.fontSizeXLarge, maxWidth: '100%', ...wordBreak.breakWord, @@ -3622,8 +3616,6 @@ const styles = (theme) => ({ marginLeft: 'auto', ...spacing.mt1, ...pointerEventsAuto, - ...spacing.dFlex, - ...spacing.alignItemsCenter, }, shareCodePage: { @@ -3735,19 +3727,14 @@ const styles = (theme) => ({ paddingBottom: 12, }, - tabText: (isSelected) => ({ + tabText: (isSelected: boolean) => ({ marginLeft: 8, fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE, fontWeight: isSelected ? fontWeightBold : 400, color: isSelected ? theme.textLight : theme.textSupporting, }), - /** - * @param {String} backgroundColor - * @param {Number} height - * @returns {Object} - */ - overscrollSpacer: (backgroundColor, height) => ({ + overscrollSpacer: (backgroundColor: string, height: number) => ({ backgroundColor, height, width: '100%', @@ -3910,7 +3897,7 @@ const styles = (theme) => ({ maxWidth: 400, }, - distanceRequestContainer: (maxHeight) => ({ + distanceRequestContainer: (maxHeight: number) => ({ ...flex.flexShrink2, minHeight: variables.optionRowHeight * 2, maxHeight, @@ -3919,7 +3906,6 @@ const styles = (theme) => ({ mapViewContainer: { ...flex.flex1, ...spacing.p4, - ...spacing.flex1, minHeight: 300, maxHeight: 500, }, diff --git a/src/styles/themes/default.js b/src/styles/themes/default.ts similarity index 88% rename from src/styles/themes/default.js rename to src/styles/themes/default.ts index c101a668666b..9b6cca499ae5 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.ts @@ -2,6 +2,7 @@ import colors from '../colors'; import SCREENS from '../../SCREENS'; import ROUTES from '../../ROUTES'; +import type {ThemeBase} from './types'; const darkTheme = { // Figma keys @@ -82,15 +83,14 @@ const darkTheme = { skeletonLHNOut: colors.darkDefaultButton, QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', -}; - -darkTheme.PAGE_BACKGROUND_COLORS = { - [SCREENS.HOME]: darkTheme.sidebar, - [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, - [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, - [ROUTES.SETTINGS_STATUS]: colors.green700, - [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, - [ROUTES.SETTINGS_SECURITY]: colors.ice500, -}; + PAGE_BACKGROUND_COLORS: { + [SCREENS.HOME]: colors.darkHighlightBackground, + [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, + [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, + [ROUTES.SETTINGS_STATUS]: colors.green700, + [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, + [ROUTES.SETTINGS_SECURITY]: colors.ice500, + }, +} satisfies ThemeBase; export default darkTheme; diff --git a/src/styles/themes/light.js b/src/styles/themes/light.ts similarity index 86% rename from src/styles/themes/light.js rename to src/styles/themes/light.ts index 1a945cb84913..a14d6b0b89b4 100644 --- a/src/styles/themes/light.js +++ b/src/styles/themes/light.ts @@ -1,6 +1,7 @@ import colors from '../colors'; import SCREENS from '../../SCREENS'; import ROUTES from '../../ROUTES'; +import type {ThemeDefault} from './types'; const lightTheme = { // Figma keys @@ -62,7 +63,7 @@ const lightTheme = { heroCard: colors.blue400, uploadPreviewActivityIndicator: colors.lightHighlightBackground, dropUIBG: 'rgba(252, 251, 249, 0.92)', - dropTransparentOverlay: 'rgba(255,255,255,0)', + receiptDropUIBG: '', // TODO: add color checkBox: colors.green400, pickerOptionsTextColor: colors.lightPrimaryText, imageCropBackgroundColor: colors.lightIcons, @@ -81,15 +82,14 @@ const lightTheme = { skeletonLHNOut: colors.lightDefaultButtonPressed, QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', -}; - -lightTheme.PAGE_BACKGROUND_COLORS = { - [SCREENS.HOME]: lightTheme.sidebar, - [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, - [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, - [ROUTES.SETTINGS_STATUS]: colors.green700, - [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, - [ROUTES.SETTINGS_SECURITY]: colors.ice500, -}; + PAGE_BACKGROUND_COLORS: { + [SCREENS.HOME]: colors.lightHighlightBackground, + [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, + [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, + [ROUTES.SETTINGS_STATUS]: colors.green700, + [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, + [ROUTES.SETTINGS_SECURITY]: colors.ice500, + }, +} satisfies ThemeDefault; export default lightTheme; diff --git a/src/styles/themes/types.ts b/src/styles/themes/types.ts new file mode 100644 index 000000000000..40b8da361654 --- /dev/null +++ b/src/styles/themes/types.ts @@ -0,0 +1,8 @@ +import DeepRecord from '../../types/utils/DeepRecord'; +import defaultTheme from './default'; + +type ThemeBase = DeepRecord; + +type ThemeDefault = typeof defaultTheme; + +export type {ThemeBase, ThemeDefault}; diff --git a/src/types/utils/DeepRecord.ts b/src/types/utils/DeepRecord.ts new file mode 100644 index 000000000000..fba14c75d679 --- /dev/null +++ b/src/types/utils/DeepRecord.ts @@ -0,0 +1,7 @@ +/** + * Represents a deeply nested record. It maps keys to values, + * and those values can either be of type `TValue` or further nested `DeepRecord` instances. + */ +type DeepRecord = {[key: string]: TValue | DeepRecord}; + +export default DeepRecord; From b8d36f774f04737efe9f04b7524beb39f848db1d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 15 Sep 2023 13:31:10 +0200 Subject: [PATCH 010/205] Remove unused imports --- src/styles/styles.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 15c1a6b9269f..88b6b161b122 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -26,8 +26,7 @@ import cursor from './utilities/cursor'; import userSelect from './utilities/userSelect'; import textUnderline from './utilities/textUnderline'; import {ThemeDefault} from './themes/types'; -import {AnimatableNumericValue, Animated, TransformsStyle} from 'react-native'; -import {ValueOf} from 'type-fest'; +import {AnimatableNumericValue, Animated} from 'react-native'; type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; From 24f363bcd791a037a077cc358dbdb0fbe57f6fd4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 15 Sep 2023 14:01:45 +0200 Subject: [PATCH 011/205] Fix return types in styles.ts --- src/styles/styles.ts | 95 ++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 88b6b161b122..927dda71626c 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -1,4 +1,10 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import lodashClamp from 'lodash/clamp'; +import {AnimatableNumericValue, Animated, TextStyle, ViewStyle} from 'react-native'; +import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; +import {PickerStyle} from 'react-native-picker-select'; +import {CustomAnimation} from 'react-native-animatable'; +import {EmptyObject} from 'type-fest'; import fontFamily from './fontFamily'; import addOutlineWidth from './addOutlineWidth'; import defaultTheme from './themes/default'; @@ -26,14 +32,13 @@ import cursor from './utilities/cursor'; import userSelect from './utilities/userSelect'; import textUnderline from './utilities/textUnderline'; import {ThemeDefault} from './themes/types'; -import {AnimatableNumericValue, Animated} from 'react-native'; type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target -const touchCalloutNone = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; +const touchCalloutNone: {WebkitTouchCallout: 'none'} | EmptyObject = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; -const picker = (theme: ThemeDefault) => ({ +const picker = (theme: ThemeDefault): TextStyle => ({ backgroundColor: theme.transparent, color: theme.text, fontFamily: fontFamily.EXP_NEUE, @@ -48,13 +53,13 @@ const picker = (theme: ThemeDefault) => ({ textAlign: 'left', }); -const link = (theme: ThemeDefault) => ({ +const link = (theme: ThemeDefault): ViewStyle & MixedStyleDeclaration => ({ color: theme.link, textDecorationColor: theme.link, fontFamily: fontFamily.EXP_NEUE, }); -const baseCodeTagStyles = (theme: ThemeDefault) => ({ +const baseCodeTagStyles = (theme: ThemeDefault): ViewStyle & MixedStyleDeclaration => ({ borderWidth: 1, borderRadius: 5, borderColor: theme.border, @@ -66,7 +71,12 @@ const headlineFont = { fontWeight: '500', } as const; -const webViewStyles = (theme: ThemeDefault) => ({ +type WebViewStyles = { + tagStyles: MixedStyleRecord; + baseFontStyle: MixedStyleDeclaration; +}; + +const webViewStyles = (theme: ThemeDefault): WebViewStyles => ({ // As of react-native-render-html v6, don't declare distinct styles for // custom renderers, the API for custom renderers has changed. Declare the // styles in the below "tagStyles" instead. If you need to reuse those @@ -126,7 +136,7 @@ const webViewStyles = (theme: ThemeDefault) => ({ code: { ...baseCodeTagStyles(theme), - ...codeStyles.codeTextStyle, + ...(codeStyles.codeTextStyle as MixedStyleDeclaration), paddingLeft: 5, paddingRight: 5, fontFamily: fontFamily.MONOSPACE, @@ -159,6 +169,11 @@ const webViewStyles = (theme: ThemeDefault) => ({ }, }); +type AnchorPosition = { + horizontal: number; + vertical: number; +}; + const styles = (theme: ThemeDefault) => ({ // Add all of our utility and helper styles ...spacing, @@ -660,7 +675,7 @@ const styles = (theme: ThemeDefault) => ({ height: 140, }, - pickerSmall: (backgroundColor = theme.highlightBG) => ({ + pickerSmall: (backgroundColor = theme.highlightBG): PickerStyle & {icon: ViewStyle} => ({ inputIOS: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSmall, @@ -795,7 +810,7 @@ const styles = (theme: ThemeDefault) => ({ color: theme.textSupporting, }, - uploadReceiptView: (isSmallScreenWidth: boolean) => ({ + uploadReceiptView: (isSmallScreenWidth: boolean): ViewStyle => ({ borderRadius: variables.componentBorderRadiusLarge, borderWidth: isSmallScreenWidth ? 0 : 2, borderColor: theme.borderFocus, @@ -925,12 +940,12 @@ const styles = (theme: ThemeDefault) => ({ backgroundColor: theme.buttonDefaultBG, }, - autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number) => ({ + autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number): ViewStyle => ({ height: lodashClamp(textInputHeight, minHeight, maxHeight), minHeight, }), - autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number) => ({ + autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number): TextStyle => ({ maxWidth, maxHeight: maxHeight && maxHeight + 1, overflow: 'hidden', @@ -968,7 +983,7 @@ const styles = (theme: ThemeDefault) => ({ transformOrigin: 'left center', }, - textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue) => ({ + textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue): TextStyle => ({ transform: [{translateY}, {translateX}, {scale}], }), @@ -1068,7 +1083,7 @@ const styles = (theme: ThemeDefault) => ({ zIndex: 1, }, - picker: (disabled = false, backgroundColor = theme.appBG) => ({ + picker: (disabled = false, backgroundColor = theme.appBG): PickerStyle => ({ iconContainer: { top: Math.round(variables.inputHeight * 0.5) - 11, right: 0, @@ -1296,7 +1311,7 @@ const styles = (theme: ThemeDefault) => ({ width: variables.componentSizeNormal, }, - statusIndicator: (backgroundColor = theme.danger) => ({ + statusIndicator: (backgroundColor = theme.danger): ViewStyle => ({ borderColor: theme.sidebar, backgroundColor, borderRadius: 8, @@ -1355,7 +1370,7 @@ const styles = (theme: ThemeDefault) => ({ textDecorationLine: 'none', }, - RHPNavigatorContainer: (isSmallScreenWidth: boolean) => ({ + RHPNavigatorContainer: (isSmallScreenWidth: boolean): ViewStyle => ({ width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, position: 'absolute', right: 0, @@ -1371,17 +1386,17 @@ const styles = (theme: ThemeDefault) => ({ lineHeight: variables.fontSizeOnlyEmojisHeight, }, - createMenuPositionSidebar: (windowHeight: number) => ({ + createMenuPositionSidebar: (windowHeight: number): AnchorPosition => ({ horizontal: 18, vertical: windowHeight - 100, }), - createMenuPositionProfile: (windowWidth: number) => ({ + createMenuPositionProfile: (windowWidth: number): AnchorPosition => ({ horizontal: windowWidth - 355, ...getPopOverVerticalOffset(162), }), - createMenuPositionReportActionCompose: (windowHeight: number) => ({ + createMenuPositionReportActionCompose: (windowHeight: number): AnchorPosition => ({ horizontal: 18 + variables.sideBarWidth, vertical: windowHeight - 83, }), @@ -1538,8 +1553,9 @@ const styles = (theme: ThemeDefault) => ({ height: variables.optionsListSectionHeaderHeight, }, - overlayStyles: (current: {progress: Animated.AnimatedInterpolation}) => ({ - position: 'fixed', + overlayStyles: (current: {progress: Animated.AnimatedInterpolation}): ViewStyle => ({ + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], // We need to stretch the overlay to cover the sidebar and the translate animation distance. left: -2 * variables.sideBarWidth, @@ -2164,7 +2180,7 @@ const styles = (theme: ThemeDefault) => ({ outline: 'none', }, - getPDFPasswordFormStyle: (isSmallScreenWidth: boolean) => ({ + getPDFPasswordFormStyle: (isSmallScreenWidth: boolean): ViewStyle => ({ width: isSmallScreenWidth ? '100%' : 350, ...(isSmallScreenWidth && flex.flex1), }), @@ -2177,7 +2193,7 @@ const styles = (theme: ThemeDefault) => ({ backgroundColor: theme.modalBackdrop, }, - centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean) => ({ + centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean): ViewStyle => ({ borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, marginHorizontal: isSmallScreenWidth ? 0 : 20, }), @@ -2310,7 +2326,7 @@ const styles = (theme: ThemeDefault) => ({ padding: 0, }, - twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}) => { + twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}): ViewStyle => { let paddingHorizontal = spacing.ph9; if (isSmallScreenWidth) { @@ -2368,7 +2384,7 @@ const styles = (theme: ThemeDefault) => ({ minWidth: 110, }, - anonymousRoomFooter: (isSmallSizeLayout: boolean) => ({ + anonymousRoomFooter: (isSmallSizeLayout: boolean): ViewStyle & TextStyle => ({ flexDirection: isSmallSizeLayout ? 'column' : 'row', ...(!isSmallSizeLayout && { alignItems: 'center', @@ -2379,7 +2395,7 @@ const styles = (theme: ThemeDefault) => ({ borderRadius: variables.componentBorderRadiusLarge, overflow: 'hidden', }), - anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean) => ({ + anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean): ViewStyle => ({ flexDirection: 'row', alignItems: 'center', ...(isSmallSizeLayout && { @@ -2428,8 +2444,8 @@ const styles = (theme: ThemeDefault) => ({ borderRadius: 88, }, - rootNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), + rootNavigatorContainerStyles: (isSmallScreenWidth: boolean): ViewStyle => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), + RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean): ViewStyle => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), avatarInnerTextChat: { color: theme.textLight, @@ -2614,7 +2630,7 @@ const styles = (theme: ThemeDefault) => ({ backgroundColor: theme.appBG, }, - switchThumbTransformation: (translateX: AnimatableNumericValue) => ({ + switchThumbTransformation: (translateX: AnimatableNumericValue): ViewStyle => ({ transform: [{translateX}], }), @@ -2823,14 +2839,15 @@ const styles = (theme: ThemeDefault) => ({ growlNotificationDesktopContainer: { maxWidth: variables.sideBarWidth, right: 0, - position: 'fixed', + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], }, - growlNotificationTranslateY: (translateY: AnimatableNumericValue) => ({ + growlNotificationTranslateY: (translateY: AnimatableNumericValue): ViewStyle => ({ transform: [{translateY}], }), - makeSlideInTranslation: (translationType: Translation, fromValue: number) => ({ + makeSlideInTranslation: (translationType: Translation, fromValue: number): CustomAnimation => ({ from: { [translationType]: fromValue, }, @@ -3130,7 +3147,7 @@ const styles = (theme: ThemeDefault) => ({ ...visibility.visible, }, - floatingMessageCounterTransformation: (translateY: AnimatableNumericValue) => ({ + floatingMessageCounterTransformation: (translateY: AnimatableNumericValue): ViewStyle => ({ transform: [{translateY}], }), @@ -3157,12 +3174,12 @@ const styles = (theme: ThemeDefault) => ({ flex: 1, }, - threeDotsPopoverOffset: (windowWidth: number) => ({ + threeDotsPopoverOffset: (windowWidth: number): AnchorPosition => ({ ...getPopOverVerticalOffset(60), horizontal: windowWidth - 60, }), - threeDotsPopoverOffsetNoCloseButton: (windowWidth: number) => ({ + threeDotsPopoverOffsetNoCloseButton: (windowWidth: number): AnchorPosition => ({ ...getPopOverVerticalOffset(60), horizontal: windowWidth - 10, }), @@ -3305,7 +3322,7 @@ const styles = (theme: ThemeDefault) => ({ zIndex: 2, }, - receiptImageWrapper: (receiptImageTopPosition: number) => ({ + receiptImageWrapper: (receiptImageTopPosition: number): ViewStyle => ({ position: 'absolute', top: receiptImageTopPosition, }), @@ -3726,14 +3743,14 @@ const styles = (theme: ThemeDefault) => ({ paddingBottom: 12, }, - tabText: (isSelected: boolean) => ({ + tabText: (isSelected: boolean): TextStyle => ({ marginLeft: 8, fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE, - fontWeight: isSelected ? fontWeightBold : 400, + fontWeight: isSelected ? fontWeightBold : '400', color: isSelected ? theme.textLight : theme.textSupporting, }), - overscrollSpacer: (backgroundColor: string, height: number) => ({ + overscrollSpacer: (backgroundColor: string, height: number): ViewStyle => ({ backgroundColor, height, width: '100%', @@ -3896,7 +3913,7 @@ const styles = (theme: ThemeDefault) => ({ maxWidth: 400, }, - distanceRequestContainer: (maxHeight: number) => ({ + distanceRequestContainer: (maxHeight: number): ViewStyle => ({ ...flex.flexShrink2, minHeight: variables.optionRowHeight * 2, maxHeight, From 1be0bf59b738c433a78e7fba83d6a5c3b910691e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Fri, 15 Sep 2023 17:16:07 +0100 Subject: [PATCH 012/205] Minor fixes in styles.ts --- src/styles/styles.ts | 56 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index b55cfd9ad497..93961b20b31f 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -1,42 +1,45 @@ /* eslint-disable @typescript-eslint/naming-convention */ import lodashClamp from 'lodash/clamp'; import {AnimatableNumericValue, Animated, TextStyle, ViewStyle} from 'react-native'; -import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; -import {PickerStyle} from 'react-native-picker-select'; import {CustomAnimation} from 'react-native-animatable'; -import {EmptyObject} from 'type-fest'; -import fontFamily from './fontFamily'; +import {PickerStyle} from 'react-native-picker-select'; +import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; +import CONST from '../CONST'; +import * as Browser from '../libs/Browser'; import addOutlineWidth from './addOutlineWidth'; -import defaultTheme from './themes/default'; +import codeStyles from './codeStyles'; +import fontFamily from './fontFamily'; import fontWeightBold from './fontWeight/bold'; -import variables from './variables'; -import spacing from './utilities/spacing'; -import sizing from './utilities/sizing'; -import flex from './utilities/flex'; +import getPopOverVerticalOffset from './getPopOverVerticalOffset'; +import optionAlternateTextPlatformStyles from './optionAlternateTextPlatformStyles'; +import overflowXHidden from './overflowXHidden'; +import pointerEventsAuto from './pointerEventsAuto'; +import pointerEventsNone from './pointerEventsNone'; +import defaultTheme from './themes/default'; +import {ThemeDefault} from './themes/types'; +import cursor from './utilities/cursor'; import display from './utilities/display'; +import flex from './utilities/flex'; import overflow from './utilities/overflow'; -import whiteSpace from './utilities/whiteSpace'; -import wordBreak from './utilities/wordBreak'; import positioning from './utilities/positioning'; -import codeStyles from './codeStyles'; +import sizing from './utilities/sizing'; +import spacing from './utilities/spacing'; +import textUnderline from './utilities/textUnderline'; +import userSelect from './utilities/userSelect'; import visibility from './utilities/visibility'; +import whiteSpace from './utilities/whiteSpace'; +import wordBreak from './utilities/wordBreak'; import writingDirection from './utilities/writingDirection'; -import optionAlternateTextPlatformStyles from './optionAlternateTextPlatformStyles'; -import pointerEventsNone from './pointerEventsNone'; -import pointerEventsAuto from './pointerEventsAuto'; -import getPopOverVerticalOffset from './getPopOverVerticalOffset'; -import overflowXHidden from './overflowXHidden'; -import CONST from '../CONST'; -import * as Browser from '../libs/Browser'; -import cursor from './utilities/cursor'; -import userSelect from './utilities/userSelect'; -import textUnderline from './utilities/textUnderline'; -import {ThemeDefault} from './themes/types'; +import variables from './variables'; + +type OverlayStylesParams = {progress: Animated.AnimatedInterpolation}; + +type TwoFactorAuthCodesBoxParams = {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}; type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target -const touchCalloutNone: {WebkitTouchCallout: 'none'} | EmptyObject = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; +const touchCalloutNone: Pick = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; const picker = (theme: ThemeDefault): TextStyle => ({ backgroundColor: theme.transparent, @@ -693,6 +696,7 @@ const styles = (theme: ThemeDefault) => ({ color: theme.text, }, doneDepressed: { + // Extracted from react-native-picker-select, src/styles.js fontSize: 17, }, modalViewMiddle: { @@ -1553,7 +1557,7 @@ const styles = (theme: ThemeDefault) => ({ height: variables.optionsListSectionHeaderHeight, }, - overlayStyles: (current: {progress: Animated.AnimatedInterpolation}): ViewStyle => ({ + overlayStyles: (current: OverlayStylesParams): ViewStyle => ({ // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". position: 'fixed' as ViewStyle['position'], @@ -2326,7 +2330,7 @@ const styles = (theme: ThemeDefault) => ({ padding: 0, }, - twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}): ViewStyle => { + twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: TwoFactorAuthCodesBoxParams): ViewStyle => { let paddingHorizontal = spacing.ph9; if (isSmallScreenWidth) { From 1338157f3a87cccde5be0b41aa45a0c60459feca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Fri, 15 Sep 2023 23:47:15 +0100 Subject: [PATCH 013/205] Add stricter types to styles.ts --- src/styles/ThemeStylesProvider.tsx | 2 +- src/styles/getModalStyles.ts | 6 +- src/styles/styles.ts | 7650 ++++++++++++++-------------- src/styles/utilities/display.ts | 5 + 4 files changed, 3861 insertions(+), 3802 deletions(-) diff --git a/src/styles/ThemeStylesProvider.tsx b/src/styles/ThemeStylesProvider.tsx index d0db784ca8ca..4e6f91baf34a 100644 --- a/src/styles/ThemeStylesProvider.tsx +++ b/src/styles/ThemeStylesProvider.tsx @@ -5,7 +5,7 @@ import ThemeStylesContext from './ThemeStylesContext'; // TODO: Rename this to "styles" once the app is migrated to theme switching hooks and HOCs import {stylesGenerator as stylesUntyped} from './styles'; -const styles = stylesUntyped as (theme: Record) => Record; +const styles = stylesUntyped; type ThemeStylesProviderProps = { children: React.ReactNode; diff --git a/src/styles/getModalStyles.ts b/src/styles/getModalStyles.ts index ceea37ddb85b..d52d29568c2d 100644 --- a/src/styles/getModalStyles.ts +++ b/src/styles/getModalStyles.ts @@ -7,9 +7,11 @@ import themeColors from './themes/default'; import variables from './variables'; function getCenteredModalStyles(windowWidth: number, isSmallScreenWidth: boolean, isFullScreenWhenSmall = false): ViewStyle { + const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall); + return { - borderWidth: styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall).borderWidth, - width: isSmallScreenWidth ? '100%' : windowWidth - styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall).marginHorizontal * 2, + borderWidth: modalStyles.borderWidth, + width: isSmallScreenWidth ? '100%' : windowWidth - modalStyles.marginHorizontal * 2, }; } diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 93961b20b31f..c93e96c8fbee 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import {LineLayerStyleProps} from '@rnmapbox/maps/src/utils/MapboxStyles'; import lodashClamp from 'lodash/clamp'; -import {AnimatableNumericValue, Animated, TextStyle, ViewStyle} from 'react-native'; +import {LineLayer} from 'react-map-gl'; +import {AnimatableNumericValue, Animated, ImageStyle, TextStyle, ViewStyle} from 'react-native'; import {CustomAnimation} from 'react-native-animatable'; import {PickerStyle} from 'react-native-picker-select'; import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; @@ -32,3960 +34,4010 @@ import wordBreak from './utilities/wordBreak'; import writingDirection from './utilities/writingDirection'; import variables from './variables'; +type AnchorPosition = { + horizontal: number; + vertical: number; +}; + +type WebViewStyle = { + tagStyles: MixedStyleRecord; + baseFontStyle: MixedStyleDeclaration; +}; + +type CustomPickerStyle = PickerStyle & {icon?: ViewStyle}; + type OverlayStylesParams = {progress: Animated.AnimatedInterpolation}; type TwoFactorAuthCodesBoxParams = {isExtraSmallScreenWidth: boolean; isSmallScreenWidth: boolean}; type Translation = 'perspective' | 'rotate' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scaleX' | 'scaleY' | 'translateX' | 'translateY' | 'skewX' | 'skewY' | 'matrix'; +type OfflineFeedbackStyle = Record<'deleted' | 'pending' | 'error' | 'container' | 'textContainer' | 'text' | 'errorDot', ViewStyle | TextStyle>; + +type MapDirectionStyle = Pick; + +type MapDirectionLayerStyle = Pick; + +type Styles = Record< + string, + | ViewStyle + | TextStyle + | ImageStyle + | WebViewStyle + | OfflineFeedbackStyle + | MapDirectionStyle + | MapDirectionLayerStyle + | ((...args: any[]) => ViewStyle | TextStyle | ImageStyle | AnchorPosition | CustomAnimation | CustomPickerStyle) +>; + // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target const touchCalloutNone: Pick = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; -const picker = (theme: ThemeDefault): TextStyle => ({ - backgroundColor: theme.transparent, - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - lineHeight: variables.fontSizeNormalHeight, - paddingBottom: 8, - paddingTop: 23, - paddingLeft: 0, - paddingRight: 25, - height: variables.inputHeight, - borderWidth: 0, - textAlign: 'left', -}); - -const link = (theme: ThemeDefault): ViewStyle & MixedStyleDeclaration => ({ - color: theme.link, - textDecorationColor: theme.link, - fontFamily: fontFamily.EXP_NEUE, -}); - -const baseCodeTagStyles = (theme: ThemeDefault): ViewStyle & MixedStyleDeclaration => ({ - borderWidth: 1, - borderRadius: 5, - borderColor: theme.border, - backgroundColor: theme.textBackground, -}); +const picker = (theme: ThemeDefault) => + ({ + backgroundColor: theme.transparent, + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + lineHeight: variables.fontSizeNormalHeight, + paddingBottom: 8, + paddingTop: 23, + paddingLeft: 0, + paddingRight: 25, + height: variables.inputHeight, + borderWidth: 0, + textAlign: 'left', + } satisfies TextStyle); + +const link = (theme: ThemeDefault) => + ({ + color: theme.link, + textDecorationColor: theme.link, + fontFamily: fontFamily.EXP_NEUE, + } satisfies ViewStyle & MixedStyleDeclaration); + +const baseCodeTagStyles = (theme: ThemeDefault) => + ({ + borderWidth: 1, + borderRadius: 5, + borderColor: theme.border, + backgroundColor: theme.textBackground, + } satisfies ViewStyle & MixedStyleDeclaration); const headlineFont = { fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, fontWeight: '500', -} as const; +} satisfies TextStyle; + +const webViewStyles = (theme: ThemeDefault) => + ({ + // As of react-native-render-html v6, don't declare distinct styles for + // custom renderers, the API for custom renderers has changed. Declare the + // styles in the below "tagStyles" instead. If you need to reuse those + // styles from the renderer, just pass the "style" prop to the underlying + // component. + tagStyles: { + em: { + fontFamily: fontFamily.EXP_NEUE, + fontStyle: 'italic', + }, -type WebViewStyles = { - tagStyles: MixedStyleRecord; - baseFontStyle: MixedStyleDeclaration; -}; + del: { + textDecorationLine: 'line-through', + textDecorationStyle: 'solid', + }, + + strong: { + fontFamily: fontFamily.EXP_NEUE, + fontWeight: 'bold', + }, + + a: link(theme), + + ul: { + maxWidth: '100%', + }, + + ol: { + maxWidth: '100%', + }, + + li: { + flexShrink: 1, + }, + + blockquote: { + borderLeftColor: theme.border, + borderLeftWidth: 4, + paddingLeft: 12, + marginTop: 4, + marginBottom: 4, + + // Overwrite default HTML margin for blockquotes + marginLeft: 0, + }, -const webViewStyles = (theme: ThemeDefault): WebViewStyles => ({ - // As of react-native-render-html v6, don't declare distinct styles for - // custom renderers, the API for custom renderers has changed. Declare the - // styles in the below "tagStyles" instead. If you need to reuse those - // styles from the renderer, just pass the "style" prop to the underlying - // component. - tagStyles: { - em: { + pre: { + ...baseCodeTagStyles(theme), + paddingTop: 12, + paddingBottom: 12, + paddingRight: 8, + paddingLeft: 8, + fontFamily: fontFamily.MONOSPACE, + marginTop: 0, + marginBottom: 0, + }, + + code: { + ...baseCodeTagStyles(theme), + ...(codeStyles.codeTextStyle as MixedStyleDeclaration), + paddingLeft: 5, + paddingRight: 5, + fontFamily: fontFamily.MONOSPACE, + fontSize: 13, + }, + + img: { + borderColor: theme.border, + borderRadius: variables.componentBorderRadiusNormal, + borderWidth: 1, + ...touchCalloutNone, + }, + + p: { + marginTop: 0, + marginBottom: 0, + }, + h1: { + fontSize: variables.fontSizeLarge, + marginBottom: 8, + }, + }, + + baseFontStyle: { + color: theme.text, + fontSize: variables.fontSizeNormal, fontFamily: fontFamily.EXP_NEUE, - fontStyle: 'italic', + flex: 1, + lineHeight: variables.fontSizeNormalHeight, + }, + } satisfies WebViewStyle); + +const styles = (theme: ThemeDefault) => + ({ + // Add all of our utility and helper styles + ...spacing, + ...sizing, + ...flex, + ...display, + ...overflow, + ...positioning, + ...wordBreak, + ...whiteSpace, + ...writingDirection, + ...cursor, + ...userSelect, + ...textUnderline, + + rateCol: { + margin: 0, + padding: 0, + flexBasis: '48%', }, - del: { - textDecorationLine: 'line-through', - textDecorationStyle: 'solid', + autoCompleteSuggestionsContainer: { + backgroundColor: theme.appBG, + borderRadius: 8, + borderWidth: 1, + borderColor: theme.border, + justifyContent: 'center', + boxShadow: variables.popoverMenuShadow, + position: 'absolute', + left: 0, + right: 0, + paddingVertical: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_INNER_PADDING, }, - strong: { - fontFamily: fontFamily.EXP_NEUE, - fontWeight: 'bold', + autoCompleteSuggestionContainer: { + flexDirection: 'row', + alignItems: 'center', }, - a: link(theme), + emojiSuggestionsEmoji: { + fontSize: variables.fontSizeMedium, + width: 51, + textAlign: 'center', + }, + emojiSuggestionsText: { + fontSize: variables.fontSizeMedium, + flex: 1, + ...wordBreak.breakWord, + ...spacing.pr4, + }, - ul: { - maxWidth: '100%', + mentionSuggestionsAvatarContainer: { + width: 24, + height: 24, + alignItems: 'center', + justifyContent: 'center', }, - ol: { - maxWidth: '100%', + mentionSuggestionsText: { + fontSize: variables.fontSizeMedium, + ...spacing.ml2, }, - li: { - flexShrink: 1, + mentionSuggestionsDisplayName: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, }, - blockquote: { - borderLeftColor: theme.border, - borderLeftWidth: 4, - paddingLeft: 12, - marginTop: 4, - marginBottom: 4, + mentionSuggestionsHandle: { + color: theme.textSupporting, + }, - // Overwrite default HTML margin for blockquotes - marginLeft: 0, + appIconBorderRadius: { + overflow: 'hidden', + borderRadius: 12, }, - pre: { - ...baseCodeTagStyles(theme), - paddingTop: 12, - paddingBottom: 12, - paddingRight: 8, - paddingLeft: 8, - fontFamily: fontFamily.MONOSPACE, - marginTop: 0, - marginBottom: 0, + unitCol: { + margin: 0, + padding: 0, + marginLeft: '4%', + flexBasis: '48%', }, - code: { - ...baseCodeTagStyles(theme), - ...(codeStyles.codeTextStyle as MixedStyleDeclaration), - paddingLeft: 5, - paddingRight: 5, - fontFamily: fontFamily.MONOSPACE, - fontSize: 13, + webViewStyles: webViewStyles(theme), + + link: link(theme), + + linkMuted: { + color: theme.textSupporting, + textDecorationColor: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, }, - img: { - borderColor: theme.border, - borderRadius: variables.componentBorderRadiusNormal, - borderWidth: 1, - ...touchCalloutNone, + linkMutedHovered: { + color: theme.textMutedReversed, }, - p: { - marginTop: 0, - marginBottom: 0, + highlightBG: { + backgroundColor: theme.highlightBG, + }, + + appBG: { + backgroundColor: theme.appBG, }, + h1: { - fontSize: variables.fontSizeLarge, - marginBottom: 8, + color: theme.heading, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeh1, + fontWeight: fontWeightBold, }, - }, - baseFontStyle: { - color: theme.text, - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - flex: 1, - lineHeight: variables.fontSizeNormalHeight, - }, -}); + h3: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + }, -type AnchorPosition = { - horizontal: number; - vertical: number; -}; + h4: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeLabel, + fontWeight: fontWeightBold, + }, -const styles = (theme: ThemeDefault) => ({ - // Add all of our utility and helper styles - ...spacing, - ...sizing, - ...flex, - ...display, - ...overflow, - ...positioning, - ...wordBreak, - ...whiteSpace, - ...writingDirection, - ...cursor, - ...userSelect, - ...textUnderline, - ...theme, // TODO: Should we do this? - - rateCol: { - margin: 0, - padding: 0, - flexBasis: '48%', - }, - - autoCompleteSuggestionsContainer: { - backgroundColor: theme.appBG, - borderRadius: 8, - borderWidth: 1, - borderColor: theme.border, - justifyContent: 'center', - boxShadow: variables.popoverMenuShadow, - position: 'absolute', - left: 0, - right: 0, - paddingVertical: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_INNER_PADDING, - }, - - autoCompleteSuggestionContainer: { - flexDirection: 'row', - alignItems: 'center', - }, - - emojiSuggestionsEmoji: { - fontSize: variables.fontSizeMedium, - width: 51, - textAlign: 'center', - }, - emojiSuggestionsText: { - fontSize: variables.fontSizeMedium, - flex: 1, - ...wordBreak.breakWord, - ...spacing.pr4, - }, - - mentionSuggestionsAvatarContainer: { - width: 24, - height: 24, - alignItems: 'center', - justifyContent: 'center', - }, - - mentionSuggestionsText: { - fontSize: variables.fontSizeMedium, - ...spacing.ml2, - }, - - mentionSuggestionsDisplayName: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - }, - - mentionSuggestionsHandle: { - color: theme.textSupporting, - }, - - appIconBorderRadius: { - overflow: 'hidden', - borderRadius: 12, - }, - - unitCol: { - margin: 0, - padding: 0, - marginLeft: '4%', - flexBasis: '48%', - }, - - webViewStyles: webViewStyles(theme), - - link: link(theme), - - linkMuted: { - color: theme.textSupporting, - textDecorationColor: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - }, + textAlignCenter: { + textAlign: 'center', + }, - linkMutedHovered: { - color: theme.textMutedReversed, - }, + textAlignRight: { + textAlign: 'right', + }, - highlightBG: { - backgroundColor: theme.highlightBG, - }, + textAlignLeft: { + textAlign: 'left', + }, - appBG: { - backgroundColor: theme.appBG, - }, + textUnderline: { + textDecorationLine: 'underline', + }, - h1: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeh1, - fontWeight: fontWeightBold, - }, + label: { + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + }, - h3: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - }, + textLabel: { + color: theme.text, + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + }, - h4: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeLabel, - fontWeight: fontWeightBold, - }, + mutedTextLabel: { + color: theme.textSupporting, + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + }, - textAlignCenter: { - textAlign: 'center', - }, + textMicro: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, - textAlignRight: { - textAlign: 'right', - }, + textMicroBold: { + color: theme.text, + fontWeight: fontWeightBold, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, - textAlignLeft: { - textAlign: 'left', - }, + textMicroSupporting: { + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, - textUnderline: { - textDecorationLine: 'underline', - }, + textExtraSmallSupporting: { + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeExtraSmall, + }, - label: { - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - }, + textNormal: { + fontSize: variables.fontSizeNormal, + }, - textLabel: { - color: theme.text, - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - }, + textLarge: { + fontSize: variables.fontSizeLarge, + }, - mutedTextLabel: { - color: theme.textSupporting, - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - }, + textXLarge: { + fontSize: variables.fontSizeXLarge, + }, - textMicro: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, + textXXLarge: { + fontSize: variables.fontSizeXXLarge, + }, - textMicroBold: { - color: theme.text, - fontWeight: fontWeightBold, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, - - textMicroSupporting: { - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, + textXXXLarge: { + fontSize: variables.fontSizeXXXLarge, + }, - textExtraSmallSupporting: { - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeExtraSmall, - }, + textHero: { + fontSize: variables.fontSizeHero, + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + lineHeight: variables.lineHeightHero, + }, - textNormal: { - fontSize: variables.fontSizeNormal, - }, - - textLarge: { - fontSize: variables.fontSizeLarge, - }, - - textXLarge: { - fontSize: variables.fontSizeXLarge, - }, - - textXXLarge: { - fontSize: variables.fontSizeXXLarge, - }, - - textXXXLarge: { - fontSize: variables.fontSizeXXXLarge, - }, - - textHero: { - fontSize: variables.fontSizeHero, - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - lineHeight: variables.lineHeightHero, - }, - - textStrong: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - }, - - textItalic: { - fontFamily: fontFamily.EXP_NEUE_ITALIC, - fontStyle: 'italic', - }, - - textHeadline: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - }, - - textHeadlineH1: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - fontSize: variables.fontSizeh1, - lineHeight: variables.lineHeightSizeh1, - }, - - textDecorationNoLine: { - textDecorationLine: 'none', - }, - - textWhite: { - color: theme.textLight, - }, - - textBlue: { - color: theme.link, - }, - - textUppercase: { - textTransform: 'uppercase', - }, - - textNoWrap: { - ...whiteSpace.noWrap, - }, - - colorReversed: { - color: theme.textReversed, - }, - - colorMutedReversed: { - color: theme.textMutedReversed, - }, - - colorMuted: { - color: theme.textSupporting, - }, - - colorHeading: { - color: theme.heading, - }, - - bgTransparent: { - backgroundColor: 'transparent', - }, - - bgDark: { - backgroundColor: theme.inverse, - }, - - opacity0: { - opacity: 0, - }, - - opacity1: { - opacity: 1, - }, - - textDanger: { - color: theme.danger, - }, - - borderRadiusNormal: { - borderRadius: variables.buttonBorderRadius, - }, - - button: { - backgroundColor: theme.buttonDefaultBG, - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeLarge, - justifyContent: 'center', - ...spacing.ph3, - }, - - buttonContainer: { - padding: 1, - borderRadius: variables.buttonBorderRadius, - }, - - buttonText: { - color: theme.text, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - textAlign: 'center', - flexShrink: 1, - - // It is needed to unset the Lineheight. We don't need it for buttons as button always contains single line of text. - // It allows to vertically center the text. - lineHeight: undefined, - - // Add 1px to the Button text to give optical vertical alignment. - paddingBottom: 1, - }, - - buttonSmall: { - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeSmall, - paddingTop: 4, - paddingHorizontal: 14, - paddingBottom: 4, - backgroundColor: theme.buttonDefaultBG, - }, - - buttonMedium: { - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeNormal, - paddingTop: 12, - paddingRight: 16, - paddingBottom: 12, - paddingLeft: 16, - backgroundColor: theme.buttonDefaultBG, - }, - - buttonLarge: { - borderRadius: variables.buttonBorderRadius, - minHeight: variables.componentSizeLarge, - paddingTop: 8, - paddingRight: 10, - paddingBottom: 8, - paddingLeft: 18, - backgroundColor: theme.buttonDefaultBG, - }, - - buttonSmallText: { - fontSize: variables.fontSizeSmall, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - textAlign: 'center', - }, - - buttonMediumText: { - fontSize: variables.fontSizeLabel, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - textAlign: 'center', - }, - - buttonLargeText: { - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - textAlign: 'center', - }, + textStrong: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + }, - buttonDefaultHovered: { - backgroundColor: theme.buttonHoveredBG, - borderWidth: 0, - }, + textItalic: { + fontFamily: fontFamily.EXP_NEUE_ITALIC, + fontStyle: 'italic', + }, - buttonSuccess: { - backgroundColor: theme.success, - borderWidth: 0, - }, + textHeadline: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + }, - buttonOpacityDisabled: { - opacity: 0.5, - }, + textHeadlineH1: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + fontSize: variables.fontSizeh1, + lineHeight: variables.lineHeightSizeh1, + }, - buttonSuccessHovered: { - backgroundColor: theme.successHover, - borderWidth: 0, - }, + textDecorationNoLine: { + textDecorationLine: 'none', + }, - buttonDanger: { - backgroundColor: theme.danger, - borderWidth: 0, - }, + textWhite: { + color: theme.textLight, + }, - buttonDangerHovered: { - backgroundColor: theme.dangerHover, - borderWidth: 0, - }, + textBlue: { + color: theme.link, + }, - buttonDisabled: { - backgroundColor: theme.buttonDefaultBG, - borderWidth: 0, - }, - - buttonDivider: { - height: variables.dropDownButtonDividerHeight, - borderWidth: 0.7, - borderColor: theme.text, - }, - - noBorderRadius: { - borderRadius: 0, - }, - - noRightBorderRadius: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, - - noLeftBorderRadius: { - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - }, - - buttonCTA: { - paddingVertical: 6, - ...spacing.mh4, - }, - - buttonCTAIcon: { - marginRight: 22, - - // Align vertically with the Button text - paddingBottom: 1, - paddingTop: 1, - }, - - buttonConfirm: { - margin: 20, - }, - - attachmentButtonBigScreen: { - minWidth: 300, - alignSelf: 'center', - }, - - buttonConfirmText: { - paddingLeft: 20, - paddingRight: 20, - }, - - buttonSuccessText: { - color: theme.textLight, - }, - - buttonDangerText: { - color: theme.textLight, - }, - - hoveredComponentBG: { - backgroundColor: theme.hoverComponentBG, - }, - - activeComponentBG: { - backgroundColor: theme.activeComponentBG, - }, - - fontWeightBold: { - fontWeight: fontWeightBold, - }, - - touchableButtonImage: { - alignItems: 'center', - height: variables.componentSizeNormal, - justifyContent: 'center', - width: variables.componentSizeNormal, - }, - - visuallyHidden: { - ...visibility.hidden, - overflow: 'hidden', - width: 0, - height: 0, - }, - - visibilityHidden: { - ...visibility.hidden, - }, - - loadingVBAAnimation: { - width: 140, - height: 140, - }, - - pickerSmall: (backgroundColor = theme.highlightBG): PickerStyle & {icon: ViewStyle} => ({ - inputIOS: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - paddingLeft: 0, - paddingRight: 17, - paddingTop: 6, - paddingBottom: 6, - borderWidth: 0, - color: theme.text, - height: 26, - opacity: 1, - backgroundColor: 'transparent', + textUppercase: { + textTransform: 'uppercase', }, - done: { - color: theme.text, + + textNoWrap: { + ...whiteSpace.noWrap, }, - doneDepressed: { - // Extracted from react-native-picker-select, src/styles.js - fontSize: 17, + + colorReversed: { + color: theme.textReversed, }, - modalViewMiddle: { - backgroundColor: theme.border, - borderTopWidth: 0, + + colorMutedReversed: { + color: theme.textMutedReversed, }, - modalViewBottom: { - backgroundColor: theme.highlightBG, + + colorMuted: { + color: theme.textSupporting, }, - inputWeb: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - paddingLeft: 0, - paddingRight: 17, - paddingTop: 6, - paddingBottom: 6, - borderWidth: 0, - color: theme.text, - appearance: 'none', - height: 26, - opacity: 1, - backgroundColor, - ...cursor.cursorPointer, + + colorHeading: { + color: theme.heading, }, - inputAndroid: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - paddingLeft: 0, - paddingRight: 17, - paddingTop: 6, - paddingBottom: 6, - borderWidth: 0, - color: theme.text, - height: 26, - opacity: 1, + + bgTransparent: { backgroundColor: 'transparent', }, - iconContainer: { - top: 7, - ...pointerEventsNone, + + bgDark: { + backgroundColor: theme.inverse, }, - icon: { - width: variables.iconSizeExtraSmall, - height: variables.iconSizeExtraSmall, + + opacity0: { + opacity: 0, }, - }), - badge: { - backgroundColor: theme.border, - borderRadius: 14, - height: variables.iconSizeNormal, - flexDirection: 'row', - paddingHorizontal: 7, - alignItems: 'center', - }, + opacity1: { + opacity: 1, + }, - badgeSuccess: { - backgroundColor: theme.success, - }, + textDanger: { + color: theme.danger, + }, - badgeSuccessPressed: { - backgroundColor: theme.successHover, - }, + borderRadiusNormal: { + borderRadius: variables.buttonBorderRadius, + }, - badgeAdHocSuccess: { - backgroundColor: theme.badgeAdHoc, - }, + button: { + backgroundColor: theme.buttonDefaultBG, + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeLarge, + justifyContent: 'center', + ...spacing.ph3, + }, - badgeAdHocSuccessPressed: { - backgroundColor: theme.badgeAdHocHover, - }, + buttonContainer: { + padding: 1, + borderRadius: variables.buttonBorderRadius, + }, - badgeDanger: { - backgroundColor: theme.danger, - }, + buttonText: { + color: theme.text, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + textAlign: 'center', + flexShrink: 1, - badgeDangerPressed: { - backgroundColor: theme.dangerPressed, - }, + // It is needed to unset the Lineheight. We don't need it for buttons as button always contains single line of text. + // It allows to vertically center the text. + lineHeight: undefined, - badgeText: { - color: theme.text, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightNormal, - ...whiteSpace.noWrap, - }, + // Add 1px to the Button text to give optical vertical alignment. + paddingBottom: 1, + }, - border: { - borderWidth: 1, - borderRadius: variables.componentBorderRadius, - borderColor: theme.border, - }, - - borderColorFocus: { - borderColor: theme.borderFocus, - }, - - borderColorDanger: { - borderColor: theme.danger, - }, - - textInputDisabled: { - // Adding disabled color theme to indicate user that the field is not editable. - backgroundColor: theme.highlightBG, - borderBottomWidth: 2, - borderColor: theme.borderLighter, - // Adding browser specefic style to bring consistency between Safari and other platforms. - // Applying the Webkit styles only to browsers as it is not available in native. - ...(Browser.getBrowser() - ? { - WebkitTextFillColor: theme.textSupporting, - WebkitOpacity: 1, - } - : {}), - color: theme.textSupporting, - }, - - uploadReceiptView: (isSmallScreenWidth: boolean): ViewStyle => ({ - borderRadius: variables.componentBorderRadiusLarge, - borderWidth: isSmallScreenWidth ? 0 : 2, - borderColor: theme.borderFocus, - borderStyle: 'dotted', - marginBottom: 20, - marginLeft: 20, - marginRight: 20, - justifyContent: 'center', - alignItems: 'center', - padding: 40, - gap: 4, - flex: 1, - }), - - cameraView: { - flex: 1, - overflow: 'hidden', - padding: 10, - borderRadius: 28, - borderStyle: 'solid', - borderWidth: 8, - backgroundColor: theme.highlightBG, - borderColor: theme.appBG, - }, - - permissionView: { - paddingVertical: 108, - paddingHorizontal: 61, - alignItems: 'center', - justifyContent: 'center', - }, - - headerAnonymousFooter: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - }, - - headerText: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - }, + buttonSmall: { + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeSmall, + paddingTop: 4, + paddingHorizontal: 14, + paddingBottom: 4, + backgroundColor: theme.buttonDefaultBG, + }, - headerGap: { - height: CONST.DESKTOP_HEADER_PADDING, - }, + buttonMedium: { + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeNormal, + paddingTop: 12, + paddingRight: 16, + paddingBottom: 12, + paddingLeft: 16, + backgroundColor: theme.buttonDefaultBG, + }, - pushTextRight: { - left: 100000, - }, + buttonLarge: { + borderRadius: variables.buttonBorderRadius, + minHeight: variables.componentSizeLarge, + paddingTop: 8, + paddingRight: 10, + paddingBottom: 8, + paddingLeft: 18, + backgroundColor: theme.buttonDefaultBG, + }, - reportOptions: { - marginLeft: 8, - }, + buttonSmallText: { + fontSize: variables.fontSizeSmall, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + textAlign: 'center', + }, - chatItemComposeSecondaryRow: { - height: 15, - marginBottom: 5, - marginTop: 5, - }, + buttonMediumText: { + fontSize: variables.fontSizeLabel, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + textAlign: 'center', + }, - chatItemComposeSecondaryRowSubText: { - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, - }, - - chatItemComposeSecondaryRowOffset: { - marginLeft: variables.chatInputSpacing, - }, - - offlineIndicator: { - marginLeft: variables.chatInputSpacing, - }, - - offlineIndicatorMobile: { - paddingLeft: 20, - paddingTop: 5, - paddingBottom: 5, - }, - - offlineIndicatorRow: { - height: 25, - }, - - // Actions - actionAvatar: { - borderRadius: 20, - }, - - componentHeightLarge: { - height: variables.inputHeight, - }, - - calendarHeader: { - height: 50, - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: 15, - paddingRight: 5, - ...userSelect.userSelectNone, - }, - - calendarDayRoot: { - flex: 1, - height: 45, - justifyContent: 'center', - alignItems: 'center', - ...userSelect.userSelectNone, - }, - - calendarDayContainer: { - width: 30, - height: 30, - justifyContent: 'center', - alignItems: 'center', - borderRadius: 15, - overflow: 'hidden', - }, - - calendarDayContainerSelected: { - backgroundColor: theme.buttonDefaultBG, - }, - - autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number): ViewStyle => ({ - height: lodashClamp(textInputHeight, minHeight, maxHeight), - minHeight, - }), - - autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number): TextStyle => ({ - maxWidth, - maxHeight: maxHeight && maxHeight + 1, - overflow: 'hidden', - }), - - textInputContainer: { - flex: 1, - justifyContent: 'center', - height: '100%', - backgroundColor: 'transparent', - borderBottomWidth: 2, - borderColor: theme.border, - overflow: 'hidden', - }, + buttonLargeText: { + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + textAlign: 'center', + }, - textInputLabel: { - position: 'absolute', - left: 0, - top: 0, - fontSize: variables.fontSizeNormal, - color: theme.textSupporting, - fontFamily: fontFamily.EXP_NEUE, - width: '100%', - }, - - textInputLabelBackground: { - position: 'absolute', - top: 0, - width: '100%', - height: 23, - backgroundColor: theme.componentBG, - }, - - textInputLabelDesktop: { - transformOrigin: 'left center', - }, - - textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue): TextStyle => ({ - transform: [{translateY}, {translateX}, {scale}], - }), - - baseTextInput: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - lineHeight: variables.lineHeightXLarge, - color: theme.text, - paddingTop: 23, - paddingBottom: 8, - paddingLeft: 0, - borderWidth: 0, - }, + buttonDefaultHovered: { + backgroundColor: theme.buttonHoveredBG, + borderWidth: 0, + }, - textInputMultiline: { - scrollPadding: '23px 0 0 0', - }, + buttonSuccess: { + backgroundColor: theme.success, + borderWidth: 0, + }, - textInputMultilineContainer: { - paddingTop: 23, - }, - - textInputAndIconContainer: { - flex: 1, - height: '100%', - zIndex: -1, - flexDirection: 'row', - }, - - textInputDesktop: addOutlineWidth({}, 0), - - textInputIconContainer: { - paddingHorizontal: 11, - justifyContent: 'center', - margin: 1, - }, - - secureInput: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, - - textInput: { - backgroundColor: 'transparent', - borderRadius: variables.componentBorderRadiusNormal, - height: variables.inputComponentSizeNormal, - borderColor: theme.border, - borderWidth: 1, - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - paddingLeft: 12, - paddingRight: 12, - paddingTop: 10, - paddingBottom: 10, - textAlignVertical: 'center', - }, - - textInputPrefixWrapper: { - position: 'absolute', - left: 0, - top: 0, - height: variables.inputHeight, - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - paddingTop: 23, - paddingBottom: 8, - }, + buttonOpacityDisabled: { + opacity: 0.5, + }, - textInputPrefix: { - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - textAlignVertical: 'center', - }, + buttonSuccessHovered: { + backgroundColor: theme.successHover, + borderWidth: 0, + }, - pickerContainer: { - borderBottomWidth: 2, - paddingLeft: 0, - borderStyle: 'solid', - borderColor: theme.border, - justifyContent: 'center', - backgroundColor: 'transparent', - height: variables.inputHeight, - overflow: 'hidden', - }, - - pickerContainerSmall: { - height: variables.inputHeightSmall, - }, - - pickerLabel: { - position: 'absolute', - left: 0, - top: 6, - zIndex: 1, - }, - - picker: (disabled = false, backgroundColor = theme.appBG): PickerStyle => ({ - iconContainer: { - top: Math.round(variables.inputHeight * 0.5) - 11, - right: 0, - ...pointerEventsNone, + buttonDanger: { + backgroundColor: theme.danger, + borderWidth: 0, }, - inputWeb: { - appearance: 'none', - ...(disabled ? cursor.cursorDisabled : cursor.cursorPointer), - ...picker(theme), - backgroundColor, + buttonDangerHovered: { + backgroundColor: theme.dangerHover, + borderWidth: 0, }, - inputIOS: { - ...picker(theme), + buttonDisabled: { + backgroundColor: theme.buttonDefaultBG, + borderWidth: 0, }, - done: { - color: theme.text, + + buttonDivider: { + height: variables.dropDownButtonDividerHeight, + borderWidth: 0.7, + borderColor: theme.text, }, - doneDepressed: { - fontSize: 17, + + noBorderRadius: { + borderRadius: 0, }, - modalViewMiddle: { - backgroundColor: theme.border, - borderTopWidth: 0, + + noRightBorderRadius: { + borderTopRightRadius: 0, + borderBottomRightRadius: 0, }, - modalViewBottom: { - backgroundColor: theme.highlightBG, + + noLeftBorderRadius: { + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, }, - inputAndroid: { - ...picker(theme), + buttonCTA: { + paddingVertical: 6, + ...spacing.mh4, }, - }), - disabledText: { - color: theme.icon, - }, + buttonCTAIcon: { + marginRight: 22, - inputDisabled: { - backgroundColor: theme.highlightBG, - color: theme.icon, - }, + // Align vertically with the Button text + paddingBottom: 1, + paddingTop: 1, + }, - noOutline: addOutlineWidth({}, 0), + buttonConfirm: { + margin: 20, + }, - errorOutline: { - borderColor: theme.danger, - }, + attachmentButtonBigScreen: { + minWidth: 300, + alignSelf: 'center', + }, - textLabelSupporting: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.textSupporting, - }, + buttonConfirmText: { + paddingLeft: 20, + paddingRight: 20, + }, - textLabelError: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.textError, - }, - - textReceiptUpload: { - ...headlineFont, - fontSize: variables.fontSizeXLarge, - color: theme.textLight, - textAlign: 'center', - }, - - subTextReceiptUpload: { - fontFamily: fontFamily.EXP_NEUE, - lineHeight: variables.lineHeightLarge, - textAlign: 'center', - color: theme.textLight, - }, + buttonSuccessText: { + color: theme.textLight, + }, - furtherDetailsText: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSmall, - color: theme.textSupporting, - }, - - lh16: { - lineHeight: 16, - }, - - lh20: { - lineHeight: 20, - }, - - lh140Percent: { - lineHeight: '140%', - }, - - formHelp: { - color: theme.textSupporting, - fontSize: variables.fontSizeLabel, - lineHeight: variables.lineHeightLarge, - marginBottom: 4, - }, - - formError: { - color: theme.textError, - fontSize: variables.fontSizeLabel, - lineHeight: variables.formErrorLineHeight, - marginBottom: 4, - }, - - formSuccess: { - color: theme.success, - fontSize: variables.fontSizeLabel, - lineHeight: 18, - marginBottom: 4, - }, - - desktopRedirectPage: { - backgroundColor: theme.appBG, - minHeight: '100%', - flex: 1, - alignItems: 'center', - }, - - signInPage: { - backgroundColor: theme.highlightBG, - minHeight: '100%', - flex: 1, - }, - - signInPageHeroCenter: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'center', - alignItems: 'center', - }, - - signInPageGradient: { - height: '100%', - width: 540, - position: 'absolute', - top: 0, - left: 0, - }, - - signInPageGradientMobile: { - height: 300, - width: 800, - position: 'absolute', - top: 0, - left: 0, - }, - - signInBackground: { - position: 'absolute', - bottom: 0, - left: 0, - minHeight: 700, - }, - - signInPageInner: { - marginLeft: 'auto', - marginRight: 'auto', - height: '100%', - width: '100%', - }, - - signInPageContentTopSpacer: { - maxHeight: 132, - minHeight: 24, - }, - - signInPageContentTopSpacerSmallScreens: { - maxHeight: 132, - minHeight: 45, - }, - - signInPageLeftContainer: { - paddingLeft: 40, - paddingRight: 40, - }, - - signInPageLeftContainerWide: { - maxWidth: variables.sideBarWidth, - }, - - signInPageWelcomeFormContainer: { - maxWidth: CONST.SIGN_IN_FORM_WIDTH, - }, - - signInPageWelcomeTextContainer: { - width: CONST.SIGN_IN_FORM_WIDTH, - }, - - changeExpensifyLoginLinkContainer: { - flexDirection: 'row', - flexWrap: 'wrap', - ...wordBreak.breakWord, - }, - - // Sidebar Styles - sidebar: { - backgroundColor: theme.sidebar, - height: '100%', - }, - - sidebarAnimatedWrapperContainer: { - height: '100%', - position: 'absolute', - }, - - sidebarFooter: { - alignItems: 'center', - display: 'flex', - justifyContent: 'center', - paddingVertical: variables.lineHeightXLarge, - width: '100%', - }, - - sidebarAvatar: { - backgroundColor: theme.icon, - borderRadius: 20, - height: variables.componentSizeNormal, - width: variables.componentSizeNormal, - }, - - statusIndicator: (backgroundColor = theme.danger): ViewStyle => ({ - borderColor: theme.sidebar, - backgroundColor, - borderRadius: 8, - borderWidth: 2, - position: 'absolute', - right: -2, - top: -1, - height: 16, - width: 16, - zIndex: 10, - }), - - floatingActionButtonContainer: { - position: 'absolute', - right: 20, - - // The bottom of the floating action button should align with the bottom of the compose box. - // The value should be equal to the height + marginBottom + marginTop of chatItemComposeSecondaryRow - bottom: 25, - }, - - floatingActionButton: { - backgroundColor: theme.success, - height: variables.componentSizeLarge, - width: variables.componentSizeLarge, - borderRadius: 999, - alignItems: 'center', - justifyContent: 'center', - }, - - sidebarFooterUsername: { - color: theme.heading, - fontSize: variables.fontSizeLabel, - fontWeight: '700', - width: 200, - textOverflow: 'ellipsis', - overflow: 'hidden', - ...whiteSpace.noWrap, - }, - - sidebarFooterLink: { - color: theme.textSupporting, - fontSize: variables.fontSizeSmall, - textDecorationLine: 'none', - fontFamily: fontFamily.EXP_NEUE, - lineHeight: 20, - }, - - sidebarListContainer: { - scrollbarWidth: 'none', - paddingBottom: 4, - }, - - sidebarListItem: { - justifyContent: 'center', - textDecorationLine: 'none', - }, - - RHPNavigatorContainer: (isSmallScreenWidth: boolean): ViewStyle => ({ - width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, - position: 'absolute', - right: 0, - height: '100%', - }), - - onlyEmojisText: { - fontSize: variables.fontSizeOnlyEmojis, - lineHeight: variables.fontSizeOnlyEmojisHeight, - }, - - onlyEmojisTextLineHeight: { - lineHeight: variables.fontSizeOnlyEmojisHeight, - }, - - createMenuPositionSidebar: (windowHeight: number): AnchorPosition => ({ - horizontal: 18, - vertical: windowHeight - 100, - }), - - createMenuPositionProfile: (windowWidth: number): AnchorPosition => ({ - horizontal: windowWidth - 355, - ...getPopOverVerticalOffset(162), - }), - - createMenuPositionReportActionCompose: (windowHeight: number): AnchorPosition => ({ - horizontal: 18 + variables.sideBarWidth, - vertical: windowHeight - 83, - }), - - createMenuPositionRightSidepane: { - right: 18, - bottom: 75, - }, - - createMenuContainer: { - width: variables.sideBarWidth - 40, - paddingVertical: 12, - }, - - createMenuHeaderText: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.heading, - }, - - popoverMenuItem: { - flexDirection: 'row', - borderRadius: 0, - paddingHorizontal: 20, - paddingVertical: 12, - justifyContent: 'space-between', - width: '100%', - }, - - popoverMenuIcon: { - width: variables.componentSizeNormal, - justifyContent: 'center', - alignItems: 'center', - }, - - popoverMenuText: { - fontSize: variables.fontSizeNormal, - color: theme.heading, - }, - - popoverInnerContainer: { - paddingTop: 0, // adjusting this because the mobile modal adds additional padding that we don't need for our layout - maxHeight: '95%', - }, - - menuItemTextContainer: { - minHeight: variables.componentSizeNormal, - }, - - chatLinkRowPressable: { - minWidth: 0, - textDecorationLine: 'none', - flex: 1, - }, - - sidebarLink: { - textDecorationLine: 'none', - }, - - sidebarLinkInner: { - alignItems: 'center', - flexDirection: 'row', - paddingLeft: 20, - paddingRight: 20, - }, - - sidebarLinkText: { - color: theme.textSupporting, - fontSize: variables.fontSizeNormal, - textDecorationLine: 'none', - overflow: 'hidden', - }, - - sidebarLinkHover: { - backgroundColor: theme.sidebarHover, - }, - - sidebarLinkActive: { - backgroundColor: theme.border, - textDecorationLine: 'none', - }, - - sidebarLinkTextBold: { - fontWeight: '700', - color: theme.heading, - }, - - sidebarLinkActiveText: { - color: theme.textSupporting, - fontSize: variables.fontSizeNormal, - textDecorationLine: 'none', - overflow: 'hidden', - }, + buttonDangerText: { + color: theme.textLight, + }, - optionItemAvatarNameWrapper: { - minWidth: 0, - flex: 1, - }, + hoveredComponentBG: { + backgroundColor: theme.hoverComponentBG, + }, - optionDisplayName: { - fontFamily: fontFamily.EXP_NEUE, - minHeight: variables.alternateTextHeight, - lineHeight: variables.lineHeightXLarge, - ...whiteSpace.noWrap, - }, - - optionDisplayNameCompact: { - minWidth: 'auto', - flexBasis: 'auto', - flexGrow: 0, - flexShrink: 1, - }, - - displayNameTooltipEllipsis: { - position: 'absolute', - opacity: 0, - right: 0, - bottom: 0, - }, - - optionAlternateText: { - minHeight: variables.alternateTextHeight, - lineHeight: variables.lineHeightXLarge, - }, - - optionAlternateTextCompact: { - flexShrink: 1, - flexGrow: 1, - flexBasis: 'auto', - ...optionAlternateTextPlatformStyles, - }, - - optionRow: { - minHeight: variables.optionRowHeight, - paddingTop: 12, - paddingBottom: 12, - }, - - optionRowSelected: { - backgroundColor: theme.activeComponentBG, - }, - - optionRowDisabled: { - color: theme.textSupporting, - }, - - optionRowCompact: { - height: variables.optionRowHeightCompact, - paddingTop: 12, - paddingBottom: 12, - }, - - optionsListSectionHeader: { - height: variables.optionsListSectionHeaderHeight, - }, - - overlayStyles: (current: OverlayStylesParams): ViewStyle => ({ - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], - - // We need to stretch the overlay to cover the sidebar and the translate animation distance. - left: -2 * variables.sideBarWidth, - top: 0, - bottom: 0, - right: 0, - backgroundColor: theme.shadow, - opacity: current.progress.interpolate({ - inputRange: [0, 1], - outputRange: [0, variables.overlayOpacity], - extrapolate: 'clamp', - }), - }), - - appContent: { - backgroundColor: theme.appBG, - overflow: 'hidden', - }, - - appContentHeader: { - height: variables.contentHeaderHeight, - justifyContent: 'center', - display: 'flex', - paddingRight: 20, - }, - - appContentHeaderTitle: { - alignItems: 'center', - flexDirection: 'row', - }, - - LHNToggle: { - alignItems: 'center', - height: variables.contentHeaderHeight, - justifyContent: 'center', - paddingRight: 10, - paddingLeft: 20, - }, - - LHNToggleIcon: { - height: 15, - width: 18, - }, - - chatContent: { - flex: 4, - justifyContent: 'flex-end', - }, - - chatContentScrollView: { - flexGrow: 1, - justifyContent: 'flex-start', - paddingBottom: 16, - }, - - // Chat Item - chatItem: { - display: 'flex', - flexDirection: 'row', - paddingTop: 8, - paddingBottom: 8, - paddingLeft: 20, - paddingRight: 20, - }, - - chatItemRightGrouped: { - flexGrow: 1, - flexShrink: 1, - flexBasis: 0, - position: 'relative', - marginLeft: variables.chatInputSpacing, - }, - - chatItemRight: { - flexGrow: 1, - flexShrink: 1, - flexBasis: 0, - position: 'relative', - }, - - chatItemMessageHeader: { - alignItems: 'center', - display: 'flex', - flexDirection: 'row', - flexWrap: 'nowrap', - }, - - chatItemMessageHeaderSender: { - color: theme.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - lineHeight: variables.lineHeightXLarge, - ...wordBreak.breakWord, - }, - - chatItemMessageHeaderTimestamp: { - flexShrink: 0, - color: theme.textSupporting, - fontSize: variables.fontSizeSmall, - paddingTop: 2, - }, - - chatItemMessage: { - color: theme.text, - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - lineHeight: variables.lineHeightXLarge, - maxWidth: '100%', - ...cursor.cursorAuto, - ...whiteSpace.preWrap, - ...wordBreak.breakWord, - }, - - chatItemComposeWithFirstRow: { - minHeight: 90, - }, - - chatItemFullComposeRow: { - ...sizing.h100, - }, - - chatItemComposeBoxColor: { - borderColor: theme.border, - }, + activeComponentBG: { + backgroundColor: theme.activeComponentBG, + }, - chatItemComposeBoxFocusedColor: { - borderColor: theme.borderFocus, - }, + fontWeightBold: { + fontWeight: fontWeightBold, + }, - chatItemComposeBox: { - backgroundColor: theme.componentBG, - borderWidth: 1, - borderRadius: variables.componentBorderRadiusRounded, - minHeight: variables.componentSizeMedium, - }, - - chatItemFullComposeBox: { - ...flex.flex1, - ...sizing.h100, - }, - - chatFooter: { - paddingLeft: 20, - paddingRight: 20, - display: 'flex', - backgroundColor: theme.appBG, - }, - - chatFooterFullCompose: { - flex: 1, - }, - - chatItemDraft: { - display: 'flex', - flexDirection: 'row', - paddingTop: 8, - paddingBottom: 8, - paddingLeft: 20, - paddingRight: 20, - }, - - chatItemReactionsDraftRight: { - marginLeft: 52, - }, - chatFooterAtTheTop: { - flexGrow: 1, - justifyContent: 'flex-start', - }, - - // Be extremely careful when editing the compose styles, as it is easy to introduce regressions. - // Make sure you run the following tests against any changes: #12669 - textInputCompose: addOutlineWidth( - { - backgroundColor: theme.componentBG, - borderColor: theme.border, - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeNormal, - borderWidth: 0, - height: 'auto', - lineHeight: variables.lineHeightXLarge, - ...overflowXHidden, + touchableButtonImage: { + alignItems: 'center', + height: variables.componentSizeNormal, + justifyContent: 'center', + width: variables.componentSizeNormal, + }, - // On Android, multiline TextInput with height: 'auto' will show extra padding unless they are configured with - // paddingVertical: 0, alignSelf: 'center', and textAlignVertical: 'center' + visuallyHidden: { + ...visibility.hidden, + overflow: 'hidden', + width: 0, + height: 0, + }, - paddingHorizontal: variables.avatarChatSpacing, - paddingTop: 0, - paddingBottom: 0, - alignSelf: 'center', - textAlignVertical: 'center', + visibilityHidden: { + ...visibility.hidden, }, - 0, - ), - textInputFullCompose: { - alignSelf: 'stretch', - flex: 1, - maxHeight: '100%', - textAlignVertical: 'top', - }, + loadingVBAAnimation: { + width: 140, + height: 140, + }, - editInputComposeSpacing: { - backgroundColor: theme.transparent, - marginVertical: 8, - }, - - // composer padding should not be modified unless thoroughly tested against the cases in this PR: #12669 - textInputComposeSpacing: { - paddingVertical: 5, - ...flex.flexRow, - flex: 1, - }, - - textInputComposeBorder: { - borderLeftWidth: 1, - borderColor: theme.border, - }, + pickerSmall: (backgroundColor = theme.highlightBG) => + ({ + inputIOS: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + paddingLeft: 0, + paddingRight: 17, + paddingTop: 6, + paddingBottom: 6, + borderWidth: 0, + color: theme.text, + height: 26, + opacity: 1, + backgroundColor: 'transparent', + }, + done: { + color: theme.text, + }, + doneDepressed: { + // Extracted from react-native-picker-select, src/styles.js + fontSize: 17, + }, + modalViewMiddle: { + backgroundColor: theme.border, + borderTopWidth: 0, + }, + modalViewBottom: { + backgroundColor: theme.highlightBG, + }, + inputWeb: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + paddingLeft: 0, + paddingRight: 17, + paddingTop: 6, + paddingBottom: 6, + borderWidth: 0, + color: theme.text, + appearance: 'none', + height: 26, + opacity: 1, + backgroundColor, + ...cursor.cursorPointer, + }, + inputAndroid: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + paddingLeft: 0, + paddingRight: 17, + paddingTop: 6, + paddingBottom: 6, + borderWidth: 0, + color: theme.text, + height: 26, + opacity: 1, + backgroundColor: 'transparent', + }, + iconContainer: { + top: 7, + ...pointerEventsNone, + }, + icon: { + width: variables.iconSizeExtraSmall, + height: variables.iconSizeExtraSmall, + }, + } satisfies CustomPickerStyle), + + badge: { + backgroundColor: theme.border, + borderRadius: 14, + height: variables.iconSizeNormal, + flexDirection: 'row', + paddingHorizontal: 7, + alignItems: 'center', + }, - chatItemSubmitButton: { - alignSelf: 'flex-end', - borderRadius: variables.componentBorderRadiusRounded, - backgroundColor: theme.transparent, - height: 40, - padding: 10, - margin: 3, - justifyContent: 'center', - }, - - emojiPickerContainer: { - backgroundColor: theme.componentBG, - }, - - emojiHeaderContainer: { - backgroundColor: theme.componentBG, - display: 'flex', - height: CONST.EMOJI_PICKER_HEADER_HEIGHT, - justifyContent: 'center', - width: '100%', - }, - - emojiSkinToneTitle: { - width: '100%', - ...spacing.pv1, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - color: theme.heading, - fontSize: variables.fontSizeSmall, - }, - - // Emoji Picker Styles - emojiText: { - textAlign: 'center', - fontSize: variables.emojiSize, - ...spacing.pv0, - ...spacing.ph0, - lineHeight: variables.emojiLineHeight, - }, - - emojiItem: { - width: '12.5%', - textAlign: 'center', - borderRadius: 8, - paddingTop: 2, - paddingBottom: 2, - height: CONST.EMOJI_PICKER_ITEM_HEIGHT, - }, - - emojiItemHighlighted: { - transition: '0.2s ease', - backgroundColor: theme.buttonDefaultBG, - }, - - emojiItemKeyboardHighlighted: { - transition: '0.2s ease', - borderWidth: 1, - borderColor: theme.link, - borderRadius: variables.buttonBorderRadius, - }, - - categoryShortcutButton: { - flex: 1, - borderRadius: 8, - height: CONST.EMOJI_PICKER_ITEM_HEIGHT, - alignItems: 'center', - justifyContent: 'center', - }, - - chatItemEmojiButton: { - alignSelf: 'flex-end', - borderRadius: variables.buttonBorderRadius, - height: 40, - marginVertical: 3, - paddingHorizontal: 10, - justifyContent: 'center', - }, - - editChatItemEmojiWrapper: { - marginRight: 3, - alignSelf: 'flex-end', - }, - - hoveredButton: { - backgroundColor: theme.buttonHoveredBG, - }, - - composerSizeButton: { - alignSelf: 'center', - height: 32, - width: 32, - padding: 6, - margin: 3, - borderRadius: variables.componentBorderRadiusRounded, - backgroundColor: theme.transparent, - justifyContent: 'center', - }, + badgeSuccess: { + backgroundColor: theme.success, + }, - chatItemAttachmentPlaceholder: { - backgroundColor: theme.sidebar, - borderColor: theme.border, - borderWidth: 1, - borderRadius: variables.componentBorderRadiusNormal, - height: 150, - textAlign: 'center', - verticalAlign: 'middle', - width: 200, - }, - - chatSwticherPillWrapper: { - marginTop: 5, - marginRight: 4, - }, - - navigationModalOverlay: { - ...userSelect.userSelectNone, - position: 'absolute', - width: '100%', - height: '100%', - transform: [ - { - translateX: -variables.sideBarWidth, - }, - ], - }, + badgeSuccessPressed: { + backgroundColor: theme.successHover, + }, - sidebarVisible: { - borderRightWidth: 1, - }, + badgeAdHocSuccess: { + backgroundColor: theme.badgeAdHoc, + }, - sidebarHidden: { - width: 0, - borderRightWidth: 0, - }, + badgeAdHocSuccessPressed: { + backgroundColor: theme.badgeAdHocHover, + }, - exampleCheckImage: { - width: '100%', - height: 80, - borderColor: theme.border, - borderWidth: 1, - borderRadius: variables.componentBorderRadiusNormal, - }, - - singleAvatar: { - height: 24, - width: 24, - backgroundColor: theme.icon, - borderRadius: 24, - }, - - singleSubscript: { - height: variables.iconSizeNormal, - width: variables.iconSizeNormal, - backgroundColor: theme.icon, - borderRadius: 20, - zIndex: 1, - }, - - singleAvatarSmall: { - height: 18, - width: 18, - backgroundColor: theme.icon, - borderRadius: 18, - }, - - secondAvatar: { - position: 'absolute', - right: -18, - bottom: -18, - borderWidth: 3, - borderRadius: 30, - borderColor: 'transparent', - }, - - secondAvatarSmall: { - position: 'absolute', - right: -13, - bottom: -13, - borderWidth: 3, - borderRadius: 18, - borderColor: 'transparent', - }, - - secondAvatarSubscript: { - position: 'absolute', - right: -6, - bottom: -6, - }, - - secondAvatarSubscriptCompact: { - position: 'absolute', - bottom: -1, - right: -1, - }, - - secondAvatarSubscriptSmallNormal: { - position: 'absolute', - bottom: 0, - right: 0, - }, - - leftSideLargeAvatar: { - left: 15, - }, - - rightSideLargeAvatar: { - right: 15, - zIndex: 2, - borderWidth: 4, - borderRadius: 100, - }, - - secondAvatarInline: { - bottom: -3, - right: -25, - borderWidth: 3, - borderRadius: 18, - borderColor: theme.cardBorder, - backgroundColor: theme.appBG, - }, - - avatarLarge: { - width: variables.avatarSizeLarge, - height: variables.avatarSizeLarge, - }, - - avatarNormal: { - height: variables.componentSizeNormal, - width: variables.componentSizeNormal, - borderRadius: variables.componentSizeNormal, - }, - - avatarSmall: { - height: variables.avatarSizeSmall, - width: variables.avatarSizeSmall, - borderRadius: variables.avatarSizeSmall, - }, - - avatarInnerText: { - color: theme.textLight, - fontSize: variables.fontSizeSmall, - lineHeight: undefined, - marginLeft: -3, - textAlign: 'center', - }, - - avatarInnerTextSmall: { - color: theme.textLight, - fontSize: variables.fontSizeExtraSmall, - lineHeight: undefined, - marginLeft: -2, - textAlign: 'center', - }, - - avatarSpace: { - top: 3, - left: 3, - }, - - avatar: { - backgroundColor: theme.sidebar, - borderColor: theme.sidebar, - }, - - focusedAvatar: { - backgroundColor: theme.border, - borderColor: theme.border, - }, - - emptyAvatar: { - height: variables.avatarSizeNormal, - width: variables.avatarSizeNormal, - }, - - emptyAvatarSmallNormal: { - height: variables.avatarSizeSmallNormal, - width: variables.avatarSizeSmallNormal, - }, - - emptyAvatarSmall: { - height: variables.avatarSizeSmall, - width: variables.avatarSizeSmall, - }, - - emptyAvatarSmaller: { - height: variables.avatarSizeSmaller, - width: variables.avatarSizeSmaller, - }, - - emptyAvatarMedium: { - height: variables.avatarSizeMedium, - width: variables.avatarSizeMedium, - }, - - emptyAvatarLarge: { - height: variables.avatarSizeLarge, - width: variables.avatarSizeLarge, - }, - - emptyAvatarMargin: { - marginRight: variables.avatarChatSpacing, - }, - - emptyAvatarMarginChat: { - marginRight: variables.avatarChatSpacing - 12, - }, - - emptyAvatarMarginSmall: { - marginRight: variables.avatarChatSpacing - 4, - }, - - emptyAvatarMarginSmaller: { - marginRight: variables.avatarChatSpacing - 4, - }, - - modalViewContainer: { - alignItems: 'center', - flex: 1, - }, - - borderTop: { - borderTopWidth: variables.borderTopWidth, - borderColor: theme.border, - }, + badgeDanger: { + backgroundColor: theme.danger, + }, - borderTopRounded: { - borderTopWidth: 1, - borderColor: theme.border, - borderTopLeftRadius: variables.componentBorderRadiusNormal, - borderTopRightRadius: variables.componentBorderRadiusNormal, - }, + badgeDangerPressed: { + backgroundColor: theme.dangerPressed, + }, - borderBottomRounded: { - borderBottomWidth: 1, - borderColor: theme.border, - borderBottomLeftRadius: variables.componentBorderRadiusNormal, - borderBottomRightRadius: variables.componentBorderRadiusNormal, - }, + badgeText: { + color: theme.text, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightNormal, + ...whiteSpace.noWrap, + }, - borderBottom: { - borderBottomWidth: 1, - borderColor: theme.border, - }, + border: { + borderWidth: 1, + borderRadius: variables.componentBorderRadius, + borderColor: theme.border, + }, - borderNone: { - borderWidth: 0, - borderBottomWidth: 0, - }, + borderColorFocus: { + borderColor: theme.borderFocus, + }, - borderRight: { - borderRightWidth: 1, - borderColor: theme.border, - }, + borderColorDanger: { + borderColor: theme.danger, + }, - borderLeft: { - borderLeftWidth: 1, - borderColor: theme.border, - }, - - pointerEventsNone, - - pointerEventsAuto, - - headerBar: { - overflow: 'hidden', - justifyContent: 'center', - display: 'flex', - paddingLeft: 20, - height: variables.contentHeaderHeight, - width: '100%', - }, - - imageViewContainer: { - width: '100%', - height: '100%', - alignItems: 'center', - justifyContent: 'center', - }, - - imageModalPDF: { - flex: 1, - backgroundColor: theme.modalBackground, - }, - - PDFView: { - // `display: grid` is not supported in native platforms! - // It's being used on Web/Desktop only to vertically center short PDFs, - // while preventing the overflow of the top of long PDF files. - display: 'grid', - backgroundColor: theme.modalBackground, - width: '100%', - height: '100%', - justifyContent: 'center', - overflow: 'hidden', - alignItems: 'center', - }, - - PDFViewList: { - overflowX: 'hidden', - // There properties disable "focus" effect on list - boxShadow: 'none', - outline: 'none', - }, - - getPDFPasswordFormStyle: (isSmallScreenWidth: boolean): ViewStyle => ({ - width: isSmallScreenWidth ? '100%' : 350, - ...(isSmallScreenWidth && flex.flex1), - }), - - modalCenterContentContainer: { - flex: 1, - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.modalBackdrop, - }, - - centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean): ViewStyle => ({ - borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, - marginHorizontal: isSmallScreenWidth ? 0 : 20, - }), - - imageModalImageCenterContainer: { - alignItems: 'center', - flex: 1, - justifyContent: 'center', - width: '100%', - }, - - defaultAttachmentView: { - backgroundColor: theme.sidebar, - borderRadius: variables.componentBorderRadiusNormal, - borderWidth: 1, - borderColor: theme.border, - flexDirection: 'row', - padding: 20, - alignItems: 'center', - }, - - notFoundSafeArea: { - flex: 1, - backgroundColor: theme.heading, - }, - - notFoundView: { - flex: 1, - alignItems: 'center', - paddingTop: 40, - paddingBottom: 40, - justifyContent: 'space-between', - }, - - notFoundLogo: { - width: 202, - height: 63, - }, - - notFoundContent: { - alignItems: 'center', - }, - - notFoundTextHeader: { - ...headlineFont, - color: theme.heading, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - marginTop: 20, - marginBottom: 8, - textAlign: 'center', - }, - - notFoundTextBody: { - color: theme.componentBG, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - fontSize: 15, - }, - - notFoundButtonText: { - color: theme.link, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - fontSize: 15, - }, - - blockingViewContainer: { - paddingBottom: variables.contentHeaderHeight, - }, - - defaultModalContainer: { - backgroundColor: theme.componentBG, - borderColor: theme.transparent, - }, - - reportActionContextMenuMiniButton: { - ...spacing.p1, - ...spacing.mv1, - ...spacing.mh1, - ...{borderRadius: variables.buttonBorderRadius}, - }, - - reportActionSystemMessageContainer: { - marginLeft: 42, - }, - - reportDetailsTitleContainer: { - ...flex.flexColumn, - ...flex.alignItemsCenter, - paddingHorizontal: 20, - paddingBottom: 20, - }, - - reportDetailsRoomInfo: { - ...flex.flex1, - ...flex.flexColumn, - ...flex.alignItemsCenter, - }, - - reportSettingsVisibilityText: { - textTransform: 'capitalize', - }, - - settingsPageBackground: { - flexDirection: 'column', - width: '100%', - flexGrow: 1, - }, - - settingsPageBody: { - width: '100%', - justifyContent: 'space-around', - }, - - settingsPageColumn: { - width: '100%', - alignItems: 'center', - justifyContent: 'space-around', - }, - - settingsPageContainer: { - justifyContent: 'space-between', - alignItems: 'center', - width: '100%', - }, - - twoFactorAuthSection: { - backgroundColor: theme.appBG, - padding: 0, - }, - - twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: TwoFactorAuthCodesBoxParams): ViewStyle => { - let paddingHorizontal = spacing.ph9; - - if (isSmallScreenWidth) { - paddingHorizontal = spacing.ph4; - } - - if (isExtraSmallScreenWidth) { - paddingHorizontal = spacing.ph2; - } - - return { - alignItems: 'center', - justifyContent: 'center', + textInputDisabled: { + // Adding disabled color theme to indicate user that the field is not editable. backgroundColor: theme.highlightBG, - paddingVertical: 28, - borderRadius: 16, - marginTop: 32, - ...paddingHorizontal, - }; - }, - - twoFactorLoadingContainer: { - alignItems: 'center', - justifyContent: 'center', - height: 210, - }, - - twoFactorAuthCodesContainer: { - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - flexWrap: 'wrap', - gap: 12, - }, - - twoFactorAuthCode: { - fontFamily: fontFamily.MONOSPACE, - width: 112, - textAlign: 'center', - }, - - twoFactorAuthCodesButtonsContainer: { - flexDirection: 'row', - justifyContent: 'center', - gap: 12, - marginTop: 20, - flexWrap: 'wrap', - }, - - twoFactorAuthCodesButton: { - minWidth: 112, - }, - - twoFactorAuthCopyCodeButton: { - minWidth: 110, - }, - - anonymousRoomFooter: (isSmallSizeLayout: boolean): ViewStyle & TextStyle => ({ - flexDirection: isSmallSizeLayout ? 'column' : 'row', - ...(!isSmallSizeLayout && { + borderBottomWidth: 2, + borderColor: theme.borderLighter, + // Adding browser specefic style to bring consistency between Safari and other platforms. + // Applying the Webkit styles only to browsers as it is not available in native. + ...(Browser.getBrowser() + ? { + WebkitTextFillColor: theme.textSupporting, + WebkitOpacity: 1, + } + : {}), + color: theme.textSupporting, + }, + + uploadReceiptView: (isSmallScreenWidth: boolean) => + ({ + borderRadius: variables.componentBorderRadiusLarge, + borderWidth: isSmallScreenWidth ? 0 : 2, + borderColor: theme.borderFocus, + borderStyle: 'dotted', + marginBottom: 20, + marginLeft: 20, + marginRight: 20, + justifyContent: 'center', + alignItems: 'center', + padding: 40, + gap: 4, + flex: 1, + } satisfies ViewStyle), + + cameraView: { + flex: 1, + overflow: 'hidden', + padding: 10, + borderRadius: 28, + borderStyle: 'solid', + borderWidth: 8, + backgroundColor: theme.highlightBG, + borderColor: theme.appBG, + }, + + permissionView: { + paddingVertical: 108, + paddingHorizontal: 61, alignItems: 'center', - justifyContent: 'space-between', - }), - padding: 20, - backgroundColor: theme.sidebar, - borderRadius: variables.componentBorderRadiusLarge, - overflow: 'hidden', - }), - anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean): ViewStyle => ({ - flexDirection: 'row', - alignItems: 'center', - ...(isSmallSizeLayout && { - justifyContent: 'space-between', - marginTop: 16, - }), - }), - anonymousRoomFooterLogo: { - width: 88, - marginLeft: 0, - height: 20, - }, - anonymousRoomFooterLogoTaglineText: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeMedium, - color: theme.textLight, - }, - signInButtonAvatar: { - width: 80, - }, - - anonymousRoomFooterSignInButton: { - width: 110, - }, - - roomHeaderAvatarSize: { - height: variables.componentSizeLarge, - width: variables.componentSizeLarge, - }, - - roomHeaderAvatar: { - backgroundColor: theme.appBG, - borderRadius: 100, - borderColor: theme.componentBG, - borderWidth: 4, - }, - - roomHeaderAvatarOverlay: { - position: 'absolute', - top: 0, - right: 0, - bottom: 0, - left: 0, - backgroundColor: theme.overlay, - opacity: variables.overlayOpacity, - borderRadius: 88, - }, - - rootNavigatorContainerStyles: (isSmallScreenWidth: boolean): ViewStyle => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean): ViewStyle => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1}), - - avatarInnerTextChat: { - color: theme.textLight, - fontSize: variables.fontSizeXLarge, - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - textAlign: 'center', - fontWeight: 'normal', - position: 'absolute', - width: 88, - left: -16, - }, - - svgAvatarBorder: { - borderRadius: 100, - overflow: 'hidden', - }, - - displayName: { - fontSize: variables.fontSizeLarge, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - color: theme.heading, - }, - - pageWrapper: { - width: '100%', - alignItems: 'center', - padding: 20, - }, - - avatarSectionWrapper: { - width: '100%', - alignItems: 'center', - paddingHorizontal: 20, - paddingBottom: 20, - }, - - avatarSectionWrapperSkeleton: { - width: '100%', - paddingHorizontal: 20, - paddingBottom: 20, - }, - - selectCircle: { - width: variables.componentSizeSmall, - height: variables.componentSizeSmall, - borderColor: theme.border, - borderWidth: 1, - borderRadius: variables.componentSizeSmall / 2, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.componentBG, - marginLeft: 8, - }, - - unreadIndicatorContainer: { - position: 'absolute', - top: -10, - left: 0, - width: '100%', - height: 20, - paddingHorizontal: 20, - flexDirection: 'row', - alignItems: 'center', - zIndex: 1, - ...cursor.cursorDefault, - }, - - unreadIndicatorLine: { - height: 1, - backgroundColor: theme.unreadIndicator, - flexGrow: 1, - marginRight: 8, - opacity: 0.5, - }, - - threadDividerLine: { - height: 1, - backgroundColor: theme.border, - flexGrow: 1, - marginHorizontal: 20, - }, - - unreadIndicatorText: { - color: theme.unreadIndicator, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeSmall, - fontWeight: fontWeightBold, - textTransform: 'capitalize', - }, - - flipUpsideDown: { - transform: [{rotate: '180deg'}], - }, - - navigationSceneContainer: { - backgroundColor: theme.appBG, - }, - - navigationScreenCardStyle: { - backgroundColor: theme.appBG, - height: '100%', - }, - - navigationSceneFullScreenWrapper: { - borderRadius: variables.componentBorderRadiusCard, - overflow: 'hidden', - height: '100%', - }, - - invisible: { - position: 'absolute', - opacity: 0, - }, - - containerWithSpaceBetween: { - justifyContent: 'space-between', - width: '100%', - flex: 1, - }, - - detailsPageSectionContainer: { - alignSelf: 'flex-start', - }, - - attachmentCarouselContainer: { - height: '100%', - width: '100%', - display: 'flex', - justifyContent: 'center', - ...cursor.cursorUnset, - }, - - attachmentArrow: { - zIndex: 23, - position: 'absolute', - }, - - attachmentRevealButtonContainer: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - ...spacing.ph4, - }, - - arrowIcon: { - height: 40, - width: 40, - alignItems: 'center', - paddingHorizontal: 0, - paddingTop: 0, - paddingBottom: 0, - }, - - detailsPageSectionVersion: { - alignSelf: 'center', - color: theme.textSupporting, - fontSize: variables.fontSizeSmall, - height: 24, - lineHeight: 20, - }, - - switchTrack: { - width: 50, - height: 28, - justifyContent: 'center', - borderRadius: 20, - padding: 15, - backgroundColor: theme.success, - }, - - switchInactive: { - backgroundColor: theme.border, - }, - - switchThumb: { - width: 22, - height: 22, - borderRadius: 11, - position: 'absolute', - left: 4, - backgroundColor: theme.appBG, - }, - - switchThumbTransformation: (translateX: AnimatableNumericValue): ViewStyle => ({ - transform: [{translateX}], - }), - - radioButtonContainer: { - backgroundColor: theme.componentBG, - borderRadius: 10, - height: 20, - width: 20, - borderColor: theme.icon, - borderWidth: 1, - justifyContent: 'center', - alignItems: 'center', - }, - - checkboxPressable: { - borderRadius: 6, - padding: 2, - justifyContent: 'center', - alignItems: 'center', - }, - - checkedContainer: { - backgroundColor: theme.checkBox, - }, - - magicCodeInputContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - minHeight: variables.inputHeight, - }, - - magicCodeInput: { - fontSize: variables.fontSizeXLarge, - color: theme.heading, - lineHeight: variables.inputHeight, - }, - - // Manually style transparent, in iOS Safari, an input in a container with its opacity set to - // 0 (completely transparent) cannot handle user interaction, hence the Paste option is never shown - inputTransparent: { - color: 'transparent', - // These properties are available in browser only - ...(Browser.getBrowser() - ? { - caretColor: 'transparent', - WebkitTextFillColor: 'transparent', - // After setting the input text color to transparent, it acquires the background-color. - // However, it is not possible to override the background-color directly as explained in this resource: https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill - // Therefore, the transition effect needs to be delayed. - transitionDelay: '99999s', - } - : {}), - }, - - iouAmountText: { - ...headlineFont, - fontSize: variables.iouAmountTextSize, - color: theme.heading, - lineHeight: variables.inputHeight, - }, - - iouAmountTextInput: addOutlineWidth( - { - ...headlineFont, - fontSize: variables.iouAmountTextSize, + justifyContent: 'center', + }, + + headerAnonymousFooter: { color: theme.heading, - padding: 0, - lineHeight: undefined, + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, }, - 0, - ), - - moneyRequestConfirmationAmount: { - ...headlineFont, - fontSize: variables.fontSizeh1, - }, - - moneyRequestMenuItem: { - flexDirection: 'row', - borderRadius: 0, - justifyContent: 'space-between', - width: '100%', - paddingHorizontal: 20, - paddingVertical: 12, - }, - - requestPreviewBox: { - marginTop: 12, - maxWidth: variables.sideBarWidth, - }, - - moneyRequestPreviewBox: { - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - maxWidth: variables.sideBarWidth, - width: '100%', - }, - - moneyRequestPreviewBoxText: { - padding: 16, - }, - - moneyRequestPreviewBoxLoading: { - // When a new IOU request arrives it is very briefly in a loading state, so set the minimum height of the container to 94 to match the rendered height after loading. - // Otherwise, the IOU request pay button will not be fully visible and the user will have to scroll up to reveal the entire IOU request container. - // See https://github.com/Expensify/App/issues/10283. - minHeight: 94, - width: '100%', - }, - - moneyRequestPreviewBoxAvatar: { - marginRight: -10, - marginBottom: 0, - }, - - moneyRequestPreviewAmount: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - }, - - defaultCheckmarkWrapper: { - marginLeft: 8, - alignSelf: 'center', - }, - - iouDetailsContainer: { - flexGrow: 1, - paddingStart: 20, - paddingEnd: 20, - }, - - codeWordWrapper: { - ...codeStyles.codeWordWrapper, - }, - - codeWordStyle: { - borderLeftWidth: 0, - borderRightWidth: 0, - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - paddingLeft: 0, - paddingRight: 0, - justifyContent: 'center', - ...codeStyles.codeWordStyle, - }, - - codeFirstWordStyle: { - borderLeftWidth: 1, - borderTopLeftRadius: 4, - borderBottomLeftRadius: 4, - paddingLeft: 5, - }, - - codeLastWordStyle: { - borderRightWidth: 1, - borderTopRightRadius: 4, - borderBottomRightRadius: 4, - paddingRight: 5, - }, - - fullScreenLoading: { - backgroundColor: theme.componentBG, - opacity: 0.8, - justifyContent: 'center', - alignItems: 'center', - zIndex: 10, - }, - - navigatorFullScreenLoading: { - backgroundColor: theme.highlightBG, - opacity: 1, - }, - - reimbursementAccountFullScreenLoading: { - backgroundColor: theme.componentBG, - opacity: 0.8, - justifyContent: 'flex-start', - alignItems: 'center', - zIndex: 10, - }, - - hiddenElementOutsideOfWindow: { - position: 'absolute', - top: -10000, - left: 0, - opacity: 0, - }, - - growlNotificationWrapper: { - zIndex: 2, - }, - - growlNotificationContainer: { - flex: 1, - justifyContent: 'flex-start', - position: 'absolute', - width: '100%', - top: 20, - ...spacing.pl5, - ...spacing.pr5, - }, - - growlNotificationDesktopContainer: { - maxWidth: variables.sideBarWidth, - right: 0, - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], - }, - - growlNotificationTranslateY: (translateY: AnimatableNumericValue): ViewStyle => ({ - transform: [{translateY}], - }), - - makeSlideInTranslation: (translationType: Translation, fromValue: number): CustomAnimation => ({ - from: { - [translationType]: fromValue, - }, - to: { - [translationType]: 0, - }, - }), - - growlNotificationBox: { - backgroundColor: theme.inverse, - borderRadius: variables.componentBorderRadiusNormal, - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-between', - shadowColor: theme.shadow, - ...spacing.p5, - }, - - growlNotificationText: { - fontSize: variables.fontSizeNormal, - fontFamily: fontFamily.EXP_NEUE, - width: '90%', - lineHeight: variables.fontSizeNormalHeight, - color: theme.textReversed, - ...spacing.ml4, - }, - - blockquote: { - borderLeftColor: theme.border, - borderLeftWidth: 4, - paddingLeft: 12, - marginVertical: 4, - }, - - noSelect: { - boxShadow: 'none', - outline: 'none', - }, - - cardStyleNavigator: { - overflow: 'hidden', - height: '100%', - }, - - fullscreenCard: { - position: 'absolute', - left: 0, - top: 0, - width: '100%', - height: '100%', - }, - - fullscreenCardWeb: { - left: 'auto', - right: '-24%', - top: '-18%', - height: '120%', - }, - - fullscreenCardWebCentered: { - left: '0', - right: '0', - top: '0', - height: '60%', - }, - - fullscreenCardMobile: { - left: '-20%', - top: '-30%', - width: '150%', - }, - - fullscreenCardMediumScreen: { - left: '-15%', - top: '-30%', - width: '145%', - }, - - smallEditIcon: { - alignItems: 'center', - backgroundColor: theme.buttonHoveredBG, - borderColor: theme.textReversed, - borderRadius: 14, - borderWidth: 3, - color: theme.textReversed, - height: 28, - width: 28, - justifyContent: 'center', - }, - - smallAvatarEditIcon: { - position: 'absolute', - right: -4, - bottom: -4, - }, - - workspaceCard: { - width: '100%', - height: 400, - borderRadius: variables.componentBorderRadiusCard, - overflow: 'hidden', - backgroundColor: theme.heroCard, - }, - - workspaceCardMobile: { - height: 475, - }, - - workspaceCardMediumScreen: { - height: 540, - }, - - workspaceCardMainText: { - fontSize: variables.fontSizeXXXLarge, - fontWeight: 'bold', - lineHeight: variables.fontSizeXXXLarge, - }, - - workspaceCardContent: { - zIndex: 1, - padding: 50, - }, - - workspaceCardContentMediumScreen: { - padding: 25, - }, - - workspaceCardCTA: { - width: 250, - }, - - autoGrowHeightMultilineInput: { - maxHeight: 115, - }, - - peopleRow: { - width: '100%', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - ...spacing.ph5, - }, - - peopleRowBorderBottom: { - borderColor: theme.border, - borderBottomWidth: 1, - ...spacing.pb2, - }, - - peopleBadge: { - backgroundColor: theme.icon, - ...spacing.ph3, - }, - - peopleBadgeText: { - color: theme.textReversed, - fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightNormal, - ...whiteSpace.noWrap, - }, - - offlineFeedback: { - deleted: { - textDecorationLine: 'line-through', - textDecorationStyle: 'solid', - }, - pending: { - opacity: 0.5, + + headerText: { + color: theme.heading, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + }, + + headerGap: { + height: CONST.DESKTOP_HEADER_PADDING, + }, + + pushTextRight: { + left: 100000, + }, + + reportOptions: { + marginLeft: 8, + }, + + chatItemComposeSecondaryRow: { + height: 15, + marginBottom: 5, + marginTop: 5, + }, + + chatItemComposeSecondaryRowSubText: { + color: theme.textSupporting, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + }, + + chatItemComposeSecondaryRowOffset: { + marginLeft: variables.chatInputSpacing, + }, + + offlineIndicator: { + marginLeft: variables.chatInputSpacing, + }, + + offlineIndicatorMobile: { + paddingLeft: 20, + paddingTop: 5, + paddingBottom: 5, + }, + + offlineIndicatorRow: { + height: 25, + }, + + // Actions + actionAvatar: { + borderRadius: 20, + }, + + componentHeightLarge: { + height: variables.inputHeight, }, - error: { + + calendarHeader: { + height: 50, flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: 15, + paddingRight: 5, + ...userSelect.userSelectNone, + }, + + calendarDayRoot: { + flex: 1, + height: 45, + justifyContent: 'center', alignItems: 'center', + ...userSelect.userSelectNone, }, - container: { - ...spacing.pv2, + + calendarDayContainer: { + width: 30, + height: 30, + justifyContent: 'center', + alignItems: 'center', + borderRadius: 15, + overflow: 'hidden', }, - textContainer: { - flexDirection: 'column', + + calendarDayContainerSelected: { + backgroundColor: theme.buttonDefaultBG, + }, + + autoGrowHeightInputContainer: (textInputHeight: number, minHeight: number, maxHeight: number) => + ({ + height: lodashClamp(textInputHeight, minHeight, maxHeight), + minHeight, + } satisfies ViewStyle), + + autoGrowHeightHiddenInput: (maxWidth: number, maxHeight?: number) => + ({ + maxWidth, + maxHeight: maxHeight && maxHeight + 1, + overflow: 'hidden', + } satisfies TextStyle), + + textInputContainer: { flex: 1, + justifyContent: 'center', + height: '100%', + backgroundColor: 'transparent', + borderBottomWidth: 2, + borderColor: theme.border, + overflow: 'hidden', }, - text: { + + textInputLabel: { + position: 'absolute', + left: 0, + top: 0, + fontSize: variables.fontSizeNormal, color: theme.textSupporting, - textAlignVertical: 'center', - fontSize: variables.fontSizeLabel, + fontFamily: fontFamily.EXP_NEUE, + width: '100%', }, - errorDot: { - marginRight: 12, + + textInputLabelBackground: { + position: 'absolute', + top: 0, + width: '100%', + height: 23, + backgroundColor: theme.componentBG, }, - }, - - dotIndicatorMessage: { - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - }, - - sidebarPopover: { - width: variables.sideBarWidth - 68, - }, - - cardOverlay: { - backgroundColor: theme.overlay, - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - opacity: variables.overlayOpacity, - }, - - communicationsLinkIcon: { - right: -36, - top: 0, - bottom: 0, - }, - - shortTermsBorder: { - borderWidth: 1, - borderColor: theme.border, - }, - shortTermsHorizontalRule: { - borderBottomWidth: 1, - borderColor: theme.border, - ...spacing.mh3, - }, + textInputLabelDesktop: { + transformOrigin: 'left center', + }, - shortTermsLargeHorizontalRule: { - borderWidth: 1, - borderColor: theme.border, - ...spacing.mh3, - }, - - shortTermsRow: { - flexDirection: 'row', - padding: 12, - }, - - termsCenterRight: { - marginTop: 'auto', - marginBottom: 'auto', - }, - - shortTermsBoldHeadingSection: { - paddingRight: 12, - paddingLeft: 12, - marginTop: 12, - }, - - shortTermsHeadline: { - ...headlineFont, - ...whiteSpace.preWrap, - color: theme.heading, - fontSize: variables.fontSizeXXXLarge, - lineHeight: variables.lineHeightXXXLarge, - }, - - longTermsRow: { - flexDirection: 'row', - marginTop: 20, - }, - - collapsibleSectionBorder: { - borderBottomWidth: 2, - borderBottomColor: theme.border, - }, - - communicationsLinkHeight: { - height: variables.communicationsLinkHeight, - }, - - floatingMessageCounterWrapper: { - position: 'absolute', - left: '50%', - top: 0, - zIndex: 100, - ...visibility.hidden, - }, - - floatingMessageCounterWrapperAndroid: { - left: 0, - width: '100%', - alignItems: 'center', - position: 'absolute', - top: 0, - zIndex: 100, - ...visibility.hidden, - }, - - floatingMessageCounterSubWrapperAndroid: { - left: '50%', - width: 'auto', - }, - - floatingMessageCounter: { - left: '-50%', - ...visibility.visible, - }, - - floatingMessageCounterTransformation: (translateY: AnimatableNumericValue): ViewStyle => ({ - transform: [{translateY}], - }), - - confirmationAnimation: { - height: 180, - width: 180, - marginBottom: 20, - }, - - googleSearchTextInputContainer: { - flexDirection: 'column', - }, - - googleSearchSeparator: { - height: 1, - backgroundColor: theme.border, - }, - - googleSearchText: { - color: theme.text, - fontSize: variables.fontSizeNormal, - lineHeight: variables.fontSizeNormalHeight, - fontFamily: fontFamily.EXP_NEUE, - flex: 1, - }, - - threeDotsPopoverOffset: (windowWidth: number): AnchorPosition => ({ - ...getPopOverVerticalOffset(60), - horizontal: windowWidth - 60, - }), - - threeDotsPopoverOffsetNoCloseButton: (windowWidth: number): AnchorPosition => ({ - ...getPopOverVerticalOffset(60), - horizontal: windowWidth - 10, - }), - - invert: { - // It's important to invert the Y AND X axis to prevent a react native issue that can lead to ANRs on android 13 - transform: [{scaleX: -1}, {scaleY: -1}], - }, - - keyboardShortcutModalContainer: { - maxHeight: '100%', - flex: 0, - flexBasis: 'auto', - }, - - keyboardShortcutTableWrapper: { - alignItems: 'center', - flex: 1, - height: 'auto', - maxHeight: '100%', - }, - - keyboardShortcutTableContainer: { - display: 'flex', - width: '100%', - borderColor: theme.border, - height: 'auto', - borderRadius: variables.componentBorderRadius, - borderWidth: 1, - }, + textInputLabelTransformation: (translateY: AnimatableNumericValue, translateX: AnimatableNumericValue, scale: AnimatableNumericValue) => + ({ + transform: [{translateY}, {translateX}, {scale}], + } satisfies TextStyle), - keyboardShortcutTableRow: { - flex: 1, - flexDirection: 'row', - borderColor: theme.border, - flexBasis: 'auto', - alignSelf: 'stretch', - borderTopWidth: 1, - }, - - keyboardShortcutTablePrefix: { - width: '30%', - borderRightWidth: 1, - borderColor: theme.border, - }, + baseTextInput: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + lineHeight: variables.lineHeightXLarge, + color: theme.text, + paddingTop: 23, + paddingBottom: 8, + paddingLeft: 0, + borderWidth: 0, + }, - keyboardShortcutTableFirstRow: { - borderTopWidth: 0, - }, + textInputMultiline: { + scrollPadding: '23px 0 0 0', + }, - iPhoneXSafeArea: { - backgroundColor: theme.inverse, - flex: 1, - }, + textInputMultilineContainer: { + paddingTop: 23, + }, - transferBalancePayment: { - borderWidth: 1, - borderRadius: variables.componentBorderRadiusNormal, - borderColor: theme.border, - }, - - transferBalanceSelectedPayment: { - borderColor: theme.iconSuccessFill, - }, - - transferBalanceBalance: { - fontSize: 48, - }, - - closeAccountMessageInput: { - height: 153, - }, - - imageCropContainer: { - overflow: 'hidden', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: theme.imageCropBackgroundColor, - ...cursor.cursorMove, - }, - - sliderKnobTooltipView: { - height: variables.sliderKnobSize, - width: variables.sliderKnobSize, - borderRadius: variables.sliderKnobSize / 2, - }, - - sliderKnob: { - backgroundColor: theme.success, - position: 'absolute', - height: variables.sliderKnobSize, - width: variables.sliderKnobSize, - borderRadius: variables.sliderKnobSize / 2, - left: -(variables.sliderKnobSize / 2), - ...cursor.cursorPointer, - }, - - sliderBar: { - backgroundColor: theme.border, - height: variables.sliderBarHeight, - borderRadius: variables.sliderBarHeight / 2, - alignSelf: 'stretch', - justifyContent: 'center', - }, - - screenCenteredContainer: { - flex: 1, - justifyContent: 'center', - marginBottom: 40, - padding: 16, - }, - - inlineSystemMessage: { - color: theme.textSupporting, - fontSize: variables.fontSizeLabel, - fontFamily: fontFamily.EXP_NEUE, - marginLeft: 6, - }, - - fullScreen: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - - invisibleOverlay: { - backgroundColor: theme.transparent, - zIndex: 1000, - }, - - reportDropOverlay: { - backgroundColor: theme.dropUIBG, - zIndex: 2, - }, - - receiptDropOverlay: { - backgroundColor: theme.receiptDropUIBG, - zIndex: 2, - }, - - receiptImageWrapper: (receiptImageTopPosition: number): ViewStyle => ({ - position: 'absolute', - top: receiptImageTopPosition, - }), - - cardSection: { - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusCard, - marginBottom: 20, - marginHorizontal: 16, - padding: 20, - width: 'auto', - textAlign: 'left', - }, - - cardSectionTitle: { - lineHeight: variables.lineHeightXXLarge, - }, - - cardMenuItem: { - paddingLeft: 8, - paddingRight: 0, - borderRadius: variables.buttonBorderRadius, - height: variables.componentSizeLarge, - alignItems: 'center', - }, - - callRequestSection: { - backgroundColor: theme.appBG, - paddingHorizontal: 0, - paddingBottom: 0, - marginHorizontal: 0, - marginBottom: 0, - }, - - archivedReportFooter: { - borderRadius: variables.componentBorderRadius, - ...wordBreak.breakWord, - }, - - saveButtonPadding: { - paddingLeft: 18, - paddingRight: 18, - }, - - deeplinkWrapperContainer: { - padding: 20, - flex: 1, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: theme.appBG, - }, - - deeplinkWrapperMessage: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - - deeplinkWrapperFooter: { - paddingTop: 80, - paddingBottom: 45, - }, - - emojiReactionBubble: { - borderRadius: 28, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - alignSelf: 'flex-start', - }, - - emojiReactionListHeader: { - marginTop: 8, - paddingBottom: 20, - borderBottomColor: theme.border, - borderBottomWidth: 1, - marginHorizontal: 20, - }, - emojiReactionListHeaderBubble: { - paddingVertical: 2, - paddingHorizontal: 8, - borderRadius: 28, - backgroundColor: theme.border, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - alignSelf: 'flex-start', - marginRight: 4, - }, - reactionListItem: { - flexDirection: 'row', - paddingVertical: 12, - paddingHorizontal: 20, - }, - reactionListHeaderText: { - color: theme.textSupporting, - marginLeft: 8, - alignSelf: 'center', - }, - - miniQuickEmojiReactionText: { - fontSize: 15, - lineHeight: 20, - textAlignVertical: 'center', - }, - - emojiReactionBubbleText: { - textAlignVertical: 'center', - }, - - reactionCounterText: { - fontSize: 13, - marginLeft: 4, - fontWeight: 'bold', - }, - - fontColorReactionLabel: { - color: theme.tooltipSupportingText, - }, - - reactionEmojiTitle: { - fontSize: variables.iconSizeLarge, - lineHeight: variables.iconSizeXLarge, - }, - - textReactionSenders: { - color: theme.tooltipPrimaryText, - ...wordBreak.breakWord, - }, - - quickReactionsContainer: { - gap: 12, - flexDirection: 'row', - paddingHorizontal: 25, - paddingVertical: 12, - justifyContent: 'space-between', - }, - - reactionListContainer: { - maxHeight: variables.listItemHeightNormal * 5.75, - ...spacing.pv2, - }, - - reactionListContainerFixedWidth: { - maxWidth: variables.popoverWidth, - }, - - validateCodeDigits: { - color: theme.text, - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeXXLarge, - letterSpacing: 4, - }, + textInputAndIconContainer: { + flex: 1, + height: '100%', + zIndex: -1, + flexDirection: 'row', + }, - footerWrapper: { - fontSize: variables.fontSizeNormal, - paddingTop: 64, - maxWidth: 1100, // Match footer across all Expensify platforms - }, - - footerColumnsContainer: { - flex: 1, - flexWrap: 'wrap', - marginBottom: 40, - marginHorizontal: -16, - }, - - footerTitle: { - fontSize: variables.fontSizeLarge, - color: theme.success, - marginBottom: 16, - }, - - footerRow: { - paddingVertical: 4, - marginBottom: 8, - color: theme.textLight, - fontSize: variables.fontSizeMedium, - }, - - footerBottomLogo: { - marginTop: 40, - width: '100%', - }, - - listPickerSeparator: { - height: 1, - backgroundColor: theme.buttonDefaultBG, - }, - - datePickerRoot: { - position: 'relative', - zIndex: 99, - }, - - datePickerPopover: { - backgroundColor: theme.appBG, - width: '100%', - alignSelf: 'center', - zIndex: 100, - marginTop: 8, - }, - - loginHeroHeader: { - fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, - color: theme.success, - fontWeight: '500', - textAlign: 'center', - }, - - newKansasLarge: { - ...headlineFont, - fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXXLarge, - }, - - loginHeroBody: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeSignInHeroBody, - color: theme.textLight, - textAlign: 'center', - }, - - linkPreviewWrapper: { - marginTop: 16, - borderLeftWidth: 4, - borderLeftColor: theme.border, - paddingLeft: 12, - }, - - linkPreviewImage: { - flex: 1, - resizeMode: 'contain', - borderRadius: 8, - marginTop: 8, - }, - - linkPreviewLogoImage: { - height: 16, - width: 16, - }, - - validateCodeMessage: { - width: variables.modalContentMaxWidth, - textAlign: 'center', - }, - - whisper: { - backgroundColor: theme.cardBG, - }, - - contextMenuItemPopoverMaxWidth: { - maxWidth: 375, - }, - - formSpaceVertical: { - height: 20, - width: 1, - }, - - taskCheckbox: { - height: 16, - width: 16, - }, - - taskTitleMenuItem: { - ...writingDirection.ltr, - ...headlineFont, - fontSize: variables.fontSizeXLarge, - maxWidth: '100%', - ...wordBreak.breakWord, - }, - - taskDescriptionMenuItem: { - maxWidth: '100%', - ...wordBreak.breakWord, - }, - - taskTitleDescription: { - fontFamily: fontFamily.EXP_NEUE, - fontSize: variables.fontSizeLabel, - color: theme.textSupporting, - lineHeight: variables.lineHeightNormal, - ...spacing.mb1, - }, - - taskMenuItemCheckbox: { - height: 27, - ...spacing.mr3, - }, - - reportHorizontalRule: { - borderBottomWidth: 1, - borderColor: theme.border, - ...spacing.mh5, - ...spacing.mv2, - }, - - assigneeTextStyle: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - minHeight: variables.avatarSizeSubscript, - }, - - taskRightIconContainer: { - width: variables.componentSizeNormal, - marginLeft: 'auto', - ...spacing.mt1, - ...pointerEventsAuto, - }, - - shareCodePage: { - paddingHorizontal: 38.5, - }, - - shareCodeContainer: { - width: '100%', - alignItems: 'center', - paddingHorizontal: variables.qrShareHorizontalPadding, - paddingVertical: 20, - borderRadius: 20, - overflow: 'hidden', - borderColor: theme.borderFocus, - borderWidth: 2, - backgroundColor: theme.highlightBG, - }, - - splashScreenHider: { - backgroundColor: theme.splashBG, - alignItems: 'center', - justifyContent: 'center', - }, - - headerEnvBadge: { - marginLeft: 0, - marginBottom: 2, - height: 12, - paddingLeft: 4, - paddingRight: 4, - alignItems: 'center', - }, - - headerEnvBadgeText: { - fontSize: 7, - fontWeight: fontWeightBold, - lineHeight: undefined, - }, - - expensifyQrLogo: { - alignSelf: 'stretch', - height: 27, - marginBottom: 20, - }, - - qrShareTitle: { - marginTop: 15, - textAlign: 'center', - }, - - loginButtonRow: { - justifyContent: 'center', - width: '100%', - ...flex.flexRow, - }, - - loginButtonRowSmallScreen: { - justifyContent: 'center', - width: '100%', - marginBottom: 10, - ...flex.flexRow, - }, - - appleButtonContainer: { - width: 40, - height: 40, - marginRight: 20, - }, - - signInIconButton: { - margin: 10, - marginTop: 0, - padding: 2, - }, - - googleButtonContainer: { - colorScheme: 'light', - width: 40, - height: 40, - marginLeft: 12, - alignItems: 'center', - overflow: 'hidden', - }, - - googlePillButtonContainer: { - colorScheme: 'light', - height: 40, - width: 219, - }, - - thirdPartyLoadingContainer: { - alignItems: 'center', - justifyContent: 'center', - height: 450, - }, - - tabSelectorButton: { - height: variables.tabSelectorButtonHeight, - padding: variables.tabSelectorButtonPadding, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - borderRadius: variables.buttonBorderRadius, - }, - - tabSelector: { - flexDirection: 'row', - paddingHorizontal: 20, - paddingBottom: 12, - }, - - tabText: (isSelected: boolean): TextStyle => ({ - marginLeft: 8, - fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE, - fontWeight: isSelected ? fontWeightBold : '400', - color: isSelected ? theme.textLight : theme.textSupporting, - }), - - overscrollSpacer: (backgroundColor: string, height: number): ViewStyle => ({ - backgroundColor, - height, - width: '100%', - position: 'absolute', - top: -height, - left: 0, - right: 0, - }), - - dualColorOverscrollSpacer: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - zIndex: -1, - }, - - willChangeTransform: { - willChange: 'transform', - }, - - dropDownButtonCartIconContainerPadding: { - paddingRight: 0, - paddingLeft: 0, - }, - - dropDownButtonArrowContain: { - marginLeft: 12, - marginRight: 14, - }, - - dropDownButtonCartIconView: { - borderTopRightRadius: variables.buttonBorderRadius, - borderBottomRightRadius: variables.buttonBorderRadius, - ...flex.flexRow, - ...flex.alignItemsCenter, - }, - - emojiPickerButtonDropdown: { - justifyContent: 'center', - backgroundColor: theme.activeComponentBG, - width: 86, - height: 52, - borderRadius: 26, - alignItems: 'center', - paddingLeft: 10, - paddingRight: 4, - marginBottom: 32, - alignSelf: 'flex-start', - }, - - emojiPickerButtonDropdownIcon: { - fontSize: 30, - }, - - moneyRequestImage: { - height: 200, - borderRadius: 16, - margin: 20, - }, - - reportPreviewBox: { - backgroundColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - maxWidth: variables.sideBarWidth, - width: '100%', - }, - - reportPreviewBoxHoverBorder: { - borderColor: theme.border, - backgroundColor: theme.border, - }, - - reportPreviewBoxBody: { - padding: 16, - }, - - reportActionItemImages: { - flexDirection: 'row', - borderWidth: 4, - borderColor: theme.transparent, - borderTopLeftRadius: variables.componentBorderRadiusLarge, - borderTopRightRadius: variables.componentBorderRadiusLarge, - borderBottomLeftRadius: variables.componentBorderRadiusLarge, - borderBottomRightRadius: variables.componentBorderRadiusLarge, - overflow: 'hidden', - height: 200, - }, - - reportActionItemImage: { - flex: 1, - width: '100%', - height: '100%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }, - - reportActionItemImageBorder: { - borderRightWidth: 2, - borderColor: theme.cardBG, - }, - - reportActionItemImagesMore: { - position: 'absolute', - borderRadius: 18, - backgroundColor: theme.cardBG, - width: 36, - height: 36, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }, - - moneyRequestHeaderStatusBarBadge: { - paddingHorizontal: 8, - borderRadius: variables.componentBorderRadiusSmall, - height: variables.inputHeightSmall, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.border, - marginRight: 12, - }, - - staticHeaderImage: { - minHeight: 240, - }, - - emojiPickerButtonDropdownContainer: { - flexDirection: 'row', - alignItems: 'center', - }, - - rotate90: { - transform: [{rotate: '90deg'}], - }, - - emojiStatusLHN: { - fontSize: 22, - }, - sidebarStatusAvatarContainer: { - height: 44, - width: 84, - backgroundColor: theme.componentBG, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - borderRadius: 42, - paddingHorizontal: 2, - marginVertical: -2, - marginRight: -2, - }, - sidebarStatusAvatar: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - - moneyRequestViewImage: { - ...spacing.mh5, - ...spacing.mv3, - overflow: 'hidden', - borderWidth: 2, - borderColor: theme.cardBG, - borderRadius: variables.componentBorderRadiusLarge, - height: 200, - maxWidth: 400, - }, - - distanceRequestContainer: (maxHeight: number): ViewStyle => ({ - ...flex.flexShrink2, - minHeight: variables.optionRowHeight * 2, - maxHeight, - }), - - mapViewContainer: { - ...flex.flex1, - ...spacing.p4, - minHeight: 300, - maxHeight: 500, - }, - - mapView: { - flex: 1, - borderRadius: 20, - overflow: 'hidden', - }, - - mapViewOverlay: { - flex: 1, - position: 'absolute', - left: 0, - top: 0, - borderRadius: variables.componentBorderRadiusLarge, - overflow: 'hidden', - backgroundColor: theme.highlightBG, - ...sizing.w100, - ...sizing.h100, - }, - - confirmationListMapItem: { - ...spacing.m5, - height: 200, - }, - - mapDirection: { - lineColor: theme.success, - lineWidth: 7, - }, - - mapDirectionLayer: { - layout: {'line-join': 'round', 'line-cap': 'round'}, - paint: {'line-color': theme.success, 'line-width': 7}, - }, - - mapPendingView: { - backgroundColor: theme.highlightBG, - ...flex.flex1, - borderRadius: variables.componentBorderRadiusLarge, - }, - userReportStatusEmoji: { - fontSize: variables.fontSizeNormal, - marginRight: 4, - }, - draggableTopBar: { - height: 30, - width: '100%', - }, -}); + textInputDesktop: addOutlineWidth({}, 0), + + textInputIconContainer: { + paddingHorizontal: 11, + justifyContent: 'center', + margin: 1, + }, + + secureInput: { + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + }, + + textInput: { + backgroundColor: 'transparent', + borderRadius: variables.componentBorderRadiusNormal, + height: variables.inputComponentSizeNormal, + borderColor: theme.border, + borderWidth: 1, + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + paddingLeft: 12, + paddingRight: 12, + paddingTop: 10, + paddingBottom: 10, + textAlignVertical: 'center', + }, + + textInputPrefixWrapper: { + position: 'absolute', + left: 0, + top: 0, + height: variables.inputHeight, + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + paddingTop: 23, + paddingBottom: 8, + }, + + textInputPrefix: { + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + textAlignVertical: 'center', + }, + + pickerContainer: { + borderBottomWidth: 2, + paddingLeft: 0, + borderStyle: 'solid', + borderColor: theme.border, + justifyContent: 'center', + backgroundColor: 'transparent', + height: variables.inputHeight, + overflow: 'hidden', + }, + + pickerContainerSmall: { + height: variables.inputHeightSmall, + }, + + pickerLabel: { + position: 'absolute', + left: 0, + top: 6, + zIndex: 1, + }, + + picker: (disabled = false, backgroundColor = theme.appBG) => + ({ + iconContainer: { + top: Math.round(variables.inputHeight * 0.5) - 11, + right: 0, + ...pointerEventsNone, + }, + + inputWeb: { + appearance: 'none', + ...(disabled ? cursor.cursorDisabled : cursor.cursorPointer), + ...picker(theme), + backgroundColor, + }, + + inputIOS: { + ...picker(theme), + }, + done: { + color: theme.text, + }, + doneDepressed: { + fontSize: 17, + }, + modalViewMiddle: { + backgroundColor: theme.border, + borderTopWidth: 0, + }, + modalViewBottom: { + backgroundColor: theme.highlightBG, + }, + + inputAndroid: { + ...picker(theme), + }, + } satisfies CustomPickerStyle), + + disabledText: { + color: theme.icon, + }, + + inputDisabled: { + backgroundColor: theme.highlightBG, + color: theme.icon, + }, + + noOutline: addOutlineWidth({}, 0), + + errorOutline: { + borderColor: theme.danger, + }, + + textLabelSupporting: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.textSupporting, + }, + + textLabelError: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.textError, + }, + + textReceiptUpload: { + ...headlineFont, + fontSize: variables.fontSizeXLarge, + color: theme.textLight, + textAlign: 'center', + }, + + subTextReceiptUpload: { + fontFamily: fontFamily.EXP_NEUE, + lineHeight: variables.lineHeightLarge, + textAlign: 'center', + color: theme.textLight, + }, + + furtherDetailsText: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + color: theme.textSupporting, + }, + + lh16: { + lineHeight: 16, + }, + + lh20: { + lineHeight: 20, + }, + + lh140Percent: { + lineHeight: '140%', + }, + + formHelp: { + color: theme.textSupporting, + fontSize: variables.fontSizeLabel, + lineHeight: variables.lineHeightLarge, + marginBottom: 4, + }, + + formError: { + color: theme.textError, + fontSize: variables.fontSizeLabel, + lineHeight: variables.formErrorLineHeight, + marginBottom: 4, + }, + + formSuccess: { + color: theme.success, + fontSize: variables.fontSizeLabel, + lineHeight: 18, + marginBottom: 4, + }, + + desktopRedirectPage: { + backgroundColor: theme.appBG, + minHeight: '100%', + flex: 1, + alignItems: 'center', + }, + + signInPage: { + backgroundColor: theme.highlightBG, + minHeight: '100%', + flex: 1, + }, + + signInPageHeroCenter: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'center', + alignItems: 'center', + }, + + signInPageGradient: { + height: '100%', + width: 540, + position: 'absolute', + top: 0, + left: 0, + }, + + signInPageGradientMobile: { + height: 300, + width: 800, + position: 'absolute', + top: 0, + left: 0, + }, + + signInBackground: { + position: 'absolute', + bottom: 0, + left: 0, + minHeight: 700, + }, + + signInPageInner: { + marginLeft: 'auto', + marginRight: 'auto', + height: '100%', + width: '100%', + }, + + signInPageContentTopSpacer: { + maxHeight: 132, + minHeight: 24, + }, + + signInPageContentTopSpacerSmallScreens: { + maxHeight: 132, + minHeight: 45, + }, + + signInPageLeftContainer: { + paddingLeft: 40, + paddingRight: 40, + }, + + signInPageLeftContainerWide: { + maxWidth: variables.sideBarWidth, + }, + + signInPageWelcomeFormContainer: { + maxWidth: CONST.SIGN_IN_FORM_WIDTH, + }, + + signInPageWelcomeTextContainer: { + width: CONST.SIGN_IN_FORM_WIDTH, + }, + + changeExpensifyLoginLinkContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + ...wordBreak.breakWord, + }, + + // Sidebar Styles + sidebar: { + backgroundColor: theme.sidebar, + height: '100%', + }, + + sidebarAnimatedWrapperContainer: { + height: '100%', + position: 'absolute', + }, + + sidebarFooter: { + alignItems: 'center', + display: 'flex', + justifyContent: 'center', + paddingVertical: variables.lineHeightXLarge, + width: '100%', + }, + + sidebarAvatar: { + backgroundColor: theme.icon, + borderRadius: 20, + height: variables.componentSizeNormal, + width: variables.componentSizeNormal, + }, + + statusIndicator: (backgroundColor = theme.danger) => + ({ + borderColor: theme.sidebar, + backgroundColor, + borderRadius: 8, + borderWidth: 2, + position: 'absolute', + right: -2, + top: -1, + height: 16, + width: 16, + zIndex: 10, + } satisfies ViewStyle), + + floatingActionButtonContainer: { + position: 'absolute', + right: 20, + + // The bottom of the floating action button should align with the bottom of the compose box. + // The value should be equal to the height + marginBottom + marginTop of chatItemComposeSecondaryRow + bottom: 25, + }, + + floatingActionButton: { + backgroundColor: theme.success, + height: variables.componentSizeLarge, + width: variables.componentSizeLarge, + borderRadius: 999, + alignItems: 'center', + justifyContent: 'center', + }, + + sidebarFooterUsername: { + color: theme.heading, + fontSize: variables.fontSizeLabel, + fontWeight: '700', + width: 200, + textOverflow: 'ellipsis', + overflow: 'hidden', + ...whiteSpace.noWrap, + }, + + sidebarFooterLink: { + color: theme.textSupporting, + fontSize: variables.fontSizeSmall, + textDecorationLine: 'none', + fontFamily: fontFamily.EXP_NEUE, + lineHeight: 20, + }, + + sidebarListContainer: { + scrollbarWidth: 'none', + paddingBottom: 4, + }, + + sidebarListItem: { + justifyContent: 'center', + textDecorationLine: 'none', + }, + + RHPNavigatorContainer: (isSmallScreenWidth: boolean) => + ({ + width: isSmallScreenWidth ? '100%' : variables.sideBarWidth, + position: 'absolute', + right: 0, + height: '100%', + } satisfies ViewStyle), + + onlyEmojisText: { + fontSize: variables.fontSizeOnlyEmojis, + lineHeight: variables.fontSizeOnlyEmojisHeight, + }, + + onlyEmojisTextLineHeight: { + lineHeight: variables.fontSizeOnlyEmojisHeight, + }, + + createMenuPositionSidebar: (windowHeight: number) => + ({ + horizontal: 18, + vertical: windowHeight - 100, + } satisfies AnchorPosition), + + createMenuPositionProfile: (windowWidth: number) => + ({ + horizontal: windowWidth - 355, + ...getPopOverVerticalOffset(162), + } satisfies AnchorPosition), + + createMenuPositionReportActionCompose: (windowHeight: number) => + ({ + horizontal: 18 + variables.sideBarWidth, + vertical: windowHeight - 83, + } satisfies AnchorPosition), + + createMenuPositionRightSidepane: { + right: 18, + bottom: 75, + }, + + createMenuContainer: { + width: variables.sideBarWidth - 40, + paddingVertical: 12, + }, + + createMenuHeaderText: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.heading, + }, + + popoverMenuItem: { + flexDirection: 'row', + borderRadius: 0, + paddingHorizontal: 20, + paddingVertical: 12, + justifyContent: 'space-between', + width: '100%', + }, + + popoverMenuIcon: { + width: variables.componentSizeNormal, + justifyContent: 'center', + alignItems: 'center', + }, + + popoverMenuText: { + fontSize: variables.fontSizeNormal, + color: theme.heading, + }, + + popoverInnerContainer: { + paddingTop: 0, // adjusting this because the mobile modal adds additional padding that we don't need for our layout + maxHeight: '95%', + }, + + menuItemTextContainer: { + minHeight: variables.componentSizeNormal, + }, + + chatLinkRowPressable: { + minWidth: 0, + textDecorationLine: 'none', + flex: 1, + }, + + sidebarLink: { + textDecorationLine: 'none', + }, + + sidebarLinkInner: { + alignItems: 'center', + flexDirection: 'row', + paddingLeft: 20, + paddingRight: 20, + }, + + sidebarLinkText: { + color: theme.textSupporting, + fontSize: variables.fontSizeNormal, + textDecorationLine: 'none', + overflow: 'hidden', + }, + + sidebarLinkHover: { + backgroundColor: theme.sidebarHover, + }, + + sidebarLinkActive: { + backgroundColor: theme.border, + textDecorationLine: 'none', + }, + + sidebarLinkTextBold: { + fontWeight: '700', + color: theme.heading, + }, + + sidebarLinkActiveText: { + color: theme.textSupporting, + fontSize: variables.fontSizeNormal, + textDecorationLine: 'none', + overflow: 'hidden', + }, + + optionItemAvatarNameWrapper: { + minWidth: 0, + flex: 1, + }, + + optionDisplayName: { + fontFamily: fontFamily.EXP_NEUE, + minHeight: variables.alternateTextHeight, + lineHeight: variables.lineHeightXLarge, + ...whiteSpace.noWrap, + }, + + optionDisplayNameCompact: { + minWidth: 'auto', + flexBasis: 'auto', + flexGrow: 0, + flexShrink: 1, + }, + + displayNameTooltipEllipsis: { + position: 'absolute', + opacity: 0, + right: 0, + bottom: 0, + }, + + optionAlternateText: { + minHeight: variables.alternateTextHeight, + lineHeight: variables.lineHeightXLarge, + }, + + optionAlternateTextCompact: { + flexShrink: 1, + flexGrow: 1, + flexBasis: 'auto', + ...optionAlternateTextPlatformStyles, + }, + + optionRow: { + minHeight: variables.optionRowHeight, + paddingTop: 12, + paddingBottom: 12, + }, + + optionRowSelected: { + backgroundColor: theme.activeComponentBG, + }, + + optionRowDisabled: { + color: theme.textSupporting, + }, + + optionRowCompact: { + height: variables.optionRowHeightCompact, + paddingTop: 12, + paddingBottom: 12, + }, + + optionsListSectionHeader: { + height: variables.optionsListSectionHeaderHeight, + }, + + overlayStyles: (current: OverlayStylesParams) => + ({ + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], + + // We need to stretch the overlay to cover the sidebar and the translate animation distance. + left: -2 * variables.sideBarWidth, + top: 0, + bottom: 0, + right: 0, + backgroundColor: theme.shadow, + opacity: current.progress.interpolate({ + inputRange: [0, 1], + outputRange: [0, variables.overlayOpacity], + extrapolate: 'clamp', + }), + } satisfies ViewStyle), + + appContent: { + backgroundColor: theme.appBG, + overflow: 'hidden', + }, + + appContentHeader: { + height: variables.contentHeaderHeight, + justifyContent: 'center', + display: 'flex', + paddingRight: 20, + }, + + appContentHeaderTitle: { + alignItems: 'center', + flexDirection: 'row', + }, + + LHNToggle: { + alignItems: 'center', + height: variables.contentHeaderHeight, + justifyContent: 'center', + paddingRight: 10, + paddingLeft: 20, + }, + + LHNToggleIcon: { + height: 15, + width: 18, + }, + + chatContent: { + flex: 4, + justifyContent: 'flex-end', + }, + + chatContentScrollView: { + flexGrow: 1, + justifyContent: 'flex-start', + paddingBottom: 16, + }, + + // Chat Item + chatItem: { + display: 'flex', + flexDirection: 'row', + paddingTop: 8, + paddingBottom: 8, + paddingLeft: 20, + paddingRight: 20, + }, + + chatItemRightGrouped: { + flexGrow: 1, + flexShrink: 1, + flexBasis: 0, + position: 'relative', + marginLeft: variables.chatInputSpacing, + }, + + chatItemRight: { + flexGrow: 1, + flexShrink: 1, + flexBasis: 0, + position: 'relative', + }, + + chatItemMessageHeader: { + alignItems: 'center', + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap', + }, + + chatItemMessageHeaderSender: { + color: theme.heading, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeNormal, + fontWeight: fontWeightBold, + lineHeight: variables.lineHeightXLarge, + ...wordBreak.breakWord, + }, + + chatItemMessageHeaderTimestamp: { + flexShrink: 0, + color: theme.textSupporting, + fontSize: variables.fontSizeSmall, + paddingTop: 2, + }, + + chatItemMessage: { + color: theme.text, + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE, + lineHeight: variables.lineHeightXLarge, + maxWidth: '100%', + ...cursor.cursorAuto, + ...whiteSpace.preWrap, + ...wordBreak.breakWord, + }, + + chatItemComposeWithFirstRow: { + minHeight: 90, + }, + + chatItemFullComposeRow: { + ...sizing.h100, + }, + + chatItemComposeBoxColor: { + borderColor: theme.border, + }, + + chatItemComposeBoxFocusedColor: { + borderColor: theme.borderFocus, + }, + + chatItemComposeBox: { + backgroundColor: theme.componentBG, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusRounded, + minHeight: variables.componentSizeMedium, + }, + + chatItemFullComposeBox: { + ...flex.flex1, + ...sizing.h100, + }, + + chatFooter: { + paddingLeft: 20, + paddingRight: 20, + display: 'flex', + backgroundColor: theme.appBG, + }, + + chatFooterFullCompose: { + flex: 1, + }, + + chatItemDraft: { + display: 'flex', + flexDirection: 'row', + paddingTop: 8, + paddingBottom: 8, + paddingLeft: 20, + paddingRight: 20, + }, + + chatItemReactionsDraftRight: { + marginLeft: 52, + }, + chatFooterAtTheTop: { + flexGrow: 1, + justifyContent: 'flex-start', + }, + + // Be extremely careful when editing the compose styles, as it is easy to introduce regressions. + // Make sure you run the following tests against any changes: #12669 + textInputCompose: addOutlineWidth( + { + backgroundColor: theme.componentBG, + borderColor: theme.border, + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeNormal, + borderWidth: 0, + height: 'auto', + lineHeight: variables.lineHeightXLarge, + ...overflowXHidden, + + // On Android, multiline TextInput with height: 'auto' will show extra padding unless they are configured with + // paddingVertical: 0, alignSelf: 'center', and textAlignVertical: 'center' + + paddingHorizontal: variables.avatarChatSpacing, + paddingTop: 0, + paddingBottom: 0, + alignSelf: 'center', + textAlignVertical: 'center', + }, + 0, + ), + + textInputFullCompose: { + alignSelf: 'stretch', + flex: 1, + maxHeight: '100%', + textAlignVertical: 'top', + }, + + editInputComposeSpacing: { + backgroundColor: theme.transparent, + marginVertical: 8, + }, + + // composer padding should not be modified unless thoroughly tested against the cases in this PR: #12669 + textInputComposeSpacing: { + paddingVertical: 5, + ...flex.flexRow, + flex: 1, + }, + + textInputComposeBorder: { + borderLeftWidth: 1, + borderColor: theme.border, + }, + + chatItemSubmitButton: { + alignSelf: 'flex-end', + borderRadius: variables.componentBorderRadiusRounded, + backgroundColor: theme.transparent, + height: 40, + padding: 10, + margin: 3, + justifyContent: 'center', + }, + + emojiPickerContainer: { + backgroundColor: theme.componentBG, + }, + + emojiHeaderContainer: { + backgroundColor: theme.componentBG, + display: 'flex', + height: CONST.EMOJI_PICKER_HEADER_HEIGHT, + justifyContent: 'center', + width: '100%', + }, + + emojiSkinToneTitle: { + width: '100%', + ...spacing.pv1, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + color: theme.heading, + fontSize: variables.fontSizeSmall, + }, + + // Emoji Picker Styles + emojiText: { + textAlign: 'center', + fontSize: variables.emojiSize, + ...spacing.pv0, + ...spacing.ph0, + lineHeight: variables.emojiLineHeight, + }, + + emojiItem: { + width: '12.5%', + textAlign: 'center', + borderRadius: 8, + paddingTop: 2, + paddingBottom: 2, + height: CONST.EMOJI_PICKER_ITEM_HEIGHT, + }, + + emojiItemHighlighted: { + transition: '0.2s ease', + backgroundColor: theme.buttonDefaultBG, + }, + + emojiItemKeyboardHighlighted: { + transition: '0.2s ease', + borderWidth: 1, + borderColor: theme.link, + borderRadius: variables.buttonBorderRadius, + }, + + categoryShortcutButton: { + flex: 1, + borderRadius: 8, + height: CONST.EMOJI_PICKER_ITEM_HEIGHT, + alignItems: 'center', + justifyContent: 'center', + }, + + chatItemEmojiButton: { + alignSelf: 'flex-end', + borderRadius: variables.buttonBorderRadius, + height: 40, + marginVertical: 3, + paddingHorizontal: 10, + justifyContent: 'center', + }, + + editChatItemEmojiWrapper: { + marginRight: 3, + alignSelf: 'flex-end', + }, + + hoveredButton: { + backgroundColor: theme.buttonHoveredBG, + }, + + composerSizeButton: { + alignSelf: 'center', + height: 32, + width: 32, + padding: 6, + margin: 3, + borderRadius: variables.componentBorderRadiusRounded, + backgroundColor: theme.transparent, + justifyContent: 'center', + }, + + chatItemAttachmentPlaceholder: { + backgroundColor: theme.sidebar, + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + height: 150, + textAlign: 'center', + verticalAlign: 'middle', + width: 200, + }, + + chatSwticherPillWrapper: { + marginTop: 5, + marginRight: 4, + }, + + navigationModalOverlay: { + ...userSelect.userSelectNone, + position: 'absolute', + width: '100%', + height: '100%', + transform: [ + { + translateX: -variables.sideBarWidth, + }, + ], + }, + + sidebarVisible: { + borderRightWidth: 1, + }, + + sidebarHidden: { + width: 0, + borderRightWidth: 0, + }, + + exampleCheckImage: { + width: '100%', + height: 80, + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + }, + + singleAvatar: { + height: 24, + width: 24, + backgroundColor: theme.icon, + borderRadius: 24, + }, + + singleSubscript: { + height: variables.iconSizeNormal, + width: variables.iconSizeNormal, + backgroundColor: theme.icon, + borderRadius: 20, + zIndex: 1, + }, + + singleAvatarSmall: { + height: 18, + width: 18, + backgroundColor: theme.icon, + borderRadius: 18, + }, + + secondAvatar: { + position: 'absolute', + right: -18, + bottom: -18, + borderWidth: 3, + borderRadius: 30, + borderColor: 'transparent', + }, + + secondAvatarSmall: { + position: 'absolute', + right: -13, + bottom: -13, + borderWidth: 3, + borderRadius: 18, + borderColor: 'transparent', + }, + + secondAvatarSubscript: { + position: 'absolute', + right: -6, + bottom: -6, + }, + + secondAvatarSubscriptCompact: { + position: 'absolute', + bottom: -1, + right: -1, + }, + + secondAvatarSubscriptSmallNormal: { + position: 'absolute', + bottom: 0, + right: 0, + }, + + leftSideLargeAvatar: { + left: 15, + }, + + rightSideLargeAvatar: { + right: 15, + zIndex: 2, + borderWidth: 4, + borderRadius: 100, + }, + + secondAvatarInline: { + bottom: -3, + right: -25, + borderWidth: 3, + borderRadius: 18, + borderColor: theme.cardBorder, + backgroundColor: theme.appBG, + }, + + avatarLarge: { + width: variables.avatarSizeLarge, + height: variables.avatarSizeLarge, + }, + + avatarNormal: { + height: variables.componentSizeNormal, + width: variables.componentSizeNormal, + borderRadius: variables.componentSizeNormal, + }, + + avatarSmall: { + height: variables.avatarSizeSmall, + width: variables.avatarSizeSmall, + borderRadius: variables.avatarSizeSmall, + }, + + avatarInnerText: { + color: theme.textLight, + fontSize: variables.fontSizeSmall, + lineHeight: undefined, + marginLeft: -3, + textAlign: 'center', + }, + + avatarInnerTextSmall: { + color: theme.textLight, + fontSize: variables.fontSizeExtraSmall, + lineHeight: undefined, + marginLeft: -2, + textAlign: 'center', + }, + + avatarSpace: { + top: 3, + left: 3, + }, + + avatar: { + backgroundColor: theme.sidebar, + borderColor: theme.sidebar, + }, + + focusedAvatar: { + backgroundColor: theme.border, + borderColor: theme.border, + }, + + emptyAvatar: { + height: variables.avatarSizeNormal, + width: variables.avatarSizeNormal, + }, + + emptyAvatarSmallNormal: { + height: variables.avatarSizeSmallNormal, + width: variables.avatarSizeSmallNormal, + }, + + emptyAvatarSmall: { + height: variables.avatarSizeSmall, + width: variables.avatarSizeSmall, + }, + + emptyAvatarSmaller: { + height: variables.avatarSizeSmaller, + width: variables.avatarSizeSmaller, + }, + + emptyAvatarMedium: { + height: variables.avatarSizeMedium, + width: variables.avatarSizeMedium, + }, + + emptyAvatarLarge: { + height: variables.avatarSizeLarge, + width: variables.avatarSizeLarge, + }, + + emptyAvatarMargin: { + marginRight: variables.avatarChatSpacing, + }, + + emptyAvatarMarginChat: { + marginRight: variables.avatarChatSpacing - 12, + }, + + emptyAvatarMarginSmall: { + marginRight: variables.avatarChatSpacing - 4, + }, + + emptyAvatarMarginSmaller: { + marginRight: variables.avatarChatSpacing - 4, + }, + + modalViewContainer: { + alignItems: 'center', + flex: 1, + }, + + borderTop: { + borderTopWidth: variables.borderTopWidth, + borderColor: theme.border, + }, + + borderTopRounded: { + borderTopWidth: 1, + borderColor: theme.border, + borderTopLeftRadius: variables.componentBorderRadiusNormal, + borderTopRightRadius: variables.componentBorderRadiusNormal, + }, + + borderBottomRounded: { + borderBottomWidth: 1, + borderColor: theme.border, + borderBottomLeftRadius: variables.componentBorderRadiusNormal, + borderBottomRightRadius: variables.componentBorderRadiusNormal, + }, + + borderBottom: { + borderBottomWidth: 1, + borderColor: theme.border, + }, + + borderNone: { + borderWidth: 0, + borderBottomWidth: 0, + }, + + borderRight: { + borderRightWidth: 1, + borderColor: theme.border, + }, + + borderLeft: { + borderLeftWidth: 1, + borderColor: theme.border, + }, + + pointerEventsNone, + + pointerEventsAuto, + + headerBar: { + overflow: 'hidden', + justifyContent: 'center', + display: 'flex', + paddingLeft: 20, + height: variables.contentHeaderHeight, + width: '100%', + }, + + imageViewContainer: { + width: '100%', + height: '100%', + alignItems: 'center', + justifyContent: 'center', + }, + + imageModalPDF: { + flex: 1, + backgroundColor: theme.modalBackground, + }, + + PDFView: { + // `display: grid` is not supported in native platforms! + // It's being used on Web/Desktop only to vertically center short PDFs, + // while preventing the overflow of the top of long PDF files. + ...display.dGrid, + backgroundColor: theme.modalBackground, + width: '100%', + height: '100%', + justifyContent: 'center', + overflow: 'hidden', + alignItems: 'center', + }, + + PDFViewList: { + overflowX: 'hidden', + // There properties disable "focus" effect on list + boxShadow: 'none', + outline: 'none', + }, + + getPDFPasswordFormStyle: (isSmallScreenWidth: boolean) => + ({ + width: isSmallScreenWidth ? '100%' : 350, + ...(isSmallScreenWidth && flex.flex1), + } satisfies ViewStyle), + + modalCenterContentContainer: { + flex: 1, + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.modalBackdrop, + }, + + centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean) => + ({ + borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, + marginHorizontal: isSmallScreenWidth ? 0 : 20, + } satisfies ViewStyle), + + imageModalImageCenterContainer: { + alignItems: 'center', + flex: 1, + justifyContent: 'center', + width: '100%', + }, + + defaultAttachmentView: { + backgroundColor: theme.sidebar, + borderRadius: variables.componentBorderRadiusNormal, + borderWidth: 1, + borderColor: theme.border, + flexDirection: 'row', + padding: 20, + alignItems: 'center', + }, + + notFoundSafeArea: { + flex: 1, + backgroundColor: theme.heading, + }, + + notFoundView: { + flex: 1, + alignItems: 'center', + paddingTop: 40, + paddingBottom: 40, + justifyContent: 'space-between', + }, + + notFoundLogo: { + width: 202, + height: 63, + }, + + notFoundContent: { + alignItems: 'center', + }, + + notFoundTextHeader: { + ...headlineFont, + color: theme.heading, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + marginTop: 20, + marginBottom: 8, + textAlign: 'center', + }, + + notFoundTextBody: { + color: theme.componentBG, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + fontSize: 15, + }, + + notFoundButtonText: { + color: theme.link, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + fontSize: 15, + }, + + blockingViewContainer: { + paddingBottom: variables.contentHeaderHeight, + }, + + defaultModalContainer: { + backgroundColor: theme.componentBG, + borderColor: theme.transparent, + }, + + reportActionContextMenuMiniButton: { + ...spacing.p1, + ...spacing.mv1, + ...spacing.mh1, + ...{borderRadius: variables.buttonBorderRadius}, + }, + + reportActionSystemMessageContainer: { + marginLeft: 42, + }, + + reportDetailsTitleContainer: { + ...flex.flexColumn, + ...flex.alignItemsCenter, + paddingHorizontal: 20, + paddingBottom: 20, + }, + + reportDetailsRoomInfo: { + ...flex.flex1, + ...flex.flexColumn, + ...flex.alignItemsCenter, + }, + + reportSettingsVisibilityText: { + textTransform: 'capitalize', + }, + + settingsPageBackground: { + flexDirection: 'column', + width: '100%', + flexGrow: 1, + }, + + settingsPageBody: { + width: '100%', + justifyContent: 'space-around', + }, + + settingsPageColumn: { + width: '100%', + alignItems: 'center', + justifyContent: 'space-around', + }, + + settingsPageContainer: { + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + }, + + twoFactorAuthSection: { + backgroundColor: theme.appBG, + padding: 0, + }, + + twoFactorAuthCodesBox: ({isExtraSmallScreenWidth, isSmallScreenWidth}: TwoFactorAuthCodesBoxParams) => { + let paddingHorizontal = spacing.ph9; + + if (isSmallScreenWidth) { + paddingHorizontal = spacing.ph4; + } + + if (isExtraSmallScreenWidth) { + paddingHorizontal = spacing.ph2; + } + + return { + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.highlightBG, + paddingVertical: 28, + borderRadius: 16, + marginTop: 32, + ...paddingHorizontal, + } satisfies ViewStyle; + }, + + twoFactorLoadingContainer: { + alignItems: 'center', + justifyContent: 'center', + height: 210, + }, + + twoFactorAuthCodesContainer: { + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + flexWrap: 'wrap', + gap: 12, + }, + + twoFactorAuthCode: { + fontFamily: fontFamily.MONOSPACE, + width: 112, + textAlign: 'center', + }, + + twoFactorAuthCodesButtonsContainer: { + flexDirection: 'row', + justifyContent: 'center', + gap: 12, + marginTop: 20, + flexWrap: 'wrap', + }, + + twoFactorAuthCodesButton: { + minWidth: 112, + }, + + twoFactorAuthCopyCodeButton: { + minWidth: 110, + }, + + anonymousRoomFooter: (isSmallSizeLayout: boolean) => + ({ + flexDirection: isSmallSizeLayout ? 'column' : 'row', + ...(!isSmallSizeLayout && { + alignItems: 'center', + justifyContent: 'space-between', + }), + padding: 20, + backgroundColor: theme.sidebar, + borderRadius: variables.componentBorderRadiusLarge, + overflow: 'hidden', + } satisfies ViewStyle & TextStyle), + anonymousRoomFooterWordmarkAndLogoContainer: (isSmallSizeLayout: boolean) => + ({ + flexDirection: 'row', + alignItems: 'center', + ...(isSmallSizeLayout && { + justifyContent: 'space-between', + marginTop: 16, + }), + } satisfies ViewStyle), + anonymousRoomFooterLogo: { + width: 88, + marginLeft: 0, + height: 20, + }, + anonymousRoomFooterLogoTaglineText: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeMedium, + color: theme.textLight, + }, + signInButtonAvatar: { + width: 80, + }, + + anonymousRoomFooterSignInButton: { + width: 110, + }, + + roomHeaderAvatarSize: { + height: variables.componentSizeLarge, + width: variables.componentSizeLarge, + }, + + roomHeaderAvatar: { + backgroundColor: theme.appBG, + borderRadius: 100, + borderColor: theme.componentBG, + borderWidth: 4, + }, + + roomHeaderAvatarOverlay: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + backgroundColor: theme.overlay, + opacity: variables.overlayOpacity, + borderRadius: 88, + }, + + rootNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1} satisfies ViewStyle), + RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1} satisfies ViewStyle), + + avatarInnerTextChat: { + color: theme.textLight, + fontSize: variables.fontSizeXLarge, + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + textAlign: 'center', + fontWeight: 'normal', + position: 'absolute', + width: 88, + left: -16, + }, + + svgAvatarBorder: { + borderRadius: 100, + overflow: 'hidden', + }, + + displayName: { + fontSize: variables.fontSizeLarge, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + color: theme.heading, + }, + + pageWrapper: { + width: '100%', + alignItems: 'center', + padding: 20, + }, + + avatarSectionWrapper: { + width: '100%', + alignItems: 'center', + paddingHorizontal: 20, + paddingBottom: 20, + }, + + avatarSectionWrapperSkeleton: { + width: '100%', + paddingHorizontal: 20, + paddingBottom: 20, + }, + + selectCircle: { + width: variables.componentSizeSmall, + height: variables.componentSizeSmall, + borderColor: theme.border, + borderWidth: 1, + borderRadius: variables.componentSizeSmall / 2, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.componentBG, + marginLeft: 8, + }, + + unreadIndicatorContainer: { + position: 'absolute', + top: -10, + left: 0, + width: '100%', + height: 20, + paddingHorizontal: 20, + flexDirection: 'row', + alignItems: 'center', + zIndex: 1, + ...cursor.cursorDefault, + }, + + unreadIndicatorLine: { + height: 1, + backgroundColor: theme.unreadIndicator, + flexGrow: 1, + marginRight: 8, + opacity: 0.5, + }, + + threadDividerLine: { + height: 1, + backgroundColor: theme.border, + flexGrow: 1, + marginHorizontal: 20, + }, + + unreadIndicatorText: { + color: theme.unreadIndicator, + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontSize: variables.fontSizeSmall, + fontWeight: fontWeightBold, + textTransform: 'capitalize', + }, + + flipUpsideDown: { + transform: [{rotate: '180deg'}], + }, + + navigationSceneContainer: { + backgroundColor: theme.appBG, + }, + + navigationScreenCardStyle: { + backgroundColor: theme.appBG, + height: '100%', + }, + + navigationSceneFullScreenWrapper: { + borderRadius: variables.componentBorderRadiusCard, + overflow: 'hidden', + height: '100%', + }, + + invisible: { + position: 'absolute', + opacity: 0, + }, + + containerWithSpaceBetween: { + justifyContent: 'space-between', + width: '100%', + flex: 1, + }, + + detailsPageSectionContainer: { + alignSelf: 'flex-start', + }, + + attachmentCarouselContainer: { + height: '100%', + width: '100%', + display: 'flex', + justifyContent: 'center', + ...cursor.cursorUnset, + }, + + attachmentArrow: { + zIndex: 23, + position: 'absolute', + }, + + attachmentRevealButtonContainer: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + ...spacing.ph4, + }, + + arrowIcon: { + height: 40, + width: 40, + alignItems: 'center', + paddingHorizontal: 0, + paddingTop: 0, + paddingBottom: 0, + }, + + detailsPageSectionVersion: { + alignSelf: 'center', + color: theme.textSupporting, + fontSize: variables.fontSizeSmall, + height: 24, + lineHeight: 20, + }, + + switchTrack: { + width: 50, + height: 28, + justifyContent: 'center', + borderRadius: 20, + padding: 15, + backgroundColor: theme.success, + }, + + switchInactive: { + backgroundColor: theme.border, + }, + + switchThumb: { + width: 22, + height: 22, + borderRadius: 11, + position: 'absolute', + left: 4, + backgroundColor: theme.appBG, + }, + + switchThumbTransformation: (translateX: AnimatableNumericValue) => + ({ + transform: [{translateX}], + } satisfies ViewStyle), + + radioButtonContainer: { + backgroundColor: theme.componentBG, + borderRadius: 10, + height: 20, + width: 20, + borderColor: theme.icon, + borderWidth: 1, + justifyContent: 'center', + alignItems: 'center', + }, + + checkboxPressable: { + borderRadius: 6, + padding: 2, + justifyContent: 'center', + alignItems: 'center', + }, + + checkedContainer: { + backgroundColor: theme.checkBox, + }, + + magicCodeInputContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + minHeight: variables.inputHeight, + }, + + magicCodeInput: { + fontSize: variables.fontSizeXLarge, + color: theme.heading, + lineHeight: variables.inputHeight, + }, + + // Manually style transparent, in iOS Safari, an input in a container with its opacity set to + // 0 (completely transparent) cannot handle user interaction, hence the Paste option is never shown + inputTransparent: { + color: 'transparent', + // These properties are available in browser only + ...(Browser.getBrowser() + ? { + caretColor: 'transparent', + WebkitTextFillColor: 'transparent', + // After setting the input text color to transparent, it acquires the background-color. + // However, it is not possible to override the background-color directly as explained in this resource: https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill + // Therefore, the transition effect needs to be delayed. + transitionDelay: '99999s', + } + : {}), + }, + + iouAmountText: { + ...headlineFont, + fontSize: variables.iouAmountTextSize, + color: theme.heading, + lineHeight: variables.inputHeight, + }, + + iouAmountTextInput: addOutlineWidth( + { + ...headlineFont, + fontSize: variables.iouAmountTextSize, + color: theme.heading, + padding: 0, + lineHeight: undefined, + }, + 0, + ), + + moneyRequestConfirmationAmount: { + ...headlineFont, + fontSize: variables.fontSizeh1, + }, + + moneyRequestMenuItem: { + flexDirection: 'row', + borderRadius: 0, + justifyContent: 'space-between', + width: '100%', + paddingHorizontal: 20, + paddingVertical: 12, + }, + + requestPreviewBox: { + marginTop: 12, + maxWidth: variables.sideBarWidth, + }, + + moneyRequestPreviewBox: { + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + maxWidth: variables.sideBarWidth, + width: '100%', + }, + + moneyRequestPreviewBoxText: { + padding: 16, + }, + + moneyRequestPreviewBoxLoading: { + // When a new IOU request arrives it is very briefly in a loading state, so set the minimum height of the container to 94 to match the rendered height after loading. + // Otherwise, the IOU request pay button will not be fully visible and the user will have to scroll up to reveal the entire IOU request container. + // See https://github.com/Expensify/App/issues/10283. + minHeight: 94, + width: '100%', + }, + + moneyRequestPreviewBoxAvatar: { + marginRight: -10, + marginBottom: 0, + }, + + moneyRequestPreviewAmount: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + }, + + defaultCheckmarkWrapper: { + marginLeft: 8, + alignSelf: 'center', + }, + + iouDetailsContainer: { + flexGrow: 1, + paddingStart: 20, + paddingEnd: 20, + }, + + codeWordWrapper: { + ...codeStyles.codeWordWrapper, + }, + + codeWordStyle: { + borderLeftWidth: 0, + borderRightWidth: 0, + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + paddingLeft: 0, + paddingRight: 0, + justifyContent: 'center', + ...codeStyles.codeWordStyle, + }, + + codeFirstWordStyle: { + borderLeftWidth: 1, + borderTopLeftRadius: 4, + borderBottomLeftRadius: 4, + paddingLeft: 5, + }, + + codeLastWordStyle: { + borderRightWidth: 1, + borderTopRightRadius: 4, + borderBottomRightRadius: 4, + paddingRight: 5, + }, + + fullScreenLoading: { + backgroundColor: theme.componentBG, + opacity: 0.8, + justifyContent: 'center', + alignItems: 'center', + zIndex: 10, + }, + + navigatorFullScreenLoading: { + backgroundColor: theme.highlightBG, + opacity: 1, + }, + + reimbursementAccountFullScreenLoading: { + backgroundColor: theme.componentBG, + opacity: 0.8, + justifyContent: 'flex-start', + alignItems: 'center', + zIndex: 10, + }, + + hiddenElementOutsideOfWindow: { + position: 'absolute', + top: -10000, + left: 0, + opacity: 0, + }, + + growlNotificationWrapper: { + zIndex: 2, + }, + + growlNotificationContainer: { + flex: 1, + justifyContent: 'flex-start', + position: 'absolute', + width: '100%', + top: 20, + ...spacing.pl5, + ...spacing.pr5, + }, + + growlNotificationDesktopContainer: { + maxWidth: variables.sideBarWidth, + right: 0, + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], + }, + + growlNotificationTranslateY: (translateY: AnimatableNumericValue) => + ({ + transform: [{translateY}], + } satisfies ViewStyle), + + makeSlideInTranslation: (translationType: Translation, fromValue: number) => + ({ + from: { + [translationType]: fromValue, + }, + to: { + [translationType]: 0, + }, + } satisfies CustomAnimation), + + growlNotificationBox: { + backgroundColor: theme.inverse, + borderRadius: variables.componentBorderRadiusNormal, + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + shadowColor: theme.shadow, + ...spacing.p5, + }, + + growlNotificationText: { + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.EXP_NEUE, + width: '90%', + lineHeight: variables.fontSizeNormalHeight, + color: theme.textReversed, + ...spacing.ml4, + }, + + blockquote: { + borderLeftColor: theme.border, + borderLeftWidth: 4, + paddingLeft: 12, + marginVertical: 4, + }, + + noSelect: { + boxShadow: 'none', + outline: 'none', + }, + + cardStyleNavigator: { + overflow: 'hidden', + height: '100%', + }, + + fullscreenCard: { + position: 'absolute', + left: 0, + top: 0, + width: '100%', + height: '100%', + }, + + fullscreenCardWeb: { + left: 'auto', + right: '-24%', + top: '-18%', + height: '120%', + }, + + fullscreenCardWebCentered: { + left: 0, + right: 0, + top: 0, + height: '60%', + }, + + fullscreenCardMobile: { + left: '-20%', + top: '-30%', + width: '150%', + }, + + fullscreenCardMediumScreen: { + left: '-15%', + top: '-30%', + width: '145%', + }, + + smallEditIcon: { + alignItems: 'center', + backgroundColor: theme.buttonHoveredBG, + borderColor: theme.textReversed, + borderRadius: 14, + borderWidth: 3, + color: theme.textReversed, + height: 28, + width: 28, + justifyContent: 'center', + }, + + smallAvatarEditIcon: { + position: 'absolute', + right: -4, + bottom: -4, + }, + + workspaceCard: { + width: '100%', + height: 400, + borderRadius: variables.componentBorderRadiusCard, + overflow: 'hidden', + backgroundColor: theme.heroCard, + }, + + workspaceCardMobile: { + height: 475, + }, + + workspaceCardMediumScreen: { + height: 540, + }, + + workspaceCardMainText: { + fontSize: variables.fontSizeXXXLarge, + fontWeight: 'bold', + lineHeight: variables.fontSizeXXXLarge, + }, + + workspaceCardContent: { + zIndex: 1, + padding: 50, + }, + + workspaceCardContentMediumScreen: { + padding: 25, + }, + + workspaceCardCTA: { + width: 250, + }, + + autoGrowHeightMultilineInput: { + maxHeight: 115, + }, + + peopleRow: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + ...spacing.ph5, + }, + + peopleRowBorderBottom: { + borderColor: theme.border, + borderBottomWidth: 1, + ...spacing.pb2, + }, + + peopleBadge: { + backgroundColor: theme.icon, + ...spacing.ph3, + }, + + peopleBadgeText: { + color: theme.textReversed, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightNormal, + ...whiteSpace.noWrap, + }, + + offlineFeedback: { + deleted: { + textDecorationLine: 'line-through', + textDecorationStyle: 'solid', + }, + pending: { + opacity: 0.5, + }, + error: { + flexDirection: 'row', + alignItems: 'center', + }, + container: { + ...spacing.pv2, + }, + textContainer: { + flexDirection: 'column', + flex: 1, + }, + text: { + color: theme.textSupporting, + textAlignVertical: 'center', + fontSize: variables.fontSizeLabel, + }, + errorDot: { + marginRight: 12, + }, + }, + + dotIndicatorMessage: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + + sidebarPopover: { + width: variables.sideBarWidth - 68, + }, + + cardOverlay: { + backgroundColor: theme.overlay, + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + opacity: variables.overlayOpacity, + }, + + communicationsLinkIcon: { + right: -36, + top: 0, + bottom: 0, + }, + + shortTermsBorder: { + borderWidth: 1, + borderColor: theme.border, + }, + + shortTermsHorizontalRule: { + borderBottomWidth: 1, + borderColor: theme.border, + ...spacing.mh3, + }, + + shortTermsLargeHorizontalRule: { + borderWidth: 1, + borderColor: theme.border, + ...spacing.mh3, + }, + + shortTermsRow: { + flexDirection: 'row', + padding: 12, + }, + + termsCenterRight: { + marginTop: 'auto', + marginBottom: 'auto', + }, + + shortTermsBoldHeadingSection: { + paddingRight: 12, + paddingLeft: 12, + marginTop: 12, + }, + + shortTermsHeadline: { + ...headlineFont, + ...whiteSpace.preWrap, + color: theme.heading, + fontSize: variables.fontSizeXXXLarge, + lineHeight: variables.lineHeightXXXLarge, + }, + + longTermsRow: { + flexDirection: 'row', + marginTop: 20, + }, + + collapsibleSectionBorder: { + borderBottomWidth: 2, + borderBottomColor: theme.border, + }, + + communicationsLinkHeight: { + height: variables.communicationsLinkHeight, + }, + + floatingMessageCounterWrapper: { + position: 'absolute', + left: '50%', + top: 0, + zIndex: 100, + ...visibility.hidden, + }, + + floatingMessageCounterWrapperAndroid: { + left: 0, + width: '100%', + alignItems: 'center', + position: 'absolute', + top: 0, + zIndex: 100, + ...visibility.hidden, + }, + + floatingMessageCounterSubWrapperAndroid: { + left: '50%', + width: 'auto', + }, + + floatingMessageCounter: { + left: '-50%', + ...visibility.visible, + }, + + floatingMessageCounterTransformation: (translateY: AnimatableNumericValue) => + ({ + transform: [{translateY}], + } satisfies ViewStyle), + + confirmationAnimation: { + height: 180, + width: 180, + marginBottom: 20, + }, + + googleSearchTextInputContainer: { + flexDirection: 'column', + }, + + googleSearchSeparator: { + height: 1, + backgroundColor: theme.border, + }, + + googleSearchText: { + color: theme.text, + fontSize: variables.fontSizeNormal, + lineHeight: variables.fontSizeNormalHeight, + fontFamily: fontFamily.EXP_NEUE, + flex: 1, + }, + + threeDotsPopoverOffset: (windowWidth: number) => + ({ + ...getPopOverVerticalOffset(60), + horizontal: windowWidth - 60, + } satisfies AnchorPosition), + + threeDotsPopoverOffsetNoCloseButton: (windowWidth: number) => + ({ + ...getPopOverVerticalOffset(60), + horizontal: windowWidth - 10, + } satisfies AnchorPosition), + + invert: { + // It's important to invert the Y AND X axis to prevent a react native issue that can lead to ANRs on android 13 + transform: [{scaleX: -1}, {scaleY: -1}], + }, + + keyboardShortcutModalContainer: { + maxHeight: '100%', + flex: 0, + flexBasis: 'auto', + }, + + keyboardShortcutTableWrapper: { + alignItems: 'center', + flex: 1, + height: 'auto', + maxHeight: '100%', + }, + + keyboardShortcutTableContainer: { + display: 'flex', + width: '100%', + borderColor: theme.border, + height: 'auto', + borderRadius: variables.componentBorderRadius, + borderWidth: 1, + }, + + keyboardShortcutTableRow: { + flex: 1, + flexDirection: 'row', + borderColor: theme.border, + flexBasis: 'auto', + alignSelf: 'stretch', + borderTopWidth: 1, + }, + + keyboardShortcutTablePrefix: { + width: '30%', + borderRightWidth: 1, + borderColor: theme.border, + }, + + keyboardShortcutTableFirstRow: { + borderTopWidth: 0, + }, + + iPhoneXSafeArea: { + backgroundColor: theme.inverse, + flex: 1, + }, + + transferBalancePayment: { + borderWidth: 1, + borderRadius: variables.componentBorderRadiusNormal, + borderColor: theme.border, + }, + + transferBalanceSelectedPayment: { + borderColor: theme.iconSuccessFill, + }, + + transferBalanceBalance: { + fontSize: 48, + }, + + closeAccountMessageInput: { + height: 153, + }, + + imageCropContainer: { + overflow: 'hidden', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.imageCropBackgroundColor, + ...cursor.cursorMove, + }, + + sliderKnobTooltipView: { + height: variables.sliderKnobSize, + width: variables.sliderKnobSize, + borderRadius: variables.sliderKnobSize / 2, + }, + + sliderKnob: { + backgroundColor: theme.success, + position: 'absolute', + height: variables.sliderKnobSize, + width: variables.sliderKnobSize, + borderRadius: variables.sliderKnobSize / 2, + left: -(variables.sliderKnobSize / 2), + ...cursor.cursorPointer, + }, + + sliderBar: { + backgroundColor: theme.border, + height: variables.sliderBarHeight, + borderRadius: variables.sliderBarHeight / 2, + alignSelf: 'stretch', + justifyContent: 'center', + }, + + screenCenteredContainer: { + flex: 1, + justifyContent: 'center', + marginBottom: 40, + padding: 16, + }, + + inlineSystemMessage: { + color: theme.textSupporting, + fontSize: variables.fontSizeLabel, + fontFamily: fontFamily.EXP_NEUE, + marginLeft: 6, + }, + + fullScreen: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + + invisibleOverlay: { + backgroundColor: theme.transparent, + zIndex: 1000, + }, + + reportDropOverlay: { + backgroundColor: theme.dropUIBG, + zIndex: 2, + }, + + receiptDropOverlay: { + backgroundColor: theme.receiptDropUIBG, + zIndex: 2, + }, + + receiptImageWrapper: (receiptImageTopPosition: number) => + ({ + position: 'absolute', + top: receiptImageTopPosition, + } satisfies ViewStyle), + + cardSection: { + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusCard, + marginBottom: 20, + marginHorizontal: 16, + padding: 20, + width: 'auto', + textAlign: 'left', + }, + + cardSectionTitle: { + lineHeight: variables.lineHeightXXLarge, + }, + + cardMenuItem: { + paddingLeft: 8, + paddingRight: 0, + borderRadius: variables.buttonBorderRadius, + height: variables.componentSizeLarge, + alignItems: 'center', + }, + + callRequestSection: { + backgroundColor: theme.appBG, + paddingHorizontal: 0, + paddingBottom: 0, + marginHorizontal: 0, + marginBottom: 0, + }, + + archivedReportFooter: { + borderRadius: variables.componentBorderRadius, + ...wordBreak.breakWord, + }, + + saveButtonPadding: { + paddingLeft: 18, + paddingRight: 18, + }, + + deeplinkWrapperContainer: { + padding: 20, + flex: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.appBG, + }, + + deeplinkWrapperMessage: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + + deeplinkWrapperFooter: { + paddingTop: 80, + paddingBottom: 45, + }, + + emojiReactionBubble: { + borderRadius: 28, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + alignSelf: 'flex-start', + }, + + emojiReactionListHeader: { + marginTop: 8, + paddingBottom: 20, + borderBottomColor: theme.border, + borderBottomWidth: 1, + marginHorizontal: 20, + }, + emojiReactionListHeaderBubble: { + paddingVertical: 2, + paddingHorizontal: 8, + borderRadius: 28, + backgroundColor: theme.border, + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'row', + alignSelf: 'flex-start', + marginRight: 4, + }, + reactionListItem: { + flexDirection: 'row', + paddingVertical: 12, + paddingHorizontal: 20, + }, + reactionListHeaderText: { + color: theme.textSupporting, + marginLeft: 8, + alignSelf: 'center', + }, + + miniQuickEmojiReactionText: { + fontSize: 15, + lineHeight: 20, + textAlignVertical: 'center', + }, + + emojiReactionBubbleText: { + textAlignVertical: 'center', + }, + + reactionCounterText: { + fontSize: 13, + marginLeft: 4, + fontWeight: 'bold', + }, + + fontColorReactionLabel: { + color: theme.tooltipSupportingText, + }, + + reactionEmojiTitle: { + fontSize: variables.iconSizeLarge, + lineHeight: variables.iconSizeXLarge, + }, + + textReactionSenders: { + color: theme.tooltipPrimaryText, + ...wordBreak.breakWord, + }, + + quickReactionsContainer: { + gap: 12, + flexDirection: 'row', + paddingHorizontal: 25, + paddingVertical: 12, + justifyContent: 'space-between', + }, + + reactionListContainer: { + maxHeight: variables.listItemHeightNormal * 5.75, + ...spacing.pv2, + }, + + reactionListContainerFixedWidth: { + maxWidth: variables.popoverWidth, + }, + + validateCodeDigits: { + color: theme.text, + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeXXLarge, + letterSpacing: 4, + }, + + footerWrapper: { + fontSize: variables.fontSizeNormal, + paddingTop: 64, + maxWidth: 1100, // Match footer across all Expensify platforms + }, + + footerColumnsContainer: { + flex: 1, + flexWrap: 'wrap', + marginBottom: 40, + marginHorizontal: -16, + }, + + footerTitle: { + fontSize: variables.fontSizeLarge, + color: theme.success, + marginBottom: 16, + }, + + footerRow: { + paddingVertical: 4, + marginBottom: 8, + color: theme.textLight, + fontSize: variables.fontSizeMedium, + }, + + footerBottomLogo: { + marginTop: 40, + width: '100%', + }, + + listPickerSeparator: { + height: 1, + backgroundColor: theme.buttonDefaultBG, + }, + + datePickerRoot: { + position: 'relative', + zIndex: 99, + }, + + datePickerPopover: { + backgroundColor: theme.appBG, + width: '100%', + alignSelf: 'center', + zIndex: 100, + marginTop: 8, + }, + + loginHeroHeader: { + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + color: theme.success, + fontWeight: '500', + textAlign: 'center', + }, + + newKansasLarge: { + ...headlineFont, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + }, + + loginHeroBody: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSignInHeroBody, + color: theme.textLight, + textAlign: 'center', + }, + + linkPreviewWrapper: { + marginTop: 16, + borderLeftWidth: 4, + borderLeftColor: theme.border, + paddingLeft: 12, + }, + + linkPreviewImage: { + flex: 1, + resizeMode: 'contain', + borderRadius: 8, + marginTop: 8, + }, + + linkPreviewLogoImage: { + height: 16, + width: 16, + }, + + validateCodeMessage: { + width: variables.modalContentMaxWidth, + textAlign: 'center', + }, + + whisper: { + backgroundColor: theme.cardBG, + }, + + contextMenuItemPopoverMaxWidth: { + maxWidth: 375, + }, + + formSpaceVertical: { + height: 20, + width: 1, + }, + + taskCheckbox: { + height: 16, + width: 16, + }, + + taskTitleMenuItem: { + ...writingDirection.ltr, + ...headlineFont, + fontSize: variables.fontSizeXLarge, + maxWidth: '100%', + ...wordBreak.breakWord, + }, + + taskDescriptionMenuItem: { + maxWidth: '100%', + ...wordBreak.breakWord, + }, + + taskTitleDescription: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeLabel, + color: theme.textSupporting, + lineHeight: variables.lineHeightNormal, + ...spacing.mb1, + }, + + taskMenuItemCheckbox: { + height: 27, + ...spacing.mr3, + }, + + reportHorizontalRule: { + borderBottomWidth: 1, + borderColor: theme.border, + ...spacing.mh5, + ...spacing.mv2, + }, + + assigneeTextStyle: { + fontFamily: fontFamily.EXP_NEUE_BOLD, + fontWeight: fontWeightBold, + minHeight: variables.avatarSizeSubscript, + }, + + taskRightIconContainer: { + width: variables.componentSizeNormal, + marginLeft: 'auto', + ...spacing.mt1, + ...pointerEventsAuto, + }, + + shareCodePage: { + paddingHorizontal: 38.5, + }, + + shareCodeContainer: { + width: '100%', + alignItems: 'center', + paddingHorizontal: variables.qrShareHorizontalPadding, + paddingVertical: 20, + borderRadius: 20, + overflow: 'hidden', + borderColor: theme.borderFocus, + borderWidth: 2, + backgroundColor: theme.highlightBG, + }, + + splashScreenHider: { + backgroundColor: theme.splashBG, + alignItems: 'center', + justifyContent: 'center', + }, + + headerEnvBadge: { + marginLeft: 0, + marginBottom: 2, + height: 12, + paddingLeft: 4, + paddingRight: 4, + alignItems: 'center', + }, + + headerEnvBadgeText: { + fontSize: 7, + fontWeight: fontWeightBold, + lineHeight: undefined, + }, + + expensifyQrLogo: { + alignSelf: 'stretch', + height: 27, + marginBottom: 20, + }, + + qrShareTitle: { + marginTop: 15, + textAlign: 'center', + }, + + loginButtonRow: { + justifyContent: 'center', + width: '100%', + ...flex.flexRow, + }, + + loginButtonRowSmallScreen: { + justifyContent: 'center', + width: '100%', + marginBottom: 10, + ...flex.flexRow, + }, + + appleButtonContainer: { + width: 40, + height: 40, + marginRight: 20, + }, + + signInIconButton: { + margin: 10, + marginTop: 0, + padding: 2, + }, + + googleButtonContainer: { + colorScheme: 'light', + width: 40, + height: 40, + marginLeft: 12, + alignItems: 'center', + overflow: 'hidden', + }, + + googlePillButtonContainer: { + colorScheme: 'light', + height: 40, + width: 219, + }, + + thirdPartyLoadingContainer: { + alignItems: 'center', + justifyContent: 'center', + height: 450, + }, + + tabSelectorButton: { + height: variables.tabSelectorButtonHeight, + padding: variables.tabSelectorButtonPadding, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + borderRadius: variables.buttonBorderRadius, + }, + + tabSelector: { + flexDirection: 'row', + paddingHorizontal: 20, + paddingBottom: 12, + }, + + tabText: (isSelected: boolean) => + ({ + marginLeft: 8, + fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE, + fontWeight: isSelected ? fontWeightBold : '400', + color: isSelected ? theme.textLight : theme.textSupporting, + } satisfies TextStyle), + + overscrollSpacer: (backgroundColor: string, height: number) => + ({ + backgroundColor, + height, + width: '100%', + position: 'absolute', + top: -height, + left: 0, + right: 0, + } satisfies ViewStyle), + + dualColorOverscrollSpacer: { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + zIndex: -1, + }, + + willChangeTransform: { + willChange: 'transform', + }, + + dropDownButtonCartIconContainerPadding: { + paddingRight: 0, + paddingLeft: 0, + }, + + dropDownButtonArrowContain: { + marginLeft: 12, + marginRight: 14, + }, + + dropDownButtonCartIconView: { + borderTopRightRadius: variables.buttonBorderRadius, + borderBottomRightRadius: variables.buttonBorderRadius, + ...flex.flexRow, + ...flex.alignItemsCenter, + }, + + emojiPickerButtonDropdown: { + justifyContent: 'center', + backgroundColor: theme.activeComponentBG, + width: 86, + height: 52, + borderRadius: 26, + alignItems: 'center', + paddingLeft: 10, + paddingRight: 4, + marginBottom: 32, + alignSelf: 'flex-start', + }, + + emojiPickerButtonDropdownIcon: { + fontSize: 30, + }, + + moneyRequestImage: { + height: 200, + borderRadius: 16, + margin: 20, + }, + + reportPreviewBox: { + backgroundColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + maxWidth: variables.sideBarWidth, + width: '100%', + }, + + reportPreviewBoxHoverBorder: { + borderColor: theme.border, + backgroundColor: theme.border, + }, + + reportPreviewBoxBody: { + padding: 16, + }, + + reportActionItemImages: { + flexDirection: 'row', + borderWidth: 4, + borderColor: theme.transparent, + borderTopLeftRadius: variables.componentBorderRadiusLarge, + borderTopRightRadius: variables.componentBorderRadiusLarge, + borderBottomLeftRadius: variables.componentBorderRadiusLarge, + borderBottomRightRadius: variables.componentBorderRadiusLarge, + overflow: 'hidden', + height: 200, + }, + + reportActionItemImage: { + flex: 1, + width: '100%', + height: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + + reportActionItemImageBorder: { + borderRightWidth: 2, + borderColor: theme.cardBG, + }, + + reportActionItemImagesMore: { + position: 'absolute', + borderRadius: 18, + backgroundColor: theme.cardBG, + width: 36, + height: 36, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + + moneyRequestHeaderStatusBarBadge: { + paddingHorizontal: 8, + borderRadius: variables.componentBorderRadiusSmall, + height: variables.inputHeightSmall, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.border, + marginRight: 12, + }, + + staticHeaderImage: { + minHeight: 240, + }, + + emojiPickerButtonDropdownContainer: { + flexDirection: 'row', + alignItems: 'center', + }, + + rotate90: { + transform: [{rotate: '90deg'}], + }, + + emojiStatusLHN: { + fontSize: 22, + }, + sidebarStatusAvatarContainer: { + height: 44, + width: 84, + backgroundColor: theme.componentBG, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + borderRadius: 42, + paddingHorizontal: 2, + marginVertical: -2, + marginRight: -2, + }, + sidebarStatusAvatar: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + + moneyRequestViewImage: { + ...spacing.mh5, + ...spacing.mv3, + overflow: 'hidden', + borderWidth: 2, + borderColor: theme.cardBG, + borderRadius: variables.componentBorderRadiusLarge, + height: 200, + maxWidth: 400, + }, + + distanceRequestContainer: (maxHeight: number) => + ({ + ...flex.flexShrink2, + minHeight: variables.optionRowHeight * 2, + maxHeight, + } satisfies ViewStyle), + + mapViewContainer: { + ...flex.flex1, + ...spacing.p4, + minHeight: 300, + maxHeight: 500, + }, + + mapView: { + flex: 1, + borderRadius: 20, + overflow: 'hidden', + }, + + mapViewOverlay: { + flex: 1, + position: 'absolute', + left: 0, + top: 0, + borderRadius: variables.componentBorderRadiusLarge, + overflow: 'hidden', + backgroundColor: theme.highlightBG, + ...sizing.w100, + ...sizing.h100, + }, + + confirmationListMapItem: { + ...spacing.m5, + height: 200, + }, + + mapDirection: { + lineColor: theme.success, + lineWidth: 7, + }, + + mapDirectionLayer: { + layout: {'line-join': 'round', 'line-cap': 'round'}, + paint: {'line-color': theme.success, 'line-width': 7}, + }, + + mapPendingView: { + backgroundColor: theme.highlightBG, + ...flex.flex1, + borderRadius: variables.componentBorderRadiusLarge, + }, + userReportStatusEmoji: { + fontSize: variables.fontSizeNormal, + marginRight: 4, + }, + draggableTopBar: { + height: 30, + width: '100%', + }, + } satisfies Styles); // For now we need to export the styles function that takes the theme as an argument // as something named different than "styles", because a lot of files import the "defaultStyles" diff --git a/src/styles/utilities/display.ts b/src/styles/utilities/display.ts index e236ad4ea14d..88b0e0cd5689 100644 --- a/src/styles/utilities/display.ts +++ b/src/styles/utilities/display.ts @@ -33,4 +33,9 @@ export default { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'block' as ViewStyle['display'], }, + + dGrid: { + // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + display: 'grid' as ViewStyle['display'], + }, } satisfies Record; From 5feb1fed6262fdc6f925e63f601acb8de0d6a186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Sat, 16 Sep 2023 00:03:21 +0100 Subject: [PATCH 014/205] Isolate some web-only styles --- src/styles/StyleUtils.ts | 19 ++++++++++++------- src/styles/cardStyles/index.ts | 5 ++--- .../index.desktop.ts | 5 ++--- .../index.website.ts | 5 ++--- src/styles/getTooltipStyles.ts | 7 +++---- src/styles/styles.ts | 6 ++---- src/styles/utilities/display.ts | 12 ++++++++++++ src/styles/utilities/overflowAuto/index.ts | 3 +++ src/styles/utilities/positioning.ts | 8 ++++++++ 9 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 550ae71365af..9736d79a4a4e 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -40,7 +40,7 @@ type ParsableStyle = ViewStyle | ((state: PressableStateCallbackType) => ViewSty type WorkspaceColorStyle = {backgroundColor: ColorValue; fill: ColorValue}; -type ModalPaddingStylesArgs = { +type ModalPaddingStylesParams = { shouldAddBottomSafeAreaMargin: boolean; shouldAddTopSafeAreaMargin: boolean; shouldAddBottomSafeAreaPadding: boolean; @@ -56,13 +56,19 @@ type ModalPaddingStylesArgs = { insets: EdgeInsets; }; -type AvatarBorderStyleArgs = { +type AvatarBorderStyleParams = { isHovered: boolean; isPressed: boolean; isInReportAction: boolean; shouldUseCardBackground: boolean; }; +type GetBaseAutoCompleteSuggestionContainerStyleParams = { + left: number; + bottom: number; + width: number; +}; + const workspaceColorOptions: WorkspaceColorStyle[] = [ {backgroundColor: colors.blue200, fill: colors.blue700}, {backgroundColor: colors.blue400, fill: colors.blue800}, @@ -531,7 +537,7 @@ function getModalPaddingStyles({ modalContainerStylePaddingTop, modalContainerStylePaddingBottom, insets, -}: ModalPaddingStylesArgs): ViewStyle { +}: ModalPaddingStylesParams): ViewStyle { // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top. // More info: issue #17376 const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop ?? 0 : safeAreaPaddingBottom; @@ -785,7 +791,7 @@ function getKeyboardShortcutsModalWidth(isSmallScreenWidth: boolean): ViewStyle return {maxWidth: 600}; } -function getHorizontalStackedAvatarBorderStyle({isHovered, isPressed, isInReportAction = false, shouldUseCardBackground = false}: AvatarBorderStyleArgs): ViewStyle { +function getHorizontalStackedAvatarBorderStyle({isHovered, isPressed, isInReportAction = false, shouldUseCardBackground = false}: AvatarBorderStyleParams): ViewStyle { let borderColor = shouldUseCardBackground ? themeColors.cardBG : themeColors.appBG; if (isHovered) { @@ -928,10 +934,9 @@ function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHe /** * Gets the correct position for the base auto complete suggestion container */ -function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: {left: number; bottom: number; width: number}): ViewStyle { +function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: GetBaseAutoCompleteSuggestionContainerStyleParams): ViewStyle { return { - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, bottom, left, width, diff --git a/src/styles/cardStyles/index.ts b/src/styles/cardStyles/index.ts index b2ba8d7c24f1..1f28ca4f4b78 100644 --- a/src/styles/cardStyles/index.ts +++ b/src/styles/cardStyles/index.ts @@ -1,12 +1,11 @@ -import {ViewStyle} from 'react-native'; +import positioning from '../utilities/positioning'; import GetCardStyles from './types'; /** * Get card style for cardStyleInterpolator */ const getCardStyles: GetCardStyles = (screenWidth) => ({ - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, width: screenWidth, height: '100%', }); diff --git a/src/styles/getNavigationModalCardStyles/index.desktop.ts b/src/styles/getNavigationModalCardStyles/index.desktop.ts index 422a17d0a9a8..75b5636f9bd8 100644 --- a/src/styles/getNavigationModalCardStyles/index.desktop.ts +++ b/src/styles/getNavigationModalCardStyles/index.desktop.ts @@ -1,4 +1,4 @@ -import {ViewStyle} from 'react-native'; +import positioning from '../utilities/positioning'; import GetNavigationModalCardStyles from './types'; const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ @@ -10,8 +10,7 @@ const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ width: '100%', height: '100%', - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, }); export default getNavigationModalCardStyles; diff --git a/src/styles/getNavigationModalCardStyles/index.website.ts b/src/styles/getNavigationModalCardStyles/index.website.ts index 422a17d0a9a8..75b5636f9bd8 100644 --- a/src/styles/getNavigationModalCardStyles/index.website.ts +++ b/src/styles/getNavigationModalCardStyles/index.website.ts @@ -1,4 +1,4 @@ -import {ViewStyle} from 'react-native'; +import positioning from '../utilities/positioning'; import GetNavigationModalCardStyles from './types'; const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ @@ -10,8 +10,7 @@ const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({ width: '100%', height: '100%', - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, }); export default getNavigationModalCardStyles; diff --git a/src/styles/getTooltipStyles.ts b/src/styles/getTooltipStyles.ts index 7747486a6eb3..f390d658cb5d 100644 --- a/src/styles/getTooltipStyles.ts +++ b/src/styles/getTooltipStyles.ts @@ -3,6 +3,7 @@ import fontFamily from './fontFamily'; import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour'; import styles from './styles'; import themeColors from './themes/default'; +import positioning from './utilities/positioning'; import spacing from './utilities/spacing'; import variables from './variables'; @@ -236,8 +237,7 @@ export default function getTooltipStyles( transform: [{scale}], }, rootWrapperStyle: { - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, backgroundColor: themeColors.heading, borderRadius: variables.componentBorderRadiusSmall, ...tooltipVerticalPadding, @@ -259,8 +259,7 @@ export default function getTooltipStyles( lineHeight: variables.lineHeightSmall, }, pointerWrapperStyle: { - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, top: pointerWrapperTop, left: pointerWrapperLeft, }, diff --git a/src/styles/styles.ts b/src/styles/styles.ts index c93e96c8fbee..1c663352569a 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -1596,8 +1596,7 @@ const styles = (theme: ThemeDefault) => overlayStyles: (current: OverlayStylesParams) => ({ - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, // We need to stretch the overlay to cover the sidebar and the translate animation distance. left: -2 * variables.sideBarWidth, @@ -2886,8 +2885,7 @@ const styles = (theme: ThemeDefault) => growlNotificationDesktopContainer: { maxWidth: variables.sideBarWidth, right: 0, - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". - position: 'fixed' as ViewStyle['position'], + ...positioning.pFixed, }, growlNotificationTranslateY: (translateY: AnimatableNumericValue) => diff --git a/src/styles/utilities/display.ts b/src/styles/utilities/display.ts index 88b0e0cd5689..124cd87bc67a 100644 --- a/src/styles/utilities/display.ts +++ b/src/styles/utilities/display.ts @@ -19,21 +19,33 @@ export default { display: 'none', }, + /** + * Web-only style. + */ dInline: { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'inline' as ViewStyle['display'], }, + /** + * Web-only style. + */ dInlineFlex: { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'inline-flex' as ViewStyle['display'], }, + /** + * Web-only style. + */ dBlock: { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'block' as ViewStyle['display'], }, + /** + * Web-only style. + */ dGrid: { // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". display: 'grid' as ViewStyle['display'], diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utilities/overflowAuto/index.ts index d73bee877c50..c87ea5437199 100644 --- a/src/styles/utilities/overflowAuto/index.ts +++ b/src/styles/utilities/overflowAuto/index.ts @@ -1,6 +1,9 @@ import {ViewStyle} from 'react-native'; import OverflowAutoStyles from './types'; +/** + * Web-only style. + */ const overflowAuto: OverflowAutoStyles = { // NOTE: asserting "overflow" to a valid type, because isn't possible to augment "overflow". overflow: 'auto' as ViewStyle['overflow'], diff --git a/src/styles/utilities/positioning.ts b/src/styles/utilities/positioning.ts index 651d2a12f2ea..7cd58c52618b 100644 --- a/src/styles/utilities/positioning.ts +++ b/src/styles/utilities/positioning.ts @@ -11,6 +11,14 @@ export default { pAbsolute: { position: 'absolute', }, + /** + * Web-only style. + */ + pFixed: { + // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + position: 'fixed' as ViewStyle['position'], + }, + t0: { top: 0, }, From 4023821b3f47345840b0bda1a4ad99c7b939793c Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 20 Sep 2023 10:41:27 +0800 Subject: [PATCH 015/205] Only search when online --- src/ONYXKEYS.ts | 3 + src/components/OptionsListSkeletonView.js | 33 +---- .../OptionsSelector/BaseOptionsSelector.js | 127 ++++++++++-------- src/libs/actions/Report.js | 30 +++++ src/pages/SearchPage.js | 27 ++++ 5 files changed, 137 insertions(+), 83 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index af060ea58901..0274d523094e 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -27,6 +27,9 @@ const ONYXKEYS = { /** Boolean flag set whenever the sidebar has loaded */ IS_SIDEBAR_LOADED: 'isSidebarLoaded', + /** Boolean flag set whenever we are searching for reports in the server */ + IS_SEARCHING_FOR_REPORTS: 'isSearchingForReports', + /** Note: These are Persisted Requests - not all requests in the main queue as the key name might lead one to believe */ PERSISTED_REQUESTS: 'networkRequestQueue', diff --git a/src/components/OptionsListSkeletonView.js b/src/components/OptionsListSkeletonView.js index 15c66affe84d..833cbc0f372e 100644 --- a/src/components/OptionsListSkeletonView.js +++ b/src/components/OptionsListSkeletonView.js @@ -1,11 +1,9 @@ import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import {Rect, Circle} from 'react-native-svg'; -import SkeletonViewContentLoader from 'react-content-loader/native'; import CONST from '../CONST'; -import themeColors from '../styles/themes/default'; import styles from '../styles/styles'; +import OptionsListSkeletonRow from './OptionsListSkeletonRow'; const propTypes = { /** Whether to animate the skeleton view */ @@ -56,32 +54,11 @@ class OptionsListSkeletonView extends React.Component { lineWidth = '25%'; } skeletonViewItems.push( - - - - - , + shouldAnimate={this.props.shouldAnimate} + lineWidth={lineWidth} + />, ); } diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index bff9f8b6d7d0..0a370cd7b2d5 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -17,6 +17,9 @@ import {propTypes as optionsSelectorPropTypes, defaultProps as optionsSelectorDe import setSelection from '../../libs/setSelection'; import compose from '../../libs/compose'; import getPlatform from '../../libs/getPlatform'; +import OptionsListSkeletonRow from '../OptionsListSkeletonRow'; +import Text from '../Text'; +import FormHelpMessage from '../FormHelpMessage'; const propTypes = { /** padding bottom style of safe area */ @@ -344,63 +347,77 @@ class BaseOptionsSelector extends Component { const shouldShowDefaultConfirmButton = !this.props.footerContent && defaultConfirmButtonText; const safeAreaPaddingBottomStyle = shouldShowFooter ? undefined : this.props.safeAreaPaddingBottomStyle; const textInput = ( - (this.textInput = el)} - value={this.props.value} - label={this.props.textInputLabel} - accessibilityLabel={this.props.textInputLabel} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} - onChangeText={this.props.onChangeText} - onSubmitEditing={this.selectFocusedOption} - placeholder={this.props.placeholderText} - maxLength={this.props.maxLength} - keyboardType={this.props.keyboardType} - onBlur={(e) => { - if (!this.props.shouldFocusOnSelectRow) { - return; - } - this.relatedTarget = e.relatedTarget; - }} - selectTextOnFocus - blurOnSubmit={Boolean(this.state.allOptions.length)} - spellCheck={false} - /> + <> + (this.textInput = el)} + value={this.props.value} + label={this.props.textInputLabel} + accessibilityLabel={this.props.textInputLabel} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} + onChangeText={this.props.onChangeText} + onSubmitEditing={this.selectFocusedOption} + placeholder={this.props.placeholderText} + maxLength={this.props.maxLength} + keyboardType={this.props.keyboardType} + onBlur={(e) => { + if (!this.props.shouldFocusOnSelectRow) { + return; + } + this.relatedTarget = e.relatedTarget; + }} + selectTextOnFocus + blurOnSubmit={Boolean(this.state.allOptions.length)} + spellCheck={false} + /> + {this.props.textInputAlert && ( + + )} + + + ); const optionsList = ( - (this.list = el)} - optionHoveredStyle={this.props.optionHoveredStyle} - onSelectRow={this.props.onSelectRow ? this.selectRow : undefined} - sections={this.props.sections} - focusedIndex={this.state.focusedIndex} - selectedOptions={this.props.selectedOptions} - canSelectMultipleOptions={this.props.canSelectMultipleOptions} - shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton} - multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText} - onAddToSelection={this.props.onAddToSelection} - hideSectionHeaders={this.props.hideSectionHeaders} - headerMessage={this.props.headerMessage} - boldStyle={this.props.boldStyle} - showTitleTooltip={this.props.showTitleTooltip} - isDisabled={this.props.isDisabled} - shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator} - highlightSelectedOptions={this.props.highlightSelectedOptions} - onLayout={() => { - if (this.props.selectedOptions.length === 0) { - this.scrollToIndex(this.state.focusedIndex, false); - } - - if (this.props.onLayout) { - this.props.onLayout(); - } - }} - contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]} - listContainerStyles={this.props.listContainerStyles} - listStyles={this.props.listStyles} - isLoading={!this.props.shouldShowOptions} - showScrollIndicator={this.props.showScrollIndicator} - isRowMultilineSupported={this.props.isRowMultilineSupported} - /> + <> + {this.props.shouldShowLoader && } + (this.list = el)} + optionHoveredStyle={this.props.optionHoveredStyle} + onSelectRow={this.props.onSelectRow ? this.selectRow : undefined} + sections={this.props.sections} + focusedIndex={this.state.focusedIndex} + selectedOptions={this.props.selectedOptions} + canSelectMultipleOptions={this.props.canSelectMultipleOptions} + shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton} + multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText} + onAddToSelection={this.props.onAddToSelection} + hideSectionHeaders={this.props.hideSectionHeaders} + headerMessage={this.props.headerMessage} + boldStyle={this.props.boldStyle} + showTitleTooltip={this.props.showTitleTooltip} + isDisabled={this.props.isDisabled} + shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator} + highlightSelectedOptions={this.props.highlightSelectedOptions} + onLayout={() => { + if (this.props.selectedOptions.length === 0) { + this.scrollToIndex(this.state.focusedIndex, false); + } + + if (this.props.onLayout) { + this.props.onLayout(); + } + }} + contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]} + listContainerStyles={this.props.listContainerStyles} + listStyles={this.props.listStyles} + isLoading={!this.props.shouldShowOptions} + showScrollIndicator={this.props.showScrollIndicator} + isRowMultilineSupported={this.props.isRowMultilineSupported} + /> + ); return ( 0) { + this.throttledSearchInServer(searchValue); + } + + // When the user searches we will this.setState({searchValue}, this.debouncedUpdateOptions); } @@ -116,6 +123,17 @@ class SearchPage extends Component { return sections; } + /** + * @param {string} searchValue + */ + searchInServer(searchValue) { + if (this.props.network.isOffline) { + return; + } + + Report.searchInServer(searchValue); + } + searchRendered() { Timing.end(CONST.TIMING.SEARCH_RENDER); Performance.markEnd(CONST.TIMING.SEARCH_RENDER); @@ -184,9 +202,11 @@ class SearchPage extends Component { showTitleTooltip shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} textInputLabel={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} + textInputAlert={this.props.network.isOffline ? 'You appear to be offline. Search results are limited until you come back online.' : ''} onLayout={this.searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus + shouldShowLoader={this.props.isSearchingForReports} /> @@ -212,5 +232,12 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, + isSearchingForReports: { + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + initWithStoredValues: false, + }, + network: { + key: ONYXKEYS.NETWORK, + } }), )(SearchPage); From dae32f291bf7793d257cf85874b8752968b77ce7 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 20 Sep 2023 12:52:44 +0800 Subject: [PATCH 016/205] Remove Text --- src/components/OptionsSelector/BaseOptionsSelector.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 0a370cd7b2d5..022eaab1ba58 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -18,7 +18,6 @@ import setSelection from '../../libs/setSelection'; import compose from '../../libs/compose'; import getPlatform from '../../libs/getPlatform'; import OptionsListSkeletonRow from '../OptionsListSkeletonRow'; -import Text from '../Text'; import FormHelpMessage from '../FormHelpMessage'; const propTypes = { From c0e5658b4092bb23282ddb6169bff290d06fcbba Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 20 Sep 2023 12:53:03 +0800 Subject: [PATCH 017/205] Add missing file --- src/components/OptionsListSkeletonRow.js | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/components/OptionsListSkeletonRow.js diff --git a/src/components/OptionsListSkeletonRow.js b/src/components/OptionsListSkeletonRow.js new file mode 100644 index 000000000000..9df9a610bc14 --- /dev/null +++ b/src/components/OptionsListSkeletonRow.js @@ -0,0 +1,48 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {Rect, Circle} from 'react-native-svg'; +import SkeletonViewContentLoader from 'react-content-loader/native'; +import themeColors from '../styles/themes/default'; +import CONST from '../CONST'; +import styles from '../styles/styles'; + +const propTypes = { + /** Whether to animate the skeleton view */ + shouldAnimate: PropTypes.bool.isRequired, + + /** Line width string */ + lineWidth: PropTypes.string.isRequired, +}; + +function OptionsListSkeletonRow({lineWidth, shouldAnimate}) { + return ( + + + + + + ); +} + +OptionsListSkeletonRow.propTypes = propTypes; +export default OptionsListSkeletonRow; \ No newline at end of file From 61e4199178325c68b07ae16ce567d4340b01fc86 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 20 Sep 2023 13:01:52 +0800 Subject: [PATCH 018/205] Run prettier --- src/components/OptionsListSkeletonRow.js | 2 +- .../OptionsSelector/BaseOptionsSelector.js | 9 ++-- src/libs/actions/Report.js | 50 ++++++++++--------- src/pages/SearchPage.js | 2 +- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/components/OptionsListSkeletonRow.js b/src/components/OptionsListSkeletonRow.js index 9df9a610bc14..d7968ef1ba80 100644 --- a/src/components/OptionsListSkeletonRow.js +++ b/src/components/OptionsListSkeletonRow.js @@ -45,4 +45,4 @@ function OptionsListSkeletonRow({lineWidth, shouldAnimate}) { } OptionsListSkeletonRow.propTypes = propTypes; -export default OptionsListSkeletonRow; \ No newline at end of file +export default OptionsListSkeletonRow; diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 022eaab1ba58..760f86db799a 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -375,13 +375,16 @@ class BaseOptionsSelector extends Component { isError={false} /> )} - - ); const optionsList = ( <> - {this.props.shouldShowLoader && } + {this.props.shouldShowLoader && ( + + )} (this.list = el)} optionHoveredStyle={this.props.optionHoveredStyle} diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 51fe69acb6fc..016b5928a8f6 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2063,29 +2063,33 @@ function clearPrivateNotesError(reportID, accountID) { * @param {string} searchInput */ function searchInServer(searchInput) { - API.read('SearchForReports', {searchInput}, { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - value: true, - } - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - value: false, - } - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - value: false, - } - ] - }); + API.read( + 'SearchForReports', + {searchInput}, + { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + value: true, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + value: false, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + value: false, + }, + ], + }, + ); } export { diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index fb95f3d24f7e..432c64d51be5 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -238,6 +238,6 @@ export default compose( }, network: { key: ONYXKEYS.NETWORK, - } + }, }), )(SearchPage); From 05a58d8ecbbea76e1b21a9f989d72e0bc278906f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Wed, 20 Sep 2023 17:07:13 +0100 Subject: [PATCH 019/205] Address review comments --- src/styles/StyleUtils.ts | 42 +++++++++---------- src/styles/addOutlineWidth/index.ts | 4 +- src/styles/addOutlineWidth/types.ts | 2 +- .../getReportActionContextMenuStyles.ts | 2 +- src/styles/utilities/display.ts | 8 ++-- src/styles/utilities/overflowAuto/index.ts | 2 +- src/styles/utilities/positioning.ts | 2 +- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 9736d79a4a4e..3c131b9645f2 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1,4 +1,4 @@ -import {Animated, FlexStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; +import {Animated, DimensionValue, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; import {EdgeInsets} from 'react-native-safe-area-context'; import {ValueOf} from 'type-fest'; import CONST from '../CONST'; @@ -272,7 +272,7 @@ function getZoomCursorStyle(isZoomed: boolean, isDragging: boolean): ViewStyle { return isDragging ? styles.cursorGrabbing : styles.cursorZoomOut; } -// NOTE: asserting some web style properties to a valid type, because isn't possible to augment them. +// NOTE: asserting some web style properties to a valid type, because it isn't possible to augment them. function getZoomSizingStyle( isZoomed: boolean, imgWidth: number, @@ -286,23 +286,23 @@ function getZoomSizingStyle( if (isLoading || imgWidth === 0 || imgHeight === 0) { return undefined; } - const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px` as ViewStyle['top']; - const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px` as ViewStyle['left']; + const top = `${Math.max((containerHeight - imgHeight) / 2, 0)}px` as DimensionValue; + const left = `${Math.max((containerWidth - imgWidth) / 2, 0)}px` as DimensionValue; // Return different size and offset style based on zoomScale and isZoom. if (isZoomed) { // When both width and height are smaller than container(modal) size, set the height by multiplying zoomScale if it is zoomed in. if (zoomScale >= 1) { return { - height: `${imgHeight * zoomScale}px` as FlexStyle['height'], - width: `${imgWidth * zoomScale}px` as FlexStyle['width'], + height: `${imgHeight * zoomScale}px` as DimensionValue, + width: `${imgWidth * zoomScale}px` as DimensionValue, }; } // If image height and width are bigger than container size, display image with original size because original size is bigger and position absolute. return { - height: `${imgHeight}px` as FlexStyle['height'], - width: `${imgWidth}px` as FlexStyle['width'], + height: `${imgHeight}px` as DimensionValue, + width: `${imgWidth}px` as DimensionValue, top, left, }; @@ -311,8 +311,8 @@ function getZoomSizingStyle( // If image is not zoomed in and image size is smaller than container size, display with original size based on offset and position absolute. if (zoomScale > 1) { return { - height: `${imgHeight}px` as FlexStyle['height'], - width: `${imgWidth}px` as FlexStyle['width'], + height: `${imgHeight}px` as DimensionValue, + width: `${imgWidth}px` as DimensionValue, top, left, }; @@ -320,11 +320,11 @@ function getZoomSizingStyle( // If image is bigger than container size, display full image in the screen with scaled size (fit by container size) and position absolute. // top, left offset should be different when displaying long or wide image. - const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px` as ViewStyle['top']; - const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px` as ViewStyle['left']; + const scaledTop = `${Math.max((containerHeight - imgHeight * zoomScale) / 2, 0)}px` as DimensionValue; + const scaledLeft = `${Math.max((containerWidth - imgWidth * zoomScale) / 2, 0)}px` as DimensionValue; return { - height: `${imgHeight * zoomScale}px` as FlexStyle['height'], - width: `${imgWidth * zoomScale}px` as FlexStyle['width'], + height: `${imgHeight * zoomScale}px` as DimensionValue, + width: `${imgWidth * zoomScale}px` as DimensionValue, top: scaledTop, left: scaledLeft, }; @@ -448,8 +448,8 @@ function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: nu /** * Generate a style for the background color of the Badge */ -function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, isAdHoc = false): ViewStyle { - if (success) { +function getBadgeColorStyle(isSuccess: boolean, isError: boolean, isPressed = false, isAdHoc = false): ViewStyle { + if (isSuccess) { if (isAdHoc) { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -459,7 +459,7 @@ function getBadgeColorStyle(success: boolean, error: boolean, isPressed = false, // eslint-disable-next-line @typescript-eslint/no-unsafe-return return isPressed ? styles.badgeSuccessPressed : styles.badgeSuccess; } - if (error) { + if (isError) { // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) // eslint-disable-next-line @typescript-eslint/no-unsafe-return return isPressed ? styles.badgeDangerPressed : styles.badgeDanger; @@ -516,7 +516,7 @@ function getAnimatedFABStyle(rotate: Animated.Value, backgroundColor: Animated.V }; } -function getWidthAndHeightStyle(width: number, height: number | undefined = undefined): ViewStyle { +function getWidthAndHeightStyle(width: number, height?: number): ViewStyle { return { width, height: height ?? width, @@ -909,12 +909,12 @@ function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean): ViewStyle /** * Gets styles for AutoCompleteSuggestion row */ -function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, hovered: boolean, currentEmojiIndex: number): ViewStyle[] { +function getAutoCompleteSuggestionItemStyle(highlightedEmojiIndex: number, rowHeight: number, isHovered: boolean, currentEmojiIndex: number): ViewStyle[] { let backgroundColor; if (currentEmojiIndex === highlightedEmojiIndex) { backgroundColor = themeColors.activeComponentBG; - } else if (hovered) { + } else if (isHovered) { backgroundColor = themeColors.hoverComponentBG; } @@ -1023,7 +1023,7 @@ function getEmojiReactionCounterTextStyle(hasUserReacted: boolean): TextStyle { * * @param direction - The direction of the rotation (CONST.DIRECTION.LEFT or CONST.DIRECTION.RIGHT). */ -function getDirectionStyle(direction: string): ViewStyle { +function getDirectionStyle(direction: ValueOf): ViewStyle { if (direction === CONST.DIRECTION.LEFT) { return {transform: [{rotate: '180deg'}]}; } diff --git a/src/styles/addOutlineWidth/index.ts b/src/styles/addOutlineWidth/index.ts index 257a3de2b6cd..6fe2ecf85978 100644 --- a/src/styles/addOutlineWidth/index.ts +++ b/src/styles/addOutlineWidth/index.ts @@ -9,11 +9,11 @@ import AddOutlineWidth from './types'; /** * Adds the addOutlineWidth property to an object to be used when styling */ -const addOutlineWidth: AddOutlineWidth = (obj, val, error = false) => ({ +const addOutlineWidth: AddOutlineWidth = (obj, val, hasError = false) => ({ ...obj, outlineWidth: val, outlineStyle: val ? 'auto' : 'none', - boxShadow: val !== 0 ? `0px 0px 0px ${val}px ${error ? themeDefault.danger : themeDefault.borderFocus}` : 'none', + boxShadow: val !== 0 ? `0px 0px 0px ${val}px ${hasError ? themeDefault.danger : themeDefault.borderFocus}` : 'none', }); export default addOutlineWidth; diff --git a/src/styles/addOutlineWidth/types.ts b/src/styles/addOutlineWidth/types.ts index fe5d1f5719b2..7a3a86506959 100644 --- a/src/styles/addOutlineWidth/types.ts +++ b/src/styles/addOutlineWidth/types.ts @@ -1,5 +1,5 @@ import {TextStyle} from 'react-native'; -type AddOutlineWidth = (obj: TextStyle, val?: number, error?: boolean) => TextStyle; +type AddOutlineWidth = (obj: TextStyle, val?: number, hasError?: boolean) => TextStyle; export default AddOutlineWidth; diff --git a/src/styles/getReportActionContextMenuStyles.ts b/src/styles/getReportActionContextMenuStyles.ts index 6b4ad8807552..cd3843169a43 100644 --- a/src/styles/getReportActionContextMenuStyles.ts +++ b/src/styles/getReportActionContextMenuStyles.ts @@ -15,7 +15,7 @@ const miniWrapperStyle: ViewStyle[] = [ borderWidth: 1, borderColor: themeColors.border, // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style. - // NOTE: asserting "transform" to a valid type, because isn't possible to augment "transform". + // NOTE: asserting "transform" to a valid type, because it isn't possible to augment "transform". transform: 'translateZ(0)' as unknown as ViewStyle['transform'], }, ]; diff --git a/src/styles/utilities/display.ts b/src/styles/utilities/display.ts index 124cd87bc67a..f14a25d641b1 100644 --- a/src/styles/utilities/display.ts +++ b/src/styles/utilities/display.ts @@ -23,7 +23,7 @@ export default { * Web-only style. */ dInline: { - // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". display: 'inline' as ViewStyle['display'], }, @@ -31,7 +31,7 @@ export default { * Web-only style. */ dInlineFlex: { - // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". display: 'inline-flex' as ViewStyle['display'], }, @@ -39,7 +39,7 @@ export default { * Web-only style. */ dBlock: { - // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". display: 'block' as ViewStyle['display'], }, @@ -47,7 +47,7 @@ export default { * Web-only style. */ dGrid: { - // NOTE: asserting "display" to a valid type, because isn't possible to augment "display". + // NOTE: asserting "display" to a valid type, because it isn't possible to augment "display". display: 'grid' as ViewStyle['display'], }, } satisfies Record; diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utilities/overflowAuto/index.ts index c87ea5437199..1e7ac8ed8246 100644 --- a/src/styles/utilities/overflowAuto/index.ts +++ b/src/styles/utilities/overflowAuto/index.ts @@ -5,7 +5,7 @@ import OverflowAutoStyles from './types'; * Web-only style. */ const overflowAuto: OverflowAutoStyles = { - // NOTE: asserting "overflow" to a valid type, because isn't possible to augment "overflow". + // NOTE: asserting "overflow" to a valid type, because it isn't possible to augment "overflow". overflow: 'auto' as ViewStyle['overflow'], }; diff --git a/src/styles/utilities/positioning.ts b/src/styles/utilities/positioning.ts index 7cd58c52618b..26e6198a5827 100644 --- a/src/styles/utilities/positioning.ts +++ b/src/styles/utilities/positioning.ts @@ -15,7 +15,7 @@ export default { * Web-only style. */ pFixed: { - // NOTE: asserting "position" to a valid type, because isn't possible to augment "position". + // NOTE: asserting "position" to a valid type, because it isn't possible to augment "position". position: 'fixed' as ViewStyle['position'], }, From 0d9a313de199df2554deb4f6ade28be73144e09f Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Thu, 21 Sep 2023 10:24:53 +0800 Subject: [PATCH 020/205] Slow down server search --- src/pages/SearchPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 432c64d51be5..9fae2fe2cd4a 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -59,7 +59,7 @@ class SearchPage extends Component { const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getSearchOptions(props.reports, props.personalDetails, '', props.betas); - this.throttledSearchInServer = _.throttle(this.searchInServer.bind(this), 300, {leading: false}); + this.throttledSearchInServer = _.throttle(this.searchInServer.bind(this), 1000, {leading: false}); this.state = { searchValue: '', From 71689bd7edd91cd94c4d871f7d5546d2c2bfb3b1 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:10:42 +0800 Subject: [PATCH 021/205] Add amount and thumbnail image --- src/components/EReceipt.js | 19 +++++++++++++------ src/styles/styles.js | 7 +++++++ src/styles/themes/default.js | 1 + 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 2068a5e50f12..1d4057c6e437 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -1,10 +1,13 @@ import React from 'react'; import {View} from 'react-native'; -import _ from 'underscore'; +import Text from './Text'; import styles from '../styles/styles'; import transactionPropTypes from './transactionPropTypes'; import * as ReceiptUtils from '../libs/ReceiptUtils'; -import Image from './Image'; +import * as ReportUtils from '../libs/ReportUtils'; +import * as CurrencyUtils from '../libs/CurrencyUtils'; +import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; +import ThumbnailImage from './ThumbnailImage'; const propTypes = { /** The transaction for the eReceipt */ @@ -17,16 +20,20 @@ const defaultProps = { function EReceipt({transaction}) { const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); + const {amount: transactionAmount, currency: transactionCurrency} = ReportUtils.getTransactionDetails(transaction); + const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); + const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); return ( <> - - + + {formattedTransactionAmount} ); } diff --git a/src/styles/styles.js b/src/styles/styles.js index 4a2472913fd2..58a6ada7de0c 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3553,6 +3553,13 @@ const styles = (theme) => ({ lineHeight: variables.lineHeightXXLarge, }, + eReceiptAmount: { + ...headlineFont, + fontSize: variables.fontSizeXXXLarge, + lineHeight: variables.lineHeightXXXLarge, + color: theme.eReceiptAmount, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js index 5ff997684304..75d3ded6b545 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.js @@ -83,6 +83,7 @@ const darkTheme = { QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', loungeAccessOverlay: colors.blue800, + eReceiptAmount: colors.green400, }; darkTheme.PAGE_BACKGROUND_COLORS = { From 8095338a5e17b95d7aaea97d38b5831ffe29d0e2 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:29:28 +0800 Subject: [PATCH 022/205] Add merchant --- src/components/EReceipt.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 1d4057c6e437..bca23ae83e24 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -20,7 +20,7 @@ const defaultProps = { function EReceipt({transaction}) { const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); - const {amount: transactionAmount, currency: transactionCurrency} = ReportUtils.getTransactionDetails(transaction); + const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant} = ReportUtils.getTransactionDetails(transaction); const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); return ( @@ -34,6 +34,7 @@ function EReceipt({transaction}) { /> {formattedTransactionAmount} + {transactionMerchant} ); } From d9ddb6f3def934ab57c0a2803855212f8b9acedf Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:40:40 +0800 Subject: [PATCH 023/205] Display waypoints --- src/components/EReceipt.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index bca23ae83e24..58d49f5a54be 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -1,13 +1,17 @@ import React from 'react'; import {View} from 'react-native'; +import lodashGet from 'lodash/get'; +import _ from 'underscore'; import Text from './Text'; import styles from '../styles/styles'; import transactionPropTypes from './transactionPropTypes'; import * as ReceiptUtils from '../libs/ReceiptUtils'; import * as ReportUtils from '../libs/ReportUtils'; import * as CurrencyUtils from '../libs/CurrencyUtils'; +import * as TransactionUtils from '../libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import ThumbnailImage from './ThumbnailImage'; +import useLocalize from '../hooks/useLocalize'; const propTypes = { /** The transaction for the eReceipt */ @@ -19,10 +23,12 @@ const defaultProps = { }; function EReceipt({transaction}) { + const {translate} = useLocalize(); const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant} = ReportUtils.getTransactionDetails(transaction); const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); + const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( <> @@ -35,6 +41,25 @@ function EReceipt({transaction}) { {formattedTransactionAmount} {transactionMerchant} + <> + {_.map(waypoints, (waypoint, key) => { + const index = TransactionUtils.getWaypointIndex(key); + let descriptionKey = 'distance.waypointDescription.'; + if (index === 0) { + descriptionKey += 'start'; + } else if (index === _.size(waypoints) - 1) { + descriptionKey += 'finish'; + } else { + descriptionKey += 'stop'; + } + return ( + + {translate(descriptionKey)} + {waypoint.address || ''} + + ); + })} + ); } From 71ff545f73fac187bf1db127243daf1a2c53794f Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:50:53 +0800 Subject: [PATCH 024/205] Remove unused import --- src/styles/themes/default.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js index 75d3ded6b545..561a1bafb532 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.js @@ -1,7 +1,6 @@ /* eslint-disable no-unused-vars */ import colors from '../colors'; import SCREENS from '../../SCREENS'; -import ROUTES from '../../ROUTES'; const darkTheme = { // Figma keys From 215c03eda0b4bbe33f0b9e8c891418342936da4c Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:51:13 +0800 Subject: [PATCH 025/205] Style e receipt merchant text --- src/components/EReceipt.js | 2 +- src/styles/styles.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 58d49f5a54be..3094ef59bf9c 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -40,7 +40,7 @@ function EReceipt({transaction}) { /> {formattedTransactionAmount} - {transactionMerchant} + {transactionMerchant} <> {_.map(waypoints, (waypoint, key) => { const index = TransactionUtils.getWaypointIndex(key); diff --git a/src/styles/styles.js b/src/styles/styles.js index 58a6ada7de0c..0b7aff4037fe 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3560,6 +3560,13 @@ const styles = (theme) => ({ color: theme.eReceiptAmount, }, + eReceiptMerchant: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXLarge, + color: theme.text, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, From 90ad2292efab5db14689ba5f0aa11fe6964b381b Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 15:57:30 +0800 Subject: [PATCH 026/205] Style eReceipt waypoint titles --- src/components/EReceipt.js | 2 +- src/styles/styles.js | 9 ++++++++- src/styles/themes/default.js | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 3094ef59bf9c..ba9dd13ae276 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -54,7 +54,7 @@ function EReceipt({transaction}) { } return ( - {translate(descriptionKey)} + {translate(descriptionKey)} {waypoint.address || ''} ); diff --git a/src/styles/styles.js b/src/styles/styles.js index 0b7aff4037fe..d17547341df0 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3557,7 +3557,7 @@ const styles = (theme) => ({ ...headlineFont, fontSize: variables.fontSizeXXXLarge, lineHeight: variables.lineHeightXXXLarge, - color: theme.eReceiptAmount, + color: theme.textBrand, }, eReceiptMerchant: { @@ -3567,6 +3567,13 @@ const styles = (theme) => ({ color: theme.text, }, + eReceiptWaypointTitle: { + fontFamily: fontFamily.EXP_NEUE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + color: theme.textBrand, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js index 561a1bafb532..eac8b087ead0 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.js @@ -82,7 +82,7 @@ const darkTheme = { QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', loungeAccessOverlay: colors.blue800, - eReceiptAmount: colors.green400, + textBrand: colors.green400, }; darkTheme.PAGE_BACKGROUND_COLORS = { From c1aa7b37a9a84eeb6fffbab0bb5a72e09d8f1ef0 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 16:05:51 +0800 Subject: [PATCH 027/205] Style eReceipt waypoint address --- src/components/EReceipt.js | 2 +- src/styles/styles.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index ba9dd13ae276..bd26b0a5b4b0 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -55,7 +55,7 @@ function EReceipt({transaction}) { return ( {translate(descriptionKey)} - {waypoint.address || ''} + {waypoint.address || ''} ); })} diff --git a/src/styles/styles.js b/src/styles/styles.js index d17547341df0..c15b0f8b9dcf 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3574,6 +3574,13 @@ const styles = (theme) => ({ color: theme.textBrand, }, + eReceiptWaypointAddress: { + fontFamily: fontFamily.MONOSPACE, + fontSize: variables.fontSizeNormal, + lineHeight: variables.lineHeightNormal, + color: theme.textColorfulBackground, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, From 5bb7b9eb6661903993300ded689d3f19c4e06ef2 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 16:10:18 +0800 Subject: [PATCH 028/205] Add date text --- src/components/EReceipt.js | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index bd26b0a5b4b0..b84be5dc8be7 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -25,7 +25,7 @@ const defaultProps = { function EReceipt({transaction}) { const {translate} = useLocalize(); const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); - const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant} = ReportUtils.getTransactionDetails(transaction); + const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); @@ -41,25 +41,25 @@ function EReceipt({transaction}) { {formattedTransactionAmount} {transactionMerchant} - <> - {_.map(waypoints, (waypoint, key) => { - const index = TransactionUtils.getWaypointIndex(key); - let descriptionKey = 'distance.waypointDescription.'; - if (index === 0) { - descriptionKey += 'start'; - } else if (index === _.size(waypoints) - 1) { - descriptionKey += 'finish'; - } else { - descriptionKey += 'stop'; - } - return ( - - {translate(descriptionKey)} - {waypoint.address || ''} - - ); - })} - + {_.map(waypoints, (waypoint, key) => { + const index = TransactionUtils.getWaypointIndex(key); + let descriptionKey = 'distance.waypointDescription.'; + if (index === 0) { + descriptionKey += 'start'; + } else if (index === _.size(waypoints) - 1) { + descriptionKey += 'finish'; + } else { + descriptionKey += 'stop'; + } + return ( + + {translate(descriptionKey)} + {waypoint.address || ''} + + ); + })} + {translate('common.date')} + {transactionDate} ); } From dd0fe7ebe01a713d0ba42004d9fc07c9f249160b Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 21 Sep 2023 16:18:38 +0800 Subject: [PATCH 029/205] WIP add logo --- src/components/EReceipt.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index b84be5dc8be7..cd5959b1a41d 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -12,6 +12,7 @@ import * as TransactionUtils from '../libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import ThumbnailImage from './ThumbnailImage'; import useLocalize from '../hooks/useLocalize'; +import fontFamily from '../styles/fontFamily'; const propTypes = { /** The transaction for the eReceipt */ @@ -60,6 +61,14 @@ function EReceipt({transaction}) { })} {translate('common.date')} {transactionDate} + + + ); } From f69bf9936eb47e7bb9d2a671b06020ab5fc91efa Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 08:37:03 +0800 Subject: [PATCH 030/205] Add logo and guaranteed eReceipt --- src/components/EReceipt.js | 7 +++++-- src/languages/en.ts | 3 +++ src/styles/styles.js | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index cd5959b1a41d..1f24144031d4 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -12,7 +12,9 @@ import * as TransactionUtils from '../libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import ThumbnailImage from './ThumbnailImage'; import useLocalize from '../hooks/useLocalize'; -import fontFamily from '../styles/fontFamily'; +import Icon from './Icon'; +import themeColors from '../styles/themes/default'; +import * as Expensicons from './Icon/Expensicons'; const propTypes = { /** The transaction for the eReceipt */ @@ -65,9 +67,10 @@ function EReceipt({transaction}) { + {translate('eReceipt.guaranteed')} ); diff --git a/src/languages/en.ts b/src/languages/en.ts index f7c028d2a106..495e7b0f7105 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1778,4 +1778,7 @@ export default { selectSuggestedAddress: 'Please select a suggested address', }, }, + eReceipt: { + guaranteed: 'Guaranteed eReceipt', + }, } satisfies TranslationBase; diff --git a/src/styles/styles.js b/src/styles/styles.js index c15b0f8b9dcf..de7cfec48ef7 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3581,6 +3581,13 @@ const styles = (theme) => ({ color: theme.textColorfulBackground, }, + eReceiptGuaranteed: { + fontFamily: fontFamily.MONOSPACE, + fontSize: variables.fontSizeSmall, + lineHeight: variables.lineHeightSmall, + color: theme.textColorfulBackground, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, From 3a67ed468d4721a7d819c92bb9c082683b416a83 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 08:57:16 +0800 Subject: [PATCH 031/205] Set up wrapping views with padding --- src/components/EReceipt.js | 80 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index 1f24144031d4..d89439e32326 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -33,46 +33,48 @@ function EReceipt({transaction}) { const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( - <> - - + + + + + + {formattedTransactionAmount} + {transactionMerchant} + {_.map(waypoints, (waypoint, key) => { + const index = TransactionUtils.getWaypointIndex(key); + let descriptionKey = 'distance.waypointDescription.'; + if (index === 0) { + descriptionKey += 'start'; + } else if (index === _.size(waypoints) - 1) { + descriptionKey += 'finish'; + } else { + descriptionKey += 'stop'; + } + return ( + + {translate(descriptionKey)} + {waypoint.address || ''} + + ); + })} + {translate('common.date')} + {transactionDate} + + + {translate('eReceipt.guaranteed')} + - {formattedTransactionAmount} - {transactionMerchant} - {_.map(waypoints, (waypoint, key) => { - const index = TransactionUtils.getWaypointIndex(key); - let descriptionKey = 'distance.waypointDescription.'; - if (index === 0) { - descriptionKey += 'start'; - } else if (index === _.size(waypoints) - 1) { - descriptionKey += 'finish'; - } else { - descriptionKey += 'stop'; - } - return ( - - {translate(descriptionKey)} - {waypoint.address || ''} - - ); - })} - {translate('common.date')} - {transactionDate} - - - {translate('eReceipt.guaranteed')} - - + ); } From 74664ceab6450c5411d8bdab9cc533cf11d61dd4 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 10:12:10 +0800 Subject: [PATCH 032/205] Fix lint issues / clean up --- src/components/Attachments/AttachmentView/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index e7d1dd5abb6b..b0e22c237eaf 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -69,6 +69,7 @@ function AttachmentView({ isWorkspaceAvatar, }) { const [loadComplete, setLoadComplete] = useState(false); + const currentRoute = useRoute(); // Handles case where source is a component (ex: SVG) if (_.isFunction(source)) { @@ -112,7 +113,6 @@ function AttachmentView({ ); } - const currentRoute = useRoute(); const reportID = _.get(currentRoute, ['params', 'reportID']); const report = ReportUtils.getReport(reportID); @@ -120,8 +120,6 @@ function AttachmentView({ const parentReportAction = ReportActionsUtils.getParentReportAction(report); const transactionID = _.get(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); const transaction = TransactionUtils.getTransaction(transactionID); - console.log('transaction', transaction); - const shouldShowEReceipt = TransactionUtils.isDistanceRequest(transaction); if (shouldShowEReceipt) { return ; From 6e1440edff178f5be01b29cef62756565971c6c6 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 10:12:35 +0800 Subject: [PATCH 033/205] Add the eReceipt background --- assets/images/eReceipt_background.svg | 1635 +++++++++++++++++++++++++ src/components/EReceipt.js | 25 +- src/styles/styles.js | 28 + src/styles/themes/default.js | 1 + src/styles/utilities/spacing.ts | 4 + 5 files changed, 1686 insertions(+), 7 deletions(-) create mode 100644 assets/images/eReceipt_background.svg diff --git a/assets/images/eReceipt_background.svg b/assets/images/eReceipt_background.svg new file mode 100644 index 000000000000..257489bd6fdd --- /dev/null +++ b/assets/images/eReceipt_background.svg @@ -0,0 +1,1635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js index d89439e32326..5f5619cb22e4 100644 --- a/src/components/EReceipt.js +++ b/src/components/EReceipt.js @@ -15,6 +15,7 @@ import useLocalize from '../hooks/useLocalize'; import Icon from './Icon'; import themeColors from '../styles/themes/default'; import * as Expensicons from './Icon/Expensicons'; +import EReceiptBackground from '../../assets/images/eReceipt_background.svg'; const propTypes = { /** The transaction for the eReceipt */ @@ -33,8 +34,14 @@ function EReceipt({transaction}) { const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( - - + + + + + - {formattedTransactionAmount} - {transactionMerchant} + + {formattedTransactionAmount} + {transactionMerchant} + {_.map(waypoints, (waypoint, key) => { const index = TransactionUtils.getWaypointIndex(key); let descriptionKey = 'distance.waypointDescription.'; @@ -56,14 +65,16 @@ function EReceipt({transaction}) { descriptionKey += 'stop'; } return ( - + {translate(descriptionKey)} {waypoint.address || ''} ); })} - {translate('common.date')} - {transactionDate} + + {translate('common.date')} + {transactionDate} + ({ color: theme.textColorfulBackground, }, + eReceiptBackgroundContainer: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'center', + alignItems: 'center', + }, + + eReceiptBackground: { + ...sizing.w100, + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + minHeight: 540, + }, + + eReceiptPanel: { + ...spacing.p5, + ...spacing.pb8, + ...flex.flex1, + backgroundColor: theme.panelBackground, + borderRadius: 20, + }, + loginHeroBody: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSignInHeroBody, diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js index eac8b087ead0..5e06753d719a 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.js @@ -83,6 +83,7 @@ const darkTheme = { starDefaultBG: 'rgb(254, 228, 94)', loungeAccessOverlay: colors.blue800, textBrand: colors.green400, + panelBackground: colors.green800, }; darkTheme.PAGE_BACKGROUND_COLORS = { diff --git a/src/styles/utilities/spacing.ts b/src/styles/utilities/spacing.ts index a3667f05ac06..a47efe504326 100644 --- a/src/styles/utilities/spacing.ts +++ b/src/styles/utilities/spacing.ts @@ -55,6 +55,10 @@ export default { marginHorizontal: -20, }, + mv0: { + marginVertical: 0, + }, + mv1: { marginVertical: 4, }, From f8ef14475d4abb35300cea6a4898138c3611d713 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 15:20:08 +0800 Subject: [PATCH 034/205] The eReceipt is specific to distance requests --- src/components/Attachments/AttachmentView/index.js | 6 +++--- src/components/{EReceipt.js => DistanceEReceipt.js} | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) rename src/components/{EReceipt.js => DistanceEReceipt.js} (95%) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index b0e22c237eaf..aca92bf7ba5d 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -3,6 +3,7 @@ import {View, ActivityIndicator} from 'react-native'; import _ from 'underscore'; import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; +import {useRoute} from '@react-navigation/native'; import styles from '../../../styles/styles'; import Icon from '../../Icon'; import * as Expensicons from '../../Icon/Expensicons'; @@ -17,10 +18,9 @@ import AttachmentViewPdf from './AttachmentViewPdf'; import addEncryptedAuthTokenToURL from '../../../libs/addEncryptedAuthTokenToURL'; import * as StyleUtils from '../../../styles/StyleUtils'; import {attachmentViewPropTypes, attachmentViewDefaultProps} from './propTypes'; -import {useRoute} from '@react-navigation/native'; import * as ReportUtils from '../../../libs/ReportUtils'; import * as TransactionUtils from '../../../libs/TransactionUtils'; -import EReceipt from '../../EReceipt'; +import DistanceEReceipt from '../../DistanceEReceipt'; import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; const propTypes = { @@ -122,7 +122,7 @@ function AttachmentView({ const transaction = TransactionUtils.getTransaction(transactionID); const shouldShowEReceipt = TransactionUtils.isDistanceRequest(transaction); if (shouldShowEReceipt) { - return ; + return ; } // For this check we use both source and file.name since temporary file source is a blob diff --git a/src/components/EReceipt.js b/src/components/DistanceEReceipt.js similarity index 95% rename from src/components/EReceipt.js rename to src/components/DistanceEReceipt.js index 5f5619cb22e4..c8d428dde000 100644 --- a/src/components/EReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -26,7 +26,7 @@ const defaultProps = { transaction: {}, }; -function EReceipt({transaction}) { +function DistanceEReceipt({transaction}) { const {translate} = useLocalize(); const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); @@ -89,7 +89,7 @@ function EReceipt({transaction}) { ); } -export default EReceipt; -EReceipt.displayName = 'EReceipt'; -EReceipt.propTypes = propTypes; -EReceipt.defaultProps = defaultProps; +export default DistanceEReceipt; +DistanceEReceipt.displayName = 'DistanceEReceipt'; +DistanceEReceipt.propTypes = propTypes; +DistanceEReceipt.defaultProps = defaultProps; From ab4db9793693bd7e57b254a3fd025657f61ee81d Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 15:38:36 +0800 Subject: [PATCH 035/205] Set up gap between main sections --- src/components/DistanceEReceipt.js | 46 ++++++++++++++++-------------- src/styles/utilities/spacing.ts | 4 +++ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index c8d428dde000..0543cbc00794 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -42,7 +42,7 @@ function DistanceEReceipt({transaction}) { pointerEvents="none" /> - + - + {formattedTransactionAmount} {transactionMerchant} - {_.map(waypoints, (waypoint, key) => { - const index = TransactionUtils.getWaypointIndex(key); - let descriptionKey = 'distance.waypointDescription.'; - if (index === 0) { - descriptionKey += 'start'; - } else if (index === _.size(waypoints) - 1) { - descriptionKey += 'finish'; - } else { - descriptionKey += 'stop'; - } - return ( - - {translate(descriptionKey)} - {waypoint.address || ''} - - ); - })} - - {translate('common.date')} - {transactionDate} + + {_.map(waypoints, (waypoint, key) => { + const index = TransactionUtils.getWaypointIndex(key); + let descriptionKey = 'distance.waypointDescription.'; + if (index === 0) { + descriptionKey += 'start'; + } else if (index === _.size(waypoints) - 1) { + descriptionKey += 'finish'; + } else { + descriptionKey += 'stop'; + } + return ( + + {translate(descriptionKey)} + {waypoint.address || ''} + + ); + })} + + {translate('common.date')} + {transactionDate} + Date: Fri, 22 Sep 2023 16:19:11 +0800 Subject: [PATCH 036/205] Spacing and layout within sections --- src/components/DistanceEReceipt.js | 17 ++++++++++------- src/styles/utilities/spacing.ts | 4 ++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 0543cbc00794..0f83adc86ef2 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -50,11 +50,11 @@ function DistanceEReceipt({transaction}) { shouldDynamicallyResize={false} /> - + {formattedTransactionAmount} {transactionMerchant} - + {_.map(waypoints, (waypoint, key) => { const index = TransactionUtils.getWaypointIndex(key); let descriptionKey = 'distance.waypointDescription.'; @@ -66,21 +66,24 @@ function DistanceEReceipt({transaction}) { descriptionKey += 'stop'; } return ( - + {translate(descriptionKey)} {waypoint.address || ''} ); })} - + {translate('common.date')} {transactionDate} - + diff --git a/src/styles/utilities/spacing.ts b/src/styles/utilities/spacing.ts index e87d8cf85b31..a998b3302b3a 100644 --- a/src/styles/utilities/spacing.ts +++ b/src/styles/utilities/spacing.ts @@ -171,6 +171,10 @@ export default { marginLeft: -32, }, + mt0: { + marginTop: 0, + }, + mt1: { marginTop: 4, }, From 8b8afe9c1aea0cd85ba57e50bed19bc49ca805ad Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 22 Sep 2023 16:35:58 +0800 Subject: [PATCH 037/205] Set width on eReceipt for large screens --- src/components/DistanceEReceipt.js | 2 +- src/styles/styles.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 0f83adc86ef2..ab9065a8c49f 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -34,7 +34,7 @@ function DistanceEReceipt({transaction}) { const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( - + ({ eReceiptPanel: { ...spacing.p5, ...spacing.pb8, - ...flex.flex1, backgroundColor: theme.panelBackground, borderRadius: 20, + width: 335, }, loginHeroBody: { From 630a6fdf8c8f2c2bb63042d0f778e15e51cdc282 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 25 Sep 2023 14:33:55 +0800 Subject: [PATCH 038/205] Make NewChatPage also search --- src/languages/en.ts | 3 +++ src/languages/es.ts | 3 +++ src/libs/actions/Report.js | 14 +++++++++++++- src/pages/NewChatPage.js | 14 +++++++++++++- src/pages/SearchPage.js | 17 ++--------------- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index f7c028d2a106..6f0258a3063e 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1554,6 +1554,9 @@ export default { screenShare: 'Screen share', screenShareRequest: 'Expensify is inviting you to a screen share', }, + search: { + offline: 'You appear to be offline; search results are limited.', + }, genericErrorPage: { title: 'Uh-oh, something went wrong!', body: { diff --git a/src/languages/es.ts b/src/languages/es.ts index a68f33a33730..1cc74b4db2aa 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1576,6 +1576,9 @@ export default { screenShare: 'Compartir pantalla', screenShareRequest: 'Expensify te está invitando a compartir la pantalla', }, + search: { + offline: 'You appear to be offline; search results are limited.', + }, genericErrorPage: { title: '¡Uh-oh, algo salió mal!', body: { diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 8d11dd1163af..14837304081a 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2131,9 +2131,15 @@ function clearPrivateNotesError(reportID, accountID) { } /** + * @private * @param {string} searchInput */ function searchInServer(searchInput) { + // We do not try to make this request while offline because it sets a loading indicator optimistically + if (isNetworkOffline) { + return; + } + API.read( 'SearchForReports', {searchInput}, @@ -2163,8 +2169,14 @@ function searchInServer(searchInput) { ); } +/** + * @private + * @param {string} searchInput + */ +const throttledSearchInServer = _.throttle(searchInServer, 1000, {leading: false}); + export { - searchInServer, + throttledSearchInServer, addComment, addAttachment, reconnect, diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index cb54aa8e5a7b..bd206eb65346 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -20,6 +20,7 @@ import compose from '../libs/compose'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; import variables from '../styles/variables'; +import useNetwork from '../hooks/useNetwork'; const propTypes = { /** Beta features list */ @@ -44,12 +45,13 @@ const defaultProps = { const excludedGroupEmails = _.without(CONST.EXPENSIFY_EMAILS, CONST.EMAIL.CONCIERGE); -function NewChatPage({betas, isGroupChat, personalDetails, reports, translate}) { +function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, isSearchingForReports}) { const [searchTerm, setSearchTerm] = useState(''); const [filteredRecentReports, setFilteredRecentReports] = useState([]); const [filteredPersonalDetails, setFilteredPersonalDetails] = useState([]); const [filteredUserToInvite, setFilteredUserToInvite] = useState(); const [selectedOptions, setSelectedOptions] = useState([]); + const {isOffline} = useNetwork(); const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; const headerMessage = OptionsListUtils.getHeaderMessage( @@ -167,6 +169,10 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate}) // eslint-disable-next-line react-hooks/exhaustive-deps }, [reports, personalDetails, searchTerm]); + // When search term updates we will fetch any reports + useEffect(() => { + Report.throttledSearchInServer(searchTerm); + }, [searchTerm]); return ( 1 ? translate('newChatPage.createGroup') : translate('newChatPage.createChat')} + textInputAlert={isOffline ? translate('search.offline') : ''} onConfirmSelection={createGroup} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} + shouldShowLoader={isSearchingForReports} /> @@ -229,5 +237,9 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, + isSearchingForReports: { + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + initWithStoredValues: false, + }, }), )(NewChatPage); diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 9fae2fe2cd4a..7dbddc166f47 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -59,8 +59,6 @@ class SearchPage extends Component { const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getSearchOptions(props.reports, props.personalDetails, '', props.betas); - this.throttledSearchInServer = _.throttle(this.searchInServer.bind(this), 1000, {leading: false}); - this.state = { searchValue: '', recentReports, @@ -78,7 +76,7 @@ class SearchPage extends Component { onChangeText(searchValue = '') { if (searchValue.length > 0) { - this.throttledSearchInServer(searchValue); + Report.throttledSearchInServer(searchValue); } // When the user searches we will @@ -123,17 +121,6 @@ class SearchPage extends Component { return sections; } - /** - * @param {string} searchValue - */ - searchInServer(searchValue) { - if (this.props.network.isOffline) { - return; - } - - Report.searchInServer(searchValue); - } - searchRendered() { Timing.end(CONST.TIMING.SEARCH_RENDER); Performance.markEnd(CONST.TIMING.SEARCH_RENDER); @@ -202,7 +189,7 @@ class SearchPage extends Component { showTitleTooltip shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} textInputLabel={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} - textInputAlert={this.props.network.isOffline ? 'You appear to be offline. Search results are limited until you come back online.' : ''} + textInputAlert={this.props.network.isOffline ? this.props.translate('search.offline') : ''} onLayout={this.searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus From 8c28a58bfb93ce1434adecf244fbb6f75a048bbf Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 25 Sep 2023 15:41:20 +0800 Subject: [PATCH 039/205] Center eReceipt vertically on page --- src/components/DistanceEReceipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index ab9065a8c49f..a96e8fb4c31b 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -34,7 +34,7 @@ function DistanceEReceipt({transaction}) { const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( - + Date: Mon, 25 Sep 2023 16:05:24 +0800 Subject: [PATCH 040/205] Use colors directly since it's a specific purpose --- src/styles/styles.js | 6 +++--- src/styles/themes/default.js | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index 936e16464fac..47407cb5c2a8 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3557,7 +3557,7 @@ const styles = (theme) => ({ ...headlineFont, fontSize: variables.fontSizeXXXLarge, lineHeight: variables.lineHeightXXXLarge, - color: theme.textBrand, + color: colors.green400, }, eReceiptMerchant: { @@ -3571,7 +3571,7 @@ const styles = (theme) => ({ fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeSmall, lineHeight: variables.lineHeightSmall, - color: theme.textBrand, + color: colors.green400, }, eReceiptWaypointAddress: { @@ -3611,7 +3611,7 @@ const styles = (theme) => ({ eReceiptPanel: { ...spacing.p5, ...spacing.pb8, - backgroundColor: theme.panelBackground, + color: colors.green800, borderRadius: 20, width: 335, }, diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js index 5e06753d719a..807d14d0a7aa 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/default.js @@ -82,8 +82,6 @@ const darkTheme = { QRLogo: colors.green400, starDefaultBG: 'rgb(254, 228, 94)', loungeAccessOverlay: colors.blue800, - textBrand: colors.green400, - panelBackground: colors.green800, }; darkTheme.PAGE_BACKGROUND_COLORS = { From 16f3d79c508c05ee7e979348c4cb636fb55749e1 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 25 Sep 2023 16:24:01 +0800 Subject: [PATCH 041/205] Fix missing colors import --- src/styles/styles.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/styles.js b/src/styles/styles.js index 47407cb5c2a8..7b09f7c2e8cc 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -26,6 +26,7 @@ import * as Browser from '../libs/Browser'; import cursor from './utilities/cursor'; import userSelect from './utilities/userSelect'; import textUnderline from './utilities/textUnderline'; +import colors from './colors'; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target const touchCalloutNone = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; From 7aef1259f94efeff401093b1f18856a390f29cef Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 25 Sep 2023 16:55:29 +0800 Subject: [PATCH 042/205] Fix background color --- src/styles/styles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index 7b09f7c2e8cc..bf66e8c378fd 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3612,7 +3612,7 @@ const styles = (theme) => ({ eReceiptPanel: { ...spacing.p5, ...spacing.pb8, - color: colors.green800, + backgroundColor: colors.green800, borderRadius: 20, width: 335, }, From 00108d1aa7bf8c8ba63d662527cf13eb085b4f47 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 25 Sep 2023 16:55:52 +0800 Subject: [PATCH 043/205] Make large eReceipts scrollable --- src/components/DistanceEReceipt.js | 110 +++++++++++++++-------------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index a96e8fb4c31b..115eac5b3939 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -1,5 +1,5 @@ import React from 'react'; -import {View} from 'react-native'; +import {View, ScrollView} from 'react-native'; import lodashGet from 'lodash/get'; import _ from 'underscore'; import Text from './Text'; @@ -35,61 +35,63 @@ function DistanceEReceipt({transaction}) { const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( - - - - - - - - - {formattedTransactionAmount} - {transactionMerchant} - - - {_.map(waypoints, (waypoint, key) => { - const index = TransactionUtils.getWaypointIndex(key); - let descriptionKey = 'distance.waypointDescription.'; - if (index === 0) { - descriptionKey += 'start'; - } else if (index === _.size(waypoints) - 1) { - descriptionKey += 'finish'; - } else { - descriptionKey += 'stop'; - } - return ( - - {translate(descriptionKey)} - {waypoint.address || ''} - - ); - })} - - {translate('common.date')} - {transactionDate} + + + + + + + + + + {formattedTransactionAmount} + {transactionMerchant} + + + {_.map(waypoints, (waypoint, key) => { + const index = TransactionUtils.getWaypointIndex(key); + let descriptionKey = 'distance.waypointDescription.'; + if (index === 0) { + descriptionKey += 'start'; + } else if (index === _.size(waypoints) - 1) { + descriptionKey += 'finish'; + } else { + descriptionKey += 'stop'; + } + return ( + + {translate(descriptionKey)} + {waypoint.address || ''} + + ); + })} + + {translate('common.date')} + {transactionDate} + + + + + {translate('eReceipt.guaranteed')} - - - {translate('eReceipt.guaranteed')} - - + ); } From d2305c241ae86d1f5c4adbe592d238a2ab484456 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 25 Sep 2023 17:21:10 +0800 Subject: [PATCH 044/205] Fix center eReceipt vertically with scroll view --- src/components/DistanceEReceipt.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 115eac5b3939..03aa5bf82ce5 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -34,8 +34,8 @@ function DistanceEReceipt({transaction}) { const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( - - + + Date: Mon, 25 Sep 2023 12:56:03 +0100 Subject: [PATCH 045/205] Improve some StyleUtils functions --- src/styles/StyleUtils.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 3c131b9645f2..0ca8ba320487 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1,4 +1,4 @@ -import {Animated, DimensionValue, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; +import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; import {EdgeInsets} from 'react-native-safe-area-context'; import {ValueOf} from 'type-fest'; import CONST from '../CONST'; @@ -13,6 +13,9 @@ import positioning from './utilities/positioning'; import spacing from './utilities/spacing'; import variables from './variables'; +type AllStyles = ViewStyle | TextStyle | ImageStyle; +type ParsableStyle = AllStyles | ((state: PressableStateCallbackType) => AllStyles); + type ColorValue = ValueOf; type AvatarSizeName = ValueOf; type AvatarSizeValue = ValueOf< @@ -36,7 +39,6 @@ type ButtonSizeValue = ValueOf; type EmptyAvatarSizeName = ValueOf>; type ButtonStateName = ValueOf; type AvatarSize = {width: number}; -type ParsableStyle = ViewStyle | ((state: PressableStateCallbackType) => ViewStyle); type WorkspaceColorStyle = {backgroundColor: ColorValue; fill: ColorValue}; @@ -696,14 +698,14 @@ function getThemeBackgroundColor(bgColor: string = themeColors.appBG): string { /** * Parse styleParam and return Styles array */ -function parseStyleAsArray(styleParam: ViewStyle | ViewStyle[]): ViewStyle[] { +function parseStyleAsArray(styleParam: T | T[]): T[] { return Array.isArray(styleParam) ? styleParam : [styleParam]; } /** * Parse style function and return Styles object */ -function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): ViewStyle[] { +function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): AllStyles[] { const functionAppliedStyle = typeof style === 'function' ? style(state) : style; return parseStyleAsArray(functionAppliedStyle); } @@ -711,8 +713,8 @@ function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallb /** * Receives any number of object or array style objects and returns them all as an array */ -function combineStyles(...allStyles: Array) { - let finalStyles: ViewStyle[][] = []; +function combineStyles(...allStyles: Array): T[] { + let finalStyles: T[] = []; allStyles.forEach((style) => { finalStyles = finalStyles.concat(parseStyleAsArray(style)); }); From 327109e8d074eb09ecc3cfc24dbc14fdf23dee48 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 26 Sep 2023 09:47:22 +0800 Subject: [PATCH 046/205] Add spanish translation --- src/languages/es.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index a68f33a33730..bf18c0dce2d2 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2261,4 +2261,7 @@ export default { selectSuggestedAddress: 'Por favor, selecciona una dirección sugerida', }, }, + eReceipt: { + guaranteed: 'eRecibo garantizado', + }, } satisfies EnglishTranslation; From 001799d5d7ca99fd92bd82ff3f04df1ac90f75c6 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 26 Sep 2023 10:56:33 +0800 Subject: [PATCH 047/205] Align background with top of eReceipt --- src/components/DistanceEReceipt.js | 10 ++++------ src/styles/styles.js | 14 +------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 03aa5bf82ce5..74e46fe5abb5 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -37,12 +37,10 @@ function DistanceEReceipt({transaction}) { - - - + ({ color: theme.textColorfulBackground, }, - eReceiptBackgroundContainer: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'center', - alignItems: 'center', - }, - eReceiptBackground: { ...sizing.w100, + borderRadius: 20, position: 'absolute', top: 0, left: 0, - right: 0, - bottom: 0, - minHeight: 540, }, eReceiptPanel: { From 87c8457e0be08b61c5dc6c14ff433dd5b794daac Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 26 Sep 2023 11:00:44 +0800 Subject: [PATCH 048/205] Remove receipt image border for prettier loading --- src/components/DistanceEReceipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 74e46fe5abb5..e8b4e0ecfc44 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -41,7 +41,7 @@ function DistanceEReceipt({transaction}) { style={styles.eReceiptBackground} pointerEvents="none" /> - + Date: Wed, 27 Sep 2023 11:50:22 +0800 Subject: [PATCH 049/205] Fix distance request next button while offline --- src/components/DistanceRequest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DistanceRequest.js b/src/components/DistanceRequest.js index 5e9b73f2eb3a..4c732e025fac 100644 --- a/src/components/DistanceRequest.js +++ b/src/components/DistanceRequest.js @@ -297,9 +297,9 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken, success style={[styles.w100, styles.mb4, styles.ph4, styles.flexShrink0]} onPress={navigateToNextPage} - isDisabled={_.size(validatedWaypoints) < 2 || hasRouteError || isLoadingRoute} + isDisabled={_.size(validatedWaypoints) < 2 || (!isOffline && (hasRouteError || isLoadingRoute))} text={translate('common.next')} - isLoading={isLoadingRoute || shouldFetchRoute} + isLoading={!isOffline && (isLoadingRoute || shouldFetchRoute)} /> ); From 183e3fed5d63c5245e7aa78ea96b972a5311b02d Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 27 Sep 2023 14:15:00 +0700 Subject: [PATCH 050/205] Trim the value when going back on connect bank account --- src/components/AddressSearch/index.js | 8 ++++ src/pages/ReimbursementAccount/AddressForm.js | 2 + src/pages/ReimbursementAccount/CompanyStep.js | 38 ++++++++++++++----- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 1b4200572664..20cb31be623f 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -257,6 +257,10 @@ function AddressSearch(props) { props.onInputChange(values); } + if (_.isFunction(props.onValueChange)) { + props.onValueChange(values); + } + props.onPress(values); }; @@ -337,6 +341,10 @@ function AddressSearch(props) { props.onInputChange({street: text}); } + if (_.isFunction(props.onValueChange)) { + props.onValueChange({street: text}); + } + // If the text is empty, we set displayListViewBorder to false to prevent UI flickering if (_.isEmpty(text)) { setDisplayListViewBorder(false); diff --git a/src/pages/ReimbursementAccount/AddressForm.js b/src/pages/ReimbursementAccount/AddressForm.js index d8fbc0290136..47f93dd8f9cb 100644 --- a/src/pages/ReimbursementAccount/AddressForm.js +++ b/src/pages/ReimbursementAccount/AddressForm.js @@ -103,6 +103,7 @@ function AddressForm(props) { value={props.values.street} defaultValue={props.defaultValues.street} onInputChange={props.onFieldChange} + onValueChange={props.onFieldChange} errorText={props.errors.street ? props.translate('bankAccount.error.addressStreet') : ''} hint={props.translate('common.noPO')} renamedInputKeys={props.inputKeys} @@ -129,6 +130,7 @@ function AddressForm(props) { value={props.values.state} defaultValue={props.defaultValues.state || ''} onInputChange={(value) => props.onFieldChange({state: value})} + onValueChange={(value) => props.onFieldChange({state: value})} errorText={props.errors.state ? props.translate('bankAccount.error.addressState') : ''} /> diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 0ca9b1b7ea92..d3365afe8a23 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import lodashGet from 'lodash/get'; -import React, {useMemo} from 'react'; +import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import Str from 'expensify-common/lib/str'; import {withOnyx} from 'react-native-onyx'; @@ -54,6 +54,23 @@ const defaultProps = { }; function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaultStateForField, onBackButtonPress, translate, session, user, policyID}) { + const defaultWebsite = useMemo(() => (lodashGet(user, 'isFromPublicDomain', false) ? 'https://' : `https://www.${Str.extractEmailDomain(session.email, '')}`), [user, session]); + const [companyInfomation, setCompanyInfomation] = useState({ + street: getDefaultStateForField('addressStreet'), + city: getDefaultStateForField('addressCity'), + state: getDefaultStateForField('addressState'), + zipCode: getDefaultStateForField('addressZipCode'), + companyPhone: getDefaultStateForField('companyPhone'), + website: getDefaultStateForField('website', defaultWebsite), + }); + + const onFieldChange = (field) => { + setCompanyInfomation((prevCompanyInformation) => ({ + ...prevCompanyInformation, + ...field, + })); + }; + /** * @param {Array} fieldNames * @@ -64,8 +81,6 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul ..._.pick(reimbursementAccountDraft, ...fieldNames), }); - const defaultWebsite = useMemo(() => (lodashGet(user, 'isFromPublicDomain', false) ? 'https://' : `https://www.${Str.extractEmailDomain(session.email, '')}`), [user, session]); - /** * @param {Object} values - form input values passed by the Form component * @returns {Object} - Object containing the errors for each inputID, e.g. {inputID1: error1, inputID2: error2} @@ -173,11 +188,11 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul /> @@ -196,7 +212,8 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul containerStyles={[styles.mt4]} keyboardType={CONST.KEYBOARD_TYPE.PHONE_PAD} placeholder={translate('common.phoneNumberPlaceholder')} - defaultValue={getDefaultStateForField('companyPhone')} + value={companyInfomation.companyPhone} + onValueChange={(value) => onFieldChange({companyPhone: value})} shouldSaveDraft /> onFieldChange({website: value})} shouldSaveDraft hint={translate('common.websiteExample')} keyboardType={CONST.KEYBOARD_TYPE.URL} From 99477df8ca0ff2d52093bbdf5e96459b548988f1 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 27 Sep 2023 16:49:41 +0700 Subject: [PATCH 051/205] use draft if the default value is empty --- src/pages/ReimbursementAccount/CompanyStep.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index d3365afe8a23..26cc2b49cefc 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -56,12 +56,12 @@ const defaultProps = { function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaultStateForField, onBackButtonPress, translate, session, user, policyID}) { const defaultWebsite = useMemo(() => (lodashGet(user, 'isFromPublicDomain', false) ? 'https://' : `https://www.${Str.extractEmailDomain(session.email, '')}`), [user, session]); const [companyInfomation, setCompanyInfomation] = useState({ - street: getDefaultStateForField('addressStreet'), - city: getDefaultStateForField('addressCity'), - state: getDefaultStateForField('addressState'), - zipCode: getDefaultStateForField('addressZipCode'), - companyPhone: getDefaultStateForField('companyPhone'), - website: getDefaultStateForField('website', defaultWebsite), + street: getDefaultStateForField('addressStreet') || reimbursementAccountDraft.addressStreet, + city: getDefaultStateForField('addressCity') || reimbursementAccountDraft.addressCity, + state: getDefaultStateForField('addressState') || reimbursementAccountDraft.addressState, + zipCode: getDefaultStateForField('addressZipCode') || reimbursementAccountDraft.addressZipCode, + companyPhone: getDefaultStateForField('companyPhone') || reimbursementAccountDraft.companyPhone, + website: getDefaultStateForField('website', defaultWebsite) || reimbursementAccountDraft.website, }); const onFieldChange = (field) => { From b9e14d743570ac924ebe4487b4b97ce0389f0d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Wed, 27 Sep 2023 11:52:08 +0100 Subject: [PATCH 052/205] Minor fix in theme files --- src/styles/themes/default.ts | 3 ++- src/styles/themes/light.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index 1fec07f4caab..4060f942198b 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -1,5 +1,6 @@ import SCREENS from '../../SCREENS'; import colors from '../colors'; +import type {ThemeBase} from './types'; const darkTheme = { // Figma keys @@ -91,6 +92,6 @@ const darkTheme = { [SCREENS.SETTINGS.STATUS]: colors.green700, [SCREENS.SETTINGS.ROOT]: colors.darkHighlightBackground, }, -}; +} satisfies ThemeBase; export default darkTheme; diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index f40da44d75c4..76f5435b9ef2 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -1,5 +1,6 @@ import SCREENS from '../../SCREENS'; import colors from '../colors'; +import type {ThemeDefault} from './types'; const lightTheme = { // Figma keys @@ -91,6 +92,6 @@ const lightTheme = { [SCREENS.SETTINGS.STATUS]: colors.green700, [SCREENS.SETTINGS.ROOT]: colors.lightHighlightBackground, }, -}; +} satisfies ThemeDefault; export default lightTheme; From 04e7887b3a51e72c3a16e010d7d488368e101810 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 27 Sep 2023 19:12:52 +0700 Subject: [PATCH 053/205] fix typo --- src/pages/ReimbursementAccount/CompanyStep.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 26cc2b49cefc..4e3b0f0b6f92 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -55,7 +55,7 @@ const defaultProps = { function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaultStateForField, onBackButtonPress, translate, session, user, policyID}) { const defaultWebsite = useMemo(() => (lodashGet(user, 'isFromPublicDomain', false) ? 'https://' : `https://www.${Str.extractEmailDomain(session.email, '')}`), [user, session]); - const [companyInfomation, setCompanyInfomation] = useState({ + const [companyInformation, setCompanyInformation] = useState({ street: getDefaultStateForField('addressStreet') || reimbursementAccountDraft.addressStreet, city: getDefaultStateForField('addressCity') || reimbursementAccountDraft.addressCity, state: getDefaultStateForField('addressState') || reimbursementAccountDraft.addressState, @@ -65,7 +65,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul }); const onFieldChange = (field) => { - setCompanyInfomation((prevCompanyInformation) => ({ + setCompanyInformation((prevCompanyInformation) => ({ ...prevCompanyInformation, ...field, })); @@ -189,10 +189,10 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul onFieldChange({companyPhone: value})} shouldSaveDraft /> @@ -222,7 +222,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul accessibilityLabel={translate('companyStep.companyWebsite')} accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} containerStyles={[styles.mt4]} - value={companyInfomation.website} + value={companyInformation.website} onValueChange={(value) => onFieldChange({website: value})} shouldSaveDraft hint={translate('common.websiteExample')} From 161abe512cc809bae6ac4fe1d0280b1409f10e7d Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 27 Sep 2023 21:10:26 +0700 Subject: [PATCH 054/205] save value to draft after updating data successfully --- src/components/AddressSearch/index.js | 8 ---- src/libs/actions/BankAccounts.js | 1 + .../actions/ReimbursementAccount/index.js | 1 + src/pages/ReimbursementAccount/AddressForm.js | 2 - src/pages/ReimbursementAccount/CompanyStep.js | 39 ++++++------------- .../ReimbursementAccountPage.js | 39 +++++++++++++++++++ 6 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 20cb31be623f..1b4200572664 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -257,10 +257,6 @@ function AddressSearch(props) { props.onInputChange(values); } - if (_.isFunction(props.onValueChange)) { - props.onValueChange(values); - } - props.onPress(values); }; @@ -341,10 +337,6 @@ function AddressSearch(props) { props.onInputChange({street: text}); } - if (_.isFunction(props.onValueChange)) { - props.onValueChange({street: text}); - } - // If the text is empty, we set displayListViewBorder to false to prevent UI flickering if (_.isEmpty(text)) { setDisplayListViewBorder(false); diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index b1cb09a8a5e2..3ac414a1c39a 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -79,6 +79,7 @@ function getVBBADataForOnyx() { value: { isLoading: false, errors: null, + shouldUpdateDataToDraft: true, }, }, ], diff --git a/src/libs/actions/ReimbursementAccount/index.js b/src/libs/actions/ReimbursementAccount/index.js index 49ff30e7be8e..e720053bfcd5 100644 --- a/src/libs/actions/ReimbursementAccount/index.js +++ b/src/libs/actions/ReimbursementAccount/index.js @@ -31,6 +31,7 @@ function setWorkspaceIDForReimbursementAccount(workspaceID) { */ function updateReimbursementAccountDraft(bankAccountData) { Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT, bankAccountData); + Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {shouldUpdateDataToDraft: false}); } /** diff --git a/src/pages/ReimbursementAccount/AddressForm.js b/src/pages/ReimbursementAccount/AddressForm.js index 47f93dd8f9cb..d8fbc0290136 100644 --- a/src/pages/ReimbursementAccount/AddressForm.js +++ b/src/pages/ReimbursementAccount/AddressForm.js @@ -103,7 +103,6 @@ function AddressForm(props) { value={props.values.street} defaultValue={props.defaultValues.street} onInputChange={props.onFieldChange} - onValueChange={props.onFieldChange} errorText={props.errors.street ? props.translate('bankAccount.error.addressStreet') : ''} hint={props.translate('common.noPO')} renamedInputKeys={props.inputKeys} @@ -130,7 +129,6 @@ function AddressForm(props) { value={props.values.state} defaultValue={props.defaultValues.state || ''} onInputChange={(value) => props.onFieldChange({state: value})} - onValueChange={(value) => props.onFieldChange({state: value})} errorText={props.errors.state ? props.translate('bankAccount.error.addressState') : ''} /> diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 4e3b0f0b6f92..d4fdd0b1492e 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import lodashGet from 'lodash/get'; -import React, {useMemo, useState} from 'react'; +import React, {useMemo} from 'react'; import {View} from 'react-native'; import Str from 'expensify-common/lib/str'; import {withOnyx} from 'react-native-onyx'; @@ -54,23 +54,6 @@ const defaultProps = { }; function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaultStateForField, onBackButtonPress, translate, session, user, policyID}) { - const defaultWebsite = useMemo(() => (lodashGet(user, 'isFromPublicDomain', false) ? 'https://' : `https://www.${Str.extractEmailDomain(session.email, '')}`), [user, session]); - const [companyInformation, setCompanyInformation] = useState({ - street: getDefaultStateForField('addressStreet') || reimbursementAccountDraft.addressStreet, - city: getDefaultStateForField('addressCity') || reimbursementAccountDraft.addressCity, - state: getDefaultStateForField('addressState') || reimbursementAccountDraft.addressState, - zipCode: getDefaultStateForField('addressZipCode') || reimbursementAccountDraft.addressZipCode, - companyPhone: getDefaultStateForField('companyPhone') || reimbursementAccountDraft.companyPhone, - website: getDefaultStateForField('website', defaultWebsite) || reimbursementAccountDraft.website, - }); - - const onFieldChange = (field) => { - setCompanyInformation((prevCompanyInformation) => ({ - ...prevCompanyInformation, - ...field, - })); - }; - /** * @param {Array} fieldNames * @@ -81,6 +64,8 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul ..._.pick(reimbursementAccountDraft, ...fieldNames), }); + const defaultWebsite = useMemo(() => (lodashGet(user, 'isFromPublicDomain', false) ? 'https://' : `https://www.${Str.extractEmailDomain(session.email, '')}`), [user, session]); + /** * @param {Object} values - form input values passed by the Form component * @returns {Object} - Object containing the errors for each inputID, e.g. {inputID1: error1, inputID2: error2} @@ -172,6 +157,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul onSubmit={submit} scrollContextEnabled submitButtonText={translate('common.saveAndContinue')} + shouldPriorityDefaultValue style={[styles.mh5, styles.flexGrow1]} > {translate('companyStep.subtitle')} @@ -188,11 +174,11 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul /> @@ -212,8 +197,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul containerStyles={[styles.mt4]} keyboardType={CONST.KEYBOARD_TYPE.PHONE_PAD} placeholder={translate('common.phoneNumberPlaceholder')} - value={companyInformation.companyPhone} - onValueChange={(value) => onFieldChange({companyPhone: value})} + defaultValuevalue={getDefaultStateForField('companyPhone')} shouldSaveDraft /> onFieldChange({website: value})} + defaultValue={getDefaultStateForField('website', defaultWebsite)} shouldSaveDraft hint={translate('common.websiteExample')} keyboardType={CONST.KEYBOARD_TYPE.URL} diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index afcd84ffa660..5c97a4f838fc 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -158,6 +158,9 @@ class ReimbursementAccountPage extends React.Component { } const currentStepRouteParam = this.getStepToOpenFromRouteParams(); + if (this.props.reimbursementAccount.shouldUpdateDataToDraft) { + BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(currentStepRouteParam))); + } if (currentStepRouteParam === currentStep) { // The route is showing the correct step, no need to update the route param or clear errors. return; @@ -177,6 +180,42 @@ class ReimbursementAccountPage extends React.Component { Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(this.getRouteForCurrentStep(currentStep), policyId, backTo)); } + getFieldsOfCurrentStep(currentStep) { + switch (currentStep) { + case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: + return ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']; + case CONST.BANK_ACCOUNT.STEP.COMPANY: + return [ + 'companyName', + 'addressStreet', + 'addressZipCode', + 'addressCity', + 'addressState', + 'companyPhone', + 'website', + 'companyTaxID', + 'incorporationType', + 'incorporationDate', + 'incorporationState', + ]; + case CONST.BANK_ACCOUNT.STEP.REQUESTOR: + return ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode']; + default: + return []; + } + } + + /** + * @param {Array} fieldNames + * + * @returns {*} + */ + getBankAccountFields(fieldNames) { + return { + ..._.pick(lodashGet(this.props.reimbursementAccount, 'achData'), ...fieldNames), + }; + } + /* * Calculates the state used to show the "Continue with setup" view. If a bank account setup is already in progress and * no specific further step was passed in the url we'll show the workspace bank account reset modal if the user wishes to start over From 15ef37fc86555ce31bc63d0d09f9db41ff66a570 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 27 Sep 2023 21:11:28 +0700 Subject: [PATCH 055/205] fix merge main --- src/pages/ReimbursementAccount/CompanyStep.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index d4fdd0b1492e..0ca9b1b7ea92 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -157,7 +157,6 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul onSubmit={submit} scrollContextEnabled submitButtonText={translate('common.saveAndContinue')} - shouldPriorityDefaultValue style={[styles.mh5, styles.flexGrow1]} > {translate('companyStep.subtitle')} @@ -197,7 +196,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul containerStyles={[styles.mt4]} keyboardType={CONST.KEYBOARD_TYPE.PHONE_PAD} placeholder={translate('common.phoneNumberPlaceholder')} - defaultValuevalue={getDefaultStateForField('companyPhone')} + defaultValue={getDefaultStateForField('companyPhone')} shouldSaveDraft /> Date: Wed, 27 Sep 2023 21:17:10 +0700 Subject: [PATCH 056/205] fix jest --- src/pages/ReimbursementAccount/CompanyStep.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 0ca9b1b7ea92..409af4302042 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -187,6 +187,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul }} shouldSaveDraft streetTranslationKey="common.companyAddress" + /> Date: Wed, 27 Sep 2023 21:17:47 +0700 Subject: [PATCH 057/205] fix lint --- src/pages/ReimbursementAccount/CompanyStep.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 409af4302042..0ca9b1b7ea92 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -187,7 +187,6 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul }} shouldSaveDraft streetTranslationKey="common.companyAddress" - /> Date: Wed, 27 Sep 2023 22:02:18 +0700 Subject: [PATCH 058/205] add comment --- src/pages/ReimbursementAccount/ReimbursementAccountPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 5c97a4f838fc..2ddef05d2378 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -158,6 +158,7 @@ class ReimbursementAccountPage extends React.Component { } const currentStepRouteParam = this.getStepToOpenFromRouteParams(); + // Update the data that is returned from back-end to draft value if (this.props.reimbursementAccount.shouldUpdateDataToDraft) { BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(currentStepRouteParam))); } From 3c4be813712699352cf45d2d1178267a0e8fda9e Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 27 Sep 2023 23:27:25 +0700 Subject: [PATCH 059/205] create a const for fields of the step --- src/CONST.ts | 7 ++++++ .../ReimbursementAccountPage.js | 23 +------------------ 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index e487514f150e..c4fd7ca5716d 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -161,6 +161,13 @@ const CONST = { MISSING_INCORPORATION_STATE: '402 Missing incorporationState in additionalData', MISSING_INCORPORATION_TYPE: '402 Missing incorporationType in additionalData', }, + FIELDS: { + BankAccountStep: ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings'], + CompanyStep: ['companyName', 'addressStreet', 'addressZipCode', 'addressCity', 'addressState', 'companyPhone', 'website','companyTaxID', 'incorporationType', 'incorporationDate', 'incorporationState'], + RequestorStep: ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode'], + ACHContractStep: [], + ENABLE: [] + }, STEP: { // In the order they appear in the VBA flow BANK_ACCOUNT: 'BankAccountStep', diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 5c97a4f838fc..e229353b43d2 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -181,28 +181,7 @@ class ReimbursementAccountPage extends React.Component { } getFieldsOfCurrentStep(currentStep) { - switch (currentStep) { - case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: - return ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']; - case CONST.BANK_ACCOUNT.STEP.COMPANY: - return [ - 'companyName', - 'addressStreet', - 'addressZipCode', - 'addressCity', - 'addressState', - 'companyPhone', - 'website', - 'companyTaxID', - 'incorporationType', - 'incorporationDate', - 'incorporationState', - ]; - case CONST.BANK_ACCOUNT.STEP.REQUESTOR: - return ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode']; - default: - return []; - } + return CONST.BANK_ACCOUNT.FIELDS[currentStep] || []; } /** From 936289ca05e0ff663161c49f6082968e3283038c Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 27 Sep 2023 23:31:53 +0700 Subject: [PATCH 060/205] fix lint --- src/CONST.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index c4fd7ca5716d..19582e702cb0 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -163,10 +163,22 @@ const CONST = { }, FIELDS: { BankAccountStep: ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings'], - CompanyStep: ['companyName', 'addressStreet', 'addressZipCode', 'addressCity', 'addressState', 'companyPhone', 'website','companyTaxID', 'incorporationType', 'incorporationDate', 'incorporationState'], + CompanyStep: [ + 'companyName', + 'addressStreet', + 'addressZipCode', + 'addressCity', + 'addressState', + 'companyPhone', + 'website', + 'companyTaxID', + 'incorporationType', + 'incorporationDate', + 'incorporationState', + ], RequestorStep: ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode'], ACHContractStep: [], - ENABLE: [] + ENABLE: [], }, STEP: { // In the order they appear in the VBA flow From 25c23663f165ac11f7610a09f94158dbf76b90af Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 27 Sep 2023 23:35:43 +0700 Subject: [PATCH 061/205] reuse the const --- src/pages/ReimbursementAccount/CompanyStep.js | 16 ++-------------- src/pages/ReimbursementAccount/RequestorStep.js | 2 +- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 0ca9b1b7ea92..70042f903e76 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -71,19 +71,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul * @returns {Object} - Object containing the errors for each inputID, e.g. {inputID1: error1, inputID2: error2} */ const validate = (values) => { - const requiredFields = [ - 'companyName', - 'addressStreet', - 'addressZipCode', - 'addressCity', - 'addressState', - 'companyPhone', - 'website', - 'companyTaxID', - 'incorporationType', - 'incorporationDate', - 'incorporationState', - ]; + const requiredFields = CONST.BANK_ACCOUNT.FIELDS.CompanyStep; const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { @@ -124,7 +112,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul bankAccountID: lodashGet(reimbursementAccount, 'achData.bankAccountID') || 0, // Fields from BankAccount step - ...getBankAccountFields(['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']), + ...getBankAccountFields(CONST.BANK_ACCOUNT.FIELDS.BankAccountStep), // Fields from Company step ...values, diff --git a/src/pages/ReimbursementAccount/RequestorStep.js b/src/pages/ReimbursementAccount/RequestorStep.js index 53ca279c2cb2..2983062a8160 100644 --- a/src/pages/ReimbursementAccount/RequestorStep.js +++ b/src/pages/ReimbursementAccount/RequestorStep.js @@ -29,7 +29,7 @@ const propTypes = { shouldShowOnfido: PropTypes.bool.isRequired, }; -const REQUIRED_FIELDS = ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode']; +const REQUIRED_FIELDS = CONST.BANK_ACCOUNT.FIELDS.RequestorStep; const INPUT_KEYS = { firstName: 'firstName', lastName: 'lastName', From 32d4091197b6448fef44ec7cd7317f4f02c666f7 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 28 Sep 2023 17:23:03 +0800 Subject: [PATCH 062/205] Fix isDistanceRequest for optimistic transactions --- src/libs/actions/IOU.js | 9 ++++++--- src/pages/iou/DistanceRequestPage.js | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index b346900b37dc..555cfd79a2f4 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -2102,9 +2102,12 @@ function setMoneyRequestReceipt(receiptPath, receiptSource) { Onyx.merge(ONYXKEYS.IOU, {receiptPath, receiptSource, merchant: ''}); } -function createEmptyTransaction() { +function setUpDistanceTransaction() { const transactionID = NumberUtils.rand64(); - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {transactionID}); + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { + transactionID, + comment: {type: CONST.TRANSACTION.TYPE.CUSTOM_UNIT, customUnit: {name: CONST.CUSTOM_UNITS.NAME_DISTANCE}}, + }); Onyx.merge(ONYXKEYS.IOU, {transactionID}); } @@ -2180,7 +2183,7 @@ export { setMoneyRequestBillable, setMoneyRequestParticipants, setMoneyRequestReceipt, - createEmptyTransaction, + setUpDistanceTransaction, navigateToNextPage, replaceReceipt, }; diff --git a/src/pages/iou/DistanceRequestPage.js b/src/pages/iou/DistanceRequestPage.js index 39b068975c77..2d35b500bd51 100644 --- a/src/pages/iou/DistanceRequestPage.js +++ b/src/pages/iou/DistanceRequestPage.js @@ -49,7 +49,7 @@ function DistanceRequestPage({iou, report, route}) { if (iou.transactionID) { return; } - IOU.createEmptyTransaction(); + IOU.setUpDistanceTransaction(); }, [iou.transactionID]); return ( From 54a25c4707648f1d371d67799b0fde1064de23ec Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 28 Sep 2023 17:42:57 +0800 Subject: [PATCH 063/205] Use placeholders for offline distance eReceipts --- src/components/DistanceEReceipt.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index e8b4e0ecfc44..dc059cc1015c 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -16,6 +16,8 @@ import Icon from './Icon'; import themeColors from '../styles/themes/default'; import * as Expensicons from './Icon/Expensicons'; import EReceiptBackground from '../../assets/images/eReceipt_background.svg'; +import useNetwork from '../hooks/useNetwork'; +import PendingMapView from './MapView/PendingMapView'; const propTypes = { /** The transaction for the eReceipt */ @@ -28,9 +30,11 @@ const defaultProps = { function DistanceEReceipt({transaction}) { const {translate} = useLocalize(); + const {isOffline} = useNetwork(); const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); - const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); + const hasRoute = TransactionUtils.hasRoute(transaction); + const formattedTransactionAmount = hasRoute ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : translate('common.tbd'); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( @@ -42,12 +46,16 @@ function DistanceEReceipt({transaction}) { pointerEvents="none" /> - + {isOffline ? ( + + ) : ( + + )} {formattedTransactionAmount} From 5c5f717ce230b1653d7bc32e7bb13b20b4b1ec5b Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 28 Sep 2023 18:16:31 +0800 Subject: [PATCH 064/205] Use withOnyx for transaction vs deprecated utils --- .../Attachments/AttachmentView/index.js | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 1b05861b581e..a08f72b9c604 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -3,7 +3,6 @@ import {View, ActivityIndicator} from 'react-native'; import _ from 'underscore'; import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; -import {useRoute} from '@react-navigation/native'; import styles from '../../../styles/styles'; import Icon from '../../Icon'; import * as Expensicons from '../../Icon/Expensicons'; @@ -18,10 +17,8 @@ import AttachmentViewPdf from './AttachmentViewPdf'; import addEncryptedAuthTokenToURL from '../../../libs/addEncryptedAuthTokenToURL'; import * as StyleUtils from '../../../styles/StyleUtils'; import {attachmentViewPropTypes, attachmentViewDefaultProps} from './propTypes'; -import * as ReportUtils from '../../../libs/ReportUtils'; import * as TransactionUtils from '../../../libs/TransactionUtils'; import DistanceEReceipt from '../../DistanceEReceipt'; -import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; import useNetwork from '../../../hooks/useNetwork'; const propTypes = { @@ -69,10 +66,9 @@ function AttachmentView({ isFocused, isWorkspaceAvatar, fallbackSource, + transaction, }) { const [loadComplete, setLoadComplete] = useState(false); - const currentRoute = useRoute(); - const [imageError, setImageError] = useState(false); useNetwork({onReconnect: () => setImageError(false)}); @@ -119,15 +115,7 @@ function AttachmentView({ ); } - const reportID = _.get(currentRoute, ['params', 'reportID']); - const report = ReportUtils.getReport(reportID); - - // Get the money request transaction - const parentReportAction = ReportActionsUtils.getParentReportAction(report); - const transactionID = _.get(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); - const transaction = TransactionUtils.getTransaction(transactionID); - const shouldShowEReceipt = TransactionUtils.isDistanceRequest(transaction); - if (shouldShowEReceipt) { + if (TransactionUtils.isDistanceRequest(transaction)) { return ; } @@ -185,4 +173,20 @@ AttachmentView.propTypes = propTypes; AttachmentView.defaultProps = defaultProps; AttachmentView.displayName = 'AttachmentView'; -export default compose(memo, withLocalize)(AttachmentView); +export default compose( + memo, + withLocalize, + withOnyx({ + parentReport: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, + }, + parentReportAction: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${(report.parentReportID, report.parentReportActionID)}`, + selector: (reportActions, props) => props && props.parentReport && reportActions && reportActions[props.parentReport.parentReportActionID], + canEvict: false, + }, + transaction: { + key: ({parentReportAction}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', 0)}`, + }, + }), +)(AttachmentView); From 6caa7962e00d548ecb542d1737c21c5485801a08 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 28 Sep 2023 17:17:56 +0700 Subject: [PATCH 065/205] revert const --- src/CONST.ts | 19 --------------- src/pages/ReimbursementAccount/CompanyStep.js | 16 +++++++++++-- .../ReimbursementAccountPage.js | 23 ++++++++++++++++++- .../ReimbursementAccount/RequestorStep.js | 2 +- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 19582e702cb0..e487514f150e 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -161,25 +161,6 @@ const CONST = { MISSING_INCORPORATION_STATE: '402 Missing incorporationState in additionalData', MISSING_INCORPORATION_TYPE: '402 Missing incorporationType in additionalData', }, - FIELDS: { - BankAccountStep: ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings'], - CompanyStep: [ - 'companyName', - 'addressStreet', - 'addressZipCode', - 'addressCity', - 'addressState', - 'companyPhone', - 'website', - 'companyTaxID', - 'incorporationType', - 'incorporationDate', - 'incorporationState', - ], - RequestorStep: ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode'], - ACHContractStep: [], - ENABLE: [], - }, STEP: { // In the order they appear in the VBA flow BANK_ACCOUNT: 'BankAccountStep', diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 70042f903e76..0ca9b1b7ea92 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -71,7 +71,19 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul * @returns {Object} - Object containing the errors for each inputID, e.g. {inputID1: error1, inputID2: error2} */ const validate = (values) => { - const requiredFields = CONST.BANK_ACCOUNT.FIELDS.CompanyStep; + const requiredFields = [ + 'companyName', + 'addressStreet', + 'addressZipCode', + 'addressCity', + 'addressState', + 'companyPhone', + 'website', + 'companyTaxID', + 'incorporationType', + 'incorporationDate', + 'incorporationState', + ]; const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { @@ -112,7 +124,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul bankAccountID: lodashGet(reimbursementAccount, 'achData.bankAccountID') || 0, // Fields from BankAccount step - ...getBankAccountFields(CONST.BANK_ACCOUNT.FIELDS.BankAccountStep), + ...getBankAccountFields(['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']), // Fields from Company step ...values, diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 5c61cbe1fa54..2ddef05d2378 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -182,7 +182,28 @@ class ReimbursementAccountPage extends React.Component { } getFieldsOfCurrentStep(currentStep) { - return CONST.BANK_ACCOUNT.FIELDS[currentStep] || []; + switch (currentStep) { + case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: + return ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']; + case CONST.BANK_ACCOUNT.STEP.COMPANY: + return [ + 'companyName', + 'addressStreet', + 'addressZipCode', + 'addressCity', + 'addressState', + 'companyPhone', + 'website', + 'companyTaxID', + 'incorporationType', + 'incorporationDate', + 'incorporationState', + ]; + case CONST.BANK_ACCOUNT.STEP.REQUESTOR: + return ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode']; + default: + return []; + } } /** diff --git a/src/pages/ReimbursementAccount/RequestorStep.js b/src/pages/ReimbursementAccount/RequestorStep.js index 2983062a8160..53ca279c2cb2 100644 --- a/src/pages/ReimbursementAccount/RequestorStep.js +++ b/src/pages/ReimbursementAccount/RequestorStep.js @@ -29,7 +29,7 @@ const propTypes = { shouldShowOnfido: PropTypes.bool.isRequired, }; -const REQUIRED_FIELDS = CONST.BANK_ACCOUNT.FIELDS.RequestorStep; +const REQUIRED_FIELDS = ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode']; const INPUT_KEYS = { firstName: 'firstName', lastName: 'lastName', From db6e4dad0ed580451ca409cb5e51f59bc2f5872f Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 28 Sep 2023 18:23:28 +0800 Subject: [PATCH 066/205] Update comment --- src/components/DistanceEReceipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index dc059cc1015c..4a92079bcce5 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -20,7 +20,7 @@ import useNetwork from '../hooks/useNetwork'; import PendingMapView from './MapView/PendingMapView'; const propTypes = { - /** The transaction for the eReceipt */ + /** The transaction for the distance request */ transaction: transactionPropTypes, }; From 270eddf232361a0bcb1cdb06acf88c93dd070b18 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 28 Sep 2023 17:23:34 +0700 Subject: [PATCH 067/205] fix jest --- src/pages/ReimbursementAccount/CompanyStep.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 0ca9b1b7ea92..16ff5cbd48e1 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -84,6 +84,7 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul 'incorporationDate', 'incorporationState', ]; + const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { From 12f5f05c66a80f9402de07c34ff72ffbfbd20cac Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 28 Sep 2023 17:23:56 +0700 Subject: [PATCH 068/205] fix lint --- src/pages/ReimbursementAccount/CompanyStep.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 16ff5cbd48e1..0ca9b1b7ea92 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -84,7 +84,6 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul 'incorporationDate', 'incorporationState', ]; - const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { From a66dbd93af2d0cbcb9c7960619e0a1986306c872 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Thu, 28 Sep 2023 20:36:21 +0800 Subject: [PATCH 069/205] Update translations --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- src/libs/actions/App.js | 3 ++- src/pages/NewChatPage.js | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 32f1315c44c0..440d96fc0160 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1562,7 +1562,7 @@ export default { screenShareRequest: 'Expensify is inviting you to a screen share', }, search: { - offline: 'You appear to be offline; search results are limited.', + resultsAreLimited: 'Search results are limited.', }, genericErrorPage: { title: 'Uh-oh, something went wrong!', diff --git a/src/languages/es.ts b/src/languages/es.ts index acf7f16d7b7c..6286a4453d53 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1584,7 +1584,7 @@ export default { screenShareRequest: 'Expensify te está invitando a compartir la pantalla', }, search: { - offline: 'You appear to be offline; search results are limited.', + resultsAreLimited: 'Los resultados de búsqueda están limitados.', }, genericErrorPage: { title: '¡Uh-oh, algo salió mal!', diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index b8be35aa1919..f2017a982fb0 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -204,7 +204,8 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false) { */ function openApp() { getPolicyParamsForOpenOrReconnect().then((policyParams) => { - API.read('OpenApp', policyParams, getOnyxDataForOpenOrReconnect(true)); + const params = {enablePriorityModeFilter: true, ...policyParams}; + API.read('OpenApp', params, getOnyxDataForOpenOrReconnect(true)); }); } diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index aa6fa8dfbbc3..30cc27845144 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -208,7 +208,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i shouldShowOptions={isOptionsDataReady} shouldShowConfirmButton confirmButtonText={selectedOptions.length > 1 ? translate('newChatPage.createGroup') : translate('newChatPage.createChat')} - textInputAlert={isOffline ? translate('search.offline') : ''} + textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''} onConfirmSelection={createGroup} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} From 7877d5edbbcf62154e1cd72447e7992eb6b08b6f Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 29 Sep 2023 09:53:26 +0800 Subject: [PATCH 070/205] Fix and simplify transaction access --- .../AttachmentCarousel/CarouselItem.js | 1 + .../Attachments/AttachmentView/index.js | 15 ++++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.js index 3aeef8482e2d..6cecfb03fd83 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.js @@ -97,6 +97,7 @@ function CarouselItem({item, isFocused, onPress}) { isFocused={isFocused} onPress={onPress} isUsedInCarousel + transactionID={item.transactionID} /> diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index a08f72b9c604..b8699764173c 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -20,6 +20,8 @@ import {attachmentViewPropTypes, attachmentViewDefaultProps} from './propTypes'; import * as TransactionUtils from '../../../libs/TransactionUtils'; import DistanceEReceipt from '../../DistanceEReceipt'; import useNetwork from '../../../hooks/useNetwork'; +import {withOnyx} from 'react-native-onyx'; +import ONYXKEYS from '../../../ONYXKEYS'; const propTypes = { ...attachmentViewPropTypes, @@ -40,6 +42,8 @@ const propTypes = { /** Denotes whether it is a workspace avatar or not */ isWorkspaceAvatar: PropTypes.bool, + + transactionID: PropTypes.number, }; const defaultProps = { @@ -49,6 +53,7 @@ const defaultProps = { onToggleKeyboard: () => {}, containerStyles: [], isWorkspaceAvatar: false, + transactionID: 0, }; function AttachmentView({ @@ -177,16 +182,8 @@ export default compose( memo, withLocalize, withOnyx({ - parentReport: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, - }, - parentReportAction: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${(report.parentReportID, report.parentReportActionID)}`, - selector: (reportActions, props) => props && props.parentReport && reportActions && reportActions[props.parentReport.parentReportActionID], - canEvict: false, - }, transaction: { - key: ({parentReportAction}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', 0)}`, + key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, }, }), )(AttachmentView); From b2c3f9e7705bfdbb6fc77f1811f58f835e07ff2f Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 29 Sep 2023 10:00:48 +0800 Subject: [PATCH 071/205] Fix prop type --- src/components/Attachments/AttachmentView/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index b8699764173c..f6bc0160e1af 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -43,7 +43,7 @@ const propTypes = { /** Denotes whether it is a workspace avatar or not */ isWorkspaceAvatar: PropTypes.bool, - transactionID: PropTypes.number, + transactionID: PropTypes.string, }; const defaultProps = { From 6963757f5975277919ea36b3ac30a10346992062 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 29 Sep 2023 10:28:40 +0800 Subject: [PATCH 072/205] Fix online and offline transaction amount --- src/components/DistanceEReceipt.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 4a92079bcce5..abc88e1306a2 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -33,8 +33,7 @@ function DistanceEReceipt({transaction}) { const {isOffline} = useNetwork(); const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); - const hasRoute = TransactionUtils.hasRoute(transaction); - const formattedTransactionAmount = hasRoute ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : translate('common.tbd'); + const formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : translate('common.tbd'); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); return ( From 1d3a8b9d10165c3ef93d391a04ff15efc46dbfdf Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 29 Sep 2023 10:33:39 +0800 Subject: [PATCH 073/205] Load thumbnail only when image source is ready --- src/components/DistanceEReceipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index abc88e1306a2..6e91edcca5e4 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -45,7 +45,7 @@ function DistanceEReceipt({transaction}) { pointerEvents="none" /> - {isOffline ? ( + {isOffline || !Boolean(thumbnailSource) ? ( ) : ( Date: Fri, 29 Sep 2023 12:53:36 +0800 Subject: [PATCH 074/205] Ensure waypoints are ordered properly for eReceipt --- src/components/DistanceEReceipt.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 6e91edcca5e4..3a79f33effe1 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useMemo} from 'react'; import {View, ScrollView} from 'react-native'; import lodashGet from 'lodash/get'; import _ from 'underscore'; @@ -36,6 +36,17 @@ function DistanceEReceipt({transaction}) { const formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : translate('common.tbd'); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); + const sortedWaypoints = useMemo(() => { + // The waypoint keys are sometimes out of order + return _.chain(waypoints) + .keys() + .sort((keyA, keyB) => { + return TransactionUtils.getWaypointIndex(keyA) - TransactionUtils.getWaypointIndex(keyB); + }) + .map((key) => ({[key]: waypoints[key]})) + .reduce((result, obj) => Object.assign(result, obj), {}) + .value(); + }, [waypoints]); return ( @@ -61,7 +72,7 @@ function DistanceEReceipt({transaction}) { {transactionMerchant} - {_.map(waypoints, (waypoint, key) => { + {_.map(sortedWaypoints, (waypoint, key) => { const index = TransactionUtils.getWaypointIndex(key); let descriptionKey = 'distance.waypointDescription.'; if (index === 0) { From a58a10e841ea3f064e52872b5389052815a115c7 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 29 Sep 2023 14:20:54 +0700 Subject: [PATCH 075/205] fix no update when clicking on go back --- src/libs/actions/BankAccounts.js | 11 ++++++----- src/libs/actions/ReimbursementAccount/index.js | 2 +- .../ReimbursementAccount/ReimbursementAccountPage.js | 6 ++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index 3ac414a1c39a..e89b7cfb3ad8 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -57,10 +57,10 @@ function clearOnfidoToken() { /** * Helper method to build the Onyx data required during setup of a Verified Business Bank Account - * + * @param {String} currentStep * @returns {Object} */ -function getVBBADataForOnyx() { +function getVBBADataForOnyx(currentStep) { return { optimisticData: [ { @@ -80,6 +80,7 @@ function getVBBADataForOnyx() { isLoading: false, errors: null, shouldUpdateDataToDraft: true, + stepToUpdate: currentStep, }, }, ], @@ -223,7 +224,7 @@ function deletePaymentBankAccount(bankAccountID) { * @param {Boolean} [params.isOnfidoSetupComplete] */ function updatePersonalInformationForBankAccount(params) { - API.write('UpdatePersonalInformationForBankAccount', params, getVBBADataForOnyx()); + API.write('UpdatePersonalInformationForBankAccount', params, getVBBADataForOnyx(CONST.BANK_ACCOUNT.STEP.REQUESTOR)); } /** @@ -340,7 +341,7 @@ function openReimbursementAccountPage(stepToOpen, subStep, localCurrentStep) { * @param {String} policyID */ function updateCompanyInformationForBankAccount(bankAccount, policyID) { - API.write('UpdateCompanyInformationForBankAccount', {...bankAccount, policyID}, getVBBADataForOnyx()); + API.write('UpdateCompanyInformationForBankAccount', {...bankAccount, policyID}, getVBBADataForOnyx(CONST.BANK_ACCOUNT.STEP.COMPANY)); } /** @@ -377,7 +378,7 @@ function connectBankAccountManually(bankAccountID, accountNumber, routingNumber, routingNumber, plaidMask, }, - getVBBADataForOnyx(), + getVBBADataForOnyx(CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT), ); } diff --git a/src/libs/actions/ReimbursementAccount/index.js b/src/libs/actions/ReimbursementAccount/index.js index e720053bfcd5..51c5f4532185 100644 --- a/src/libs/actions/ReimbursementAccount/index.js +++ b/src/libs/actions/ReimbursementAccount/index.js @@ -31,7 +31,7 @@ function setWorkspaceIDForReimbursementAccount(workspaceID) { */ function updateReimbursementAccountDraft(bankAccountData) { Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT, bankAccountData); - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {shouldUpdateDataToDraft: false}); + Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {shouldUpdateDataToDraft: false, stepTopUpdate: null}); } /** diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 2ddef05d2378..2457cc77136e 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -157,11 +157,13 @@ class ReimbursementAccountPage extends React.Component { return; } - const currentStepRouteParam = this.getStepToOpenFromRouteParams(); // Update the data that is returned from back-end to draft value if (this.props.reimbursementAccount.shouldUpdateDataToDraft) { - BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(currentStepRouteParam))); + const stepToUpdate = this.props.reimbursementAccount.stepToUpdate; + BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(stepToUpdate))); } + + const currentStepRouteParam = this.getStepToOpenFromRouteParams(); if (currentStepRouteParam === currentStep) { // The route is showing the correct step, no need to update the route param or clear errors. return; From f2b02f1ee2f1e98ded7c8300264fead9092e1fc3 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 29 Sep 2023 14:23:34 +0700 Subject: [PATCH 076/205] don't call update when unnecessary --- src/pages/ReimbursementAccount/ReimbursementAccountPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 2457cc77136e..2c3e43c6e8e5 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -158,8 +158,8 @@ class ReimbursementAccountPage extends React.Component { } // Update the data that is returned from back-end to draft value - if (this.props.reimbursementAccount.shouldUpdateDataToDraft) { - const stepToUpdate = this.props.reimbursementAccount.stepToUpdate; + const stepToUpdate = this.props.reimbursementAccount.stepToUpdate; + if (this.props.reimbursementAccount.shouldUpdateDataToDraft && stepToUpdate) { BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(stepToUpdate))); } From b62df64019c81536f09915b4eb94e703ce9882ec Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Fri, 29 Sep 2023 15:51:35 +0800 Subject: [PATCH 077/205] Set the rate to TBD when it's unavailable --- src/libs/DistanceRequestUtils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/DistanceRequestUtils.js b/src/libs/DistanceRequestUtils.js index 7744ee3d8bb1..34a5e68e21a2 100644 --- a/src/libs/DistanceRequestUtils.js +++ b/src/libs/DistanceRequestUtils.js @@ -89,8 +89,7 @@ const getDistanceMerchant = (hasRoute, distanceInMeters, unit, rate, currency, t const distanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.miles') : translate('common.kilometers'); const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); const unitString = distanceInUnits === 1 ? singularDistanceUnit : distanceUnit; - - const ratePerUnit = PolicyUtils.getUnitRateValue({rate}, toLocaleDigit); + const ratePerUnit = Boolean(rate) ? PolicyUtils.getUnitRateValue({rate}, toLocaleDigit) : translate('common.tbd'); const currencySymbol = CurrencyUtils.getCurrencySymbol(currency) || `${currency} `; return `${distanceInUnits} ${unitString} @ ${currencySymbol}${ratePerUnit} / ${singularDistanceUnit}`; From 1bbde1ae4ac28c8daf5e7b604b67e6fee911962f Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 29 Sep 2023 17:17:33 +0800 Subject: [PATCH 078/205] Use debounce --- src/libs/actions/Report.js | 5 +++-- src/pages/NewChatPage.js | 2 +- src/pages/SearchPage.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 615b0ad6bfae..b05b8510b7c5 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1,6 +1,7 @@ import {InteractionManager} from 'react-native'; import _ from 'underscore'; import lodashGet from 'lodash/get'; +import lodashDebounce from 'lodash/debounce'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Onyx from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; @@ -2210,10 +2211,10 @@ function searchInServer(searchInput) { * @private * @param {string} searchInput */ -const throttledSearchInServer = _.throttle(searchInServer, 1000, {leading: false}); +const debouncedSearchInServer = lodashDebounce(searchInServer, 300, {leading: false}); export { - throttledSearchInServer, + debouncedSearchInServer, addComment, addAttachment, reconnect, diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 30cc27845144..058efcc57e71 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -171,7 +171,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i // When search term updates we will fetch any reports useEffect(() => { - Report.throttledSearchInServer(searchTerm); + Report.debouncedSearchInServer(searchTerm); }, [searchTerm]); return ( 0) { - Report.throttledSearchInServer(searchValue); + Report.debouncedSearchInServer(searchValue); } // When the user searches we will From ad952aeae1c60a986978d8e4cafc62d9968206e2 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 29 Sep 2023 17:34:35 +0800 Subject: [PATCH 079/205] Make some requested changes --- src/components/OptionsSelector/BaseOptionsSelector.js | 2 +- src/pages/NewChatPage.js | 11 +++++++---- src/pages/SearchPage.js | 7 +++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index b183f6181009..8ab7e0fda941 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -368,7 +368,7 @@ class BaseOptionsSelector extends Component { blurOnSubmit={Boolean(this.state.allOptions.length)} spellCheck={false} /> - {this.props.textInputAlert && ( + {Boolean(this.props.textInputAlert) && ( { - Report.debouncedSearchInServer(searchTerm); - }, [searchTerm]); + const setSearchTermAndDebounceSearchInServer = useCallback((text) => { + if (text) { + Report.debouncedSearchInServer(text); + } + setSearchTerm(text); + }, []); return ( createChat(option)} - onChangeText={setSearchTerm} + onChangeText={setSearchTermAndDebounceSearchInServer} headerMessage={headerMessage} boldStyle shouldFocusOnSelectRow={!Browser.isMobile()} diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 9a81a2f3d89e..decbb10b6d62 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -20,6 +20,7 @@ import compose from '../libs/compose'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; import Performance from '../libs/Performance'; +import networkPropTypes from '../components/networkPropTypes'; const propTypes = { /* Onyx Props */ @@ -37,6 +38,8 @@ const propTypes = { ...windowDimensionsPropTypes, ...withLocalizePropTypes, + + ...networkPropTypes, }; const defaultProps = { @@ -75,7 +78,7 @@ class SearchPage extends Component { } onChangeText(searchValue = '') { - if (searchValue.length > 0) { + if (searchValue.length) { Report.debouncedSearchInServer(searchValue); } @@ -192,7 +195,7 @@ class SearchPage extends Component { showTitleTooltip shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} textInputLabel={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} - textInputAlert={this.props.network.isOffline ? this.props.translate('search.offline') : ''} + textInputAlert={this.props.network.isOffline ? `${this.props.translate('common.youAppearToBeOffline')} ${this.props.translate('search.resultsAreLimited')}` : ''} onLayout={this.searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus From bcd1a6552c6182946417e54c756e55834d2c76ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Mon, 2 Oct 2023 16:13:17 +0100 Subject: [PATCH 080/205] Minor fixes --- src/components/SelectionList/BaseSelectionList.js | 3 +-- src/libs/ComposerUtils/updateNumberOfLines/index.native.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/SelectionList/BaseSelectionList.js b/src/components/SelectionList/BaseSelectionList.js index 849aac2e32d6..5cde73fe6ce8 100644 --- a/src/components/SelectionList/BaseSelectionList.js +++ b/src/components/SelectionList/BaseSelectionList.js @@ -22,7 +22,6 @@ import Log from '../../libs/Log'; import OptionsListSkeletonView from '../OptionsListSkeletonView'; import useActiveElement from '../../hooks/useActiveElement'; import BaseListItem from './BaseListItem'; -import themeColors from '../../styles/themes/default'; import ArrowKeyFocusManager from '../ArrowKeyFocusManager'; const propTypes = { @@ -405,7 +404,7 @@ function BaseSelectionList({ onScrollBeginDrag={onScrollBeginDrag} keyExtractor={(item) => item.keyForList} extraData={focusedIndex} - indicatorStyle={'white'} + indicatorStyle="white" keyboardShouldPersistTaps="always" showsVerticalScrollIndicator={showScrollIndicator} initialNumToRender={12} diff --git a/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts b/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts index b22135b4f767..b5c28cfc79e8 100644 --- a/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts +++ b/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts @@ -8,7 +8,7 @@ import UpdateNumberOfLines from './types'; * divide by line height to get the total number of rows for the textarea. */ const updateNumberOfLines: UpdateNumberOfLines = (props, event) => { - const lineHeight = styles.textInputCompose.lineHeight; + const lineHeight = styles.textInputCompose.lineHeight ?? 0; const paddingTopAndBottom = styles.textInputComposeSpacing.paddingVertical * 2; const inputHeight = event?.nativeEvent?.contentSize?.height ?? null; if (!inputHeight) { From 6005765f66bcea9f8716200f233cd5ddcd30cf1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Mon, 2 Oct 2023 18:26:27 +0100 Subject: [PATCH 081/205] Fix findUnusedKeys to look for the correct styles file --- .github/scripts/findUnusedKeys.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/findUnusedKeys.sh b/.github/scripts/findUnusedKeys.sh index 77c3ea25326b..b1d471f632e7 100755 --- a/.github/scripts/findUnusedKeys.sh +++ b/.github/scripts/findUnusedKeys.sh @@ -6,7 +6,7 @@ LIB_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../../ && pwd)" readonly SRC_DIR="${LIB_PATH}/src" readonly STYLES_DIR="${LIB_PATH}/src/styles" -readonly STYLES_FILE="${LIB_PATH}/src/styles/styles.js" +readonly STYLES_FILE="${LIB_PATH}/src/styles/styles.ts" readonly UTILITIES_STYLES_FILE="${LIB_PATH}/src/styles/utilities" readonly STYLES_KEYS_FILE="${LIB_PATH}/scripts/style_keys_list_temp.txt" readonly UTILITY_STYLES_KEYS_FILE="${LIB_PATH}/scripts/utility_keys_list_temp.txt" @@ -348,7 +348,7 @@ echo "🔍 Looking for styles." find_utility_styles_store_prefix find_utility_usage_as_styles -# Find and store keys from styles.js +# Find and store keys from styles.ts find_styles_object_and_store_keys "$STYLES_FILE" find_styles_functions_and_store_keys "$STYLES_FILE" collect_theme_keys_from_styles "$STYLES_FILE" From 98724e9070da0843f03e561573282098a098771e Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 2 Oct 2023 10:11:26 -1000 Subject: [PATCH 082/205] Make requested changes --- src/pages/NewChatPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 9a7364df96e1..ee6ce3ce18ab 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -170,8 +170,8 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i }, [reports, personalDetails, searchTerm]); // When search term updates we will fetch any reports - const setSearchTermAndDebounceSearchInServer = useCallback((text) => { - if (text) { + const setSearchTermAndDebounceSearchInServer = useCallback((text = '') => { + if (text.length) { Report.debouncedSearchInServer(text); } setSearchTerm(text); From 25d66cc3fbf2e97dbc289f13fa0493fca45818cd Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 2 Oct 2023 17:07:21 -0700 Subject: [PATCH 083/205] Fix crash / prop types with default receipt source --- .../Attachments/AttachmentCarousel/CarouselItem.js | 5 +++-- src/components/Attachments/AttachmentView/index.js | 2 +- src/components/Attachments/propTypes.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.js index 6cecfb03fd83..2a97b3327e9c 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.js @@ -10,6 +10,7 @@ import Button from '../../Button'; import AttachmentView from '../AttachmentView'; import SafeAreaConsumer from '../../SafeAreaConsumer'; import ReportAttachmentsContext from '../../../pages/home/report/ReportAttachmentsContext'; +import * as AttachmentsPropTypes from '../propTypes'; const propTypes = { /** Attachment required information such as the source and file name */ @@ -20,8 +21,8 @@ const propTypes = { /** Whether source URL requires authentication */ isAuthTokenRequired: PropTypes.bool, - /** The source (URL) of the attachment */ - source: PropTypes.string, + /** URL to full-sized attachment or SVG function */ + source: AttachmentsPropTypes.attachmentSourcePropType.isRequired, /** Additional information about the attachment file */ file: PropTypes.shape({ diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index f6bc0160e1af..63111b75a17f 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -101,7 +101,7 @@ function AttachmentView({ // Check both source and file.name since PDFs dragged into the the text field // will appear with a source that is a blob - if (Str.isPDF(source) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) { + if ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) { const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; return ( diff --git a/src/components/Attachments/propTypes.js b/src/components/Attachments/propTypes.js index b3f0af6ab217..97717a1e51da 100644 --- a/src/components/Attachments/propTypes.js +++ b/src/components/Attachments/propTypes.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; -const attachmentSourcePropType = PropTypes.oneOfType([PropTypes.string, PropTypes.func]); +const attachmentSourcePropType = PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.number]); const attachmentFilePropType = PropTypes.shape({ name: PropTypes.string, }); From 3ab1772ca5522ef10aa79cff5860a2d05c0594c9 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 2 Oct 2023 14:35:59 -1000 Subject: [PATCH 084/205] add missing useCallback --- src/pages/NewChatPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index ee6ce3ce18ab..84222d1e26ac 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {useState, useEffect, useMemo} from 'react'; +import React, {useState, useEffect, useMemo, useCallback} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; From 97e5bb13e862a71771c1ee2d5a21eaf32a5f474f Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 2 Oct 2023 17:45:45 -1000 Subject: [PATCH 085/205] Change UX so we are showing loading row below options instead of above --- src/components/OptionsList/BaseOptionsList.js | 14 ++++++++++++-- src/components/OptionsList/optionsListPropTypes.js | 4 ++++ .../OptionsSelector/BaseOptionsSelector.js | 8 +------- src/pages/SearchPage.js | 9 ++++++++- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js index 5a40c28a86c9..25965ef5c54c 100644 --- a/src/components/OptionsList/BaseOptionsList.js +++ b/src/components/OptionsList/BaseOptionsList.js @@ -10,6 +10,7 @@ import Text from '../Text'; import {propTypes as optionsListPropTypes, defaultProps as optionsListDefaultProps} from './optionsListPropTypes'; import OptionsListSkeletonView from '../OptionsListSkeletonView'; import usePrevious from '../../hooks/usePrevious'; +import OptionsListSkeletonRow from '../OptionsListSkeletonRow'; const propTypes = { /** Determines whether the keyboard gets dismissed in response to a drag */ @@ -65,6 +66,7 @@ function BaseOptionsList({ isDisabled, innerRef, isRowMultilineSupported, + shouldShowHeaderMessage, }) { const flattenedData = useRef(); const previousSections = usePrevious(sections); @@ -189,6 +191,15 @@ function BaseOptionsList({ return option.name === item.searchText; }); + if (item.loadingRow) { + return ( + + ); + } + return ( ; }; - return ( {isLoading ? ( ) : ( <> - {headerMessage ? ( + {shouldShowHeaderMessage && headerMessage ? ( {headerMessage} diff --git a/src/components/OptionsList/optionsListPropTypes.js b/src/components/OptionsList/optionsListPropTypes.js index a2479c878041..967f30d44119 100644 --- a/src/components/OptionsList/optionsListPropTypes.js +++ b/src/components/OptionsList/optionsListPropTypes.js @@ -84,6 +84,9 @@ const propTypes = { /** Whether to wrap large text up to 2 lines */ isRowMultilineSupported: PropTypes.bool, + + /** Whether to show the header message below the input */ + shouldShowHeaderMessage: PropTypes.bool, }; const defaultProps = { @@ -109,6 +112,7 @@ const defaultProps = { shouldDisableRowInnerPadding: false, showScrollIndicator: false, isRowMultilineSupported: false, + shouldShowHeaderMessage: true, }; export {propTypes, defaultProps}; diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index bc545b7c0ebf..b6ee91e23454 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -17,7 +17,6 @@ import {propTypes as optionsSelectorPropTypes, defaultProps as optionsSelectorDe import setSelection from '../../libs/setSelection'; import compose from '../../libs/compose'; import getPlatform from '../../libs/getPlatform'; -import OptionsListSkeletonRow from '../OptionsListSkeletonRow'; import FormHelpMessage from '../FormHelpMessage'; const propTypes = { @@ -394,12 +393,6 @@ class BaseOptionsSelector extends Component { ); const optionsList = ( <> - {this.props.shouldShowLoader && ( - - )} (this.list = el)} optionHoveredStyle={this.props.optionHoveredStyle} @@ -432,6 +425,7 @@ class BaseOptionsSelector extends Component { isLoading={!this.props.shouldShowOptions} showScrollIndicator={this.props.showScrollIndicator} isRowMultilineSupported={this.props.isRowMultilineSupported} + shouldShowHeaderMessage={this.props.shouldShowHeaderMessage} /> ); diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index decbb10b6d62..318294517a97 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -121,6 +121,13 @@ class SearchPage extends Component { }); } + if (this.props.isSearchingForReports) { + sections.push({ + data: [{loadingRow: true}], + shouldShow: true, + indexOffset, + }) + } return sections; } @@ -199,7 +206,7 @@ class SearchPage extends Component { onLayout={this.searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus - shouldShowLoader={this.props.isSearchingForReports} + shouldShowHeaderMessage={!this.props.isSearchingForReports} /> From c0239e2ccfb8a57f8c114024ca77516ee5c97c10 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 3 Oct 2023 12:22:34 +0200 Subject: [PATCH 086/205] added a new condition to handle inner function with returning object in a new line --- .github/scripts/findUnusedKeys.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/scripts/findUnusedKeys.sh b/.github/scripts/findUnusedKeys.sh index b1d471f632e7..1411fffc8389 100755 --- a/.github/scripts/findUnusedKeys.sh +++ b/.github/scripts/findUnusedKeys.sh @@ -210,7 +210,12 @@ find_theme_style_and_store_keys() { fi # Check if we are inside an arrow function - if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ || "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then + if [[ "$line" =~ ^[[:space:]]*([a-zA-Zgv 0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ || "$line" =~ ^[[:space:]]*([a-zA-Zgv 0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then + inside_arrow_function=true + continue + fi + + if [[ "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then inside_arrow_function=true continue fi From 0de5e86f97edc2f65c2c07ae90ab0660a20dd699 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 3 Oct 2023 17:38:41 +0700 Subject: [PATCH 087/205] add default value for param --- src/libs/actions/BankAccounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index e89b7cfb3ad8..a90e4d066e95 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -60,7 +60,7 @@ function clearOnfidoToken() { * @param {String} currentStep * @returns {Object} */ -function getVBBADataForOnyx(currentStep) { +function getVBBADataForOnyx(currentStep = undefined) { return { optimisticData: [ { From b4bfd76fbaab87253809f3e707108731ba804516 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 3 Oct 2023 17:39:14 +0700 Subject: [PATCH 088/205] add check for shouldUpdateDataToDraft --- src/libs/actions/BankAccounts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index a90e4d066e95..3e2b064ec48e 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -57,7 +57,7 @@ function clearOnfidoToken() { /** * Helper method to build the Onyx data required during setup of a Verified Business Bank Account - * @param {String} currentStep + * @param {String | undefined} currentStep * @returns {Object} */ function getVBBADataForOnyx(currentStep = undefined) { @@ -79,7 +79,7 @@ function getVBBADataForOnyx(currentStep = undefined) { value: { isLoading: false, errors: null, - shouldUpdateDataToDraft: true, + shouldUpdateDataToDraft: !!currentStep, stepToUpdate: currentStep, }, }, From 1c1dd14f79f302d98c411e729d233282ea919244 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 3 Oct 2023 18:07:35 +0700 Subject: [PATCH 089/205] rename variable --- src/libs/actions/BankAccounts.js | 3 +-- src/libs/actions/ReimbursementAccount/index.js | 2 +- src/pages/ReimbursementAccount/ReimbursementAccountPage.js | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index 3e2b064ec48e..e7d9816292c3 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -79,8 +79,7 @@ function getVBBADataForOnyx(currentStep = undefined) { value: { isLoading: false, errors: null, - shouldUpdateDataToDraft: !!currentStep, - stepToUpdate: currentStep, + stepToUpdateToDraft: currentStep, }, }, ], diff --git a/src/libs/actions/ReimbursementAccount/index.js b/src/libs/actions/ReimbursementAccount/index.js index 51c5f4532185..5598ecb96503 100644 --- a/src/libs/actions/ReimbursementAccount/index.js +++ b/src/libs/actions/ReimbursementAccount/index.js @@ -31,7 +31,7 @@ function setWorkspaceIDForReimbursementAccount(workspaceID) { */ function updateReimbursementAccountDraft(bankAccountData) { Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT, bankAccountData); - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {shouldUpdateDataToDraft: false, stepTopUpdate: null}); + Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {shouldUpdateDataToDraft: false, stepToUpdateToDraft: undefined}); } /** diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 2c3e43c6e8e5..f2572ada51be 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -158,8 +158,8 @@ class ReimbursementAccountPage extends React.Component { } // Update the data that is returned from back-end to draft value - const stepToUpdate = this.props.reimbursementAccount.stepToUpdate; - if (this.props.reimbursementAccount.shouldUpdateDataToDraft && stepToUpdate) { + const stepToUpdateToDraft = this.props.reimbursementAccount.stepToUpdate; + if (stepToUpdateToDraft) { BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(stepToUpdate))); } From 401e05d46d76d21999447e14f59490c0c9ae49df Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 3 Oct 2023 18:20:10 +0700 Subject: [PATCH 090/205] fix lint --- src/pages/ReimbursementAccount/ReimbursementAccountPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index f2572ada51be..4e45c72f676f 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -160,7 +160,7 @@ class ReimbursementAccountPage extends React.Component { // Update the data that is returned from back-end to draft value const stepToUpdateToDraft = this.props.reimbursementAccount.stepToUpdate; if (stepToUpdateToDraft) { - BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(stepToUpdate))); + BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(stepToUpdateToDraft))); } const currentStepRouteParam = this.getStepToOpenFromRouteParams(); From cc35473ec3a3bdd8d85667eadc0f7f8e0da7394d Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 3 Oct 2023 18:32:37 +0700 Subject: [PATCH 091/205] remove unuse field --- src/libs/actions/ReimbursementAccount/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/ReimbursementAccount/index.js b/src/libs/actions/ReimbursementAccount/index.js index 5598ecb96503..4471d19c8151 100644 --- a/src/libs/actions/ReimbursementAccount/index.js +++ b/src/libs/actions/ReimbursementAccount/index.js @@ -31,7 +31,7 @@ function setWorkspaceIDForReimbursementAccount(workspaceID) { */ function updateReimbursementAccountDraft(bankAccountData) { Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT, bankAccountData); - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {shouldUpdateDataToDraft: false, stepToUpdateToDraft: undefined}); + Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {stepToUpdateToDraft: undefined}); } /** From 03144804461a8605a56546ca5c3faa756fd53971 Mon Sep 17 00:00:00 2001 From: dukenv0307 <129500732+dukenv0307@users.noreply.github.com> Date: Tue, 3 Oct 2023 18:37:39 +0700 Subject: [PATCH 092/205] Update src/pages/ReimbursementAccount/ReimbursementAccountPage.js Co-authored-by: Olly --- src/pages/ReimbursementAccount/ReimbursementAccountPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 4e45c72f676f..f09963129bb1 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -158,7 +158,7 @@ class ReimbursementAccountPage extends React.Component { } // Update the data that is returned from back-end to draft value - const stepToUpdateToDraft = this.props.reimbursementAccount.stepToUpdate; + const stepToUpdateToDraft = this.props.reimbursementAccount.stepToUpdateToDraft; if (stepToUpdateToDraft) { BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(stepToUpdateToDraft))); } From 2d653b80cd36969ef970479f1d00fce11d9c884d Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 3 Oct 2023 08:06:14 -0700 Subject: [PATCH 093/205] Fix eReceipt background on Android --- assets/images/eReceipt_background.svg | 32 +++++++++++++-------------- src/styles/styles.js | 1 + 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/assets/images/eReceipt_background.svg b/assets/images/eReceipt_background.svg index 257489bd6fdd..5070ed3b2f24 100644 --- a/assets/images/eReceipt_background.svg +++ b/assets/images/eReceipt_background.svg @@ -1,24 +1,24 @@ + viewBox="0 0 335 540" style="enable-background:new 0 0 335 540;" xml:space="preserve"> - + - + - + - - + + - + diff --git a/src/styles/styles.js b/src/styles/styles.js index 40dfa19d5448..6605b6c0ce53 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3280,6 +3280,7 @@ const styles = (theme) => ({ position: 'absolute', top: 0, left: 0, + height: 540, }, eReceiptPanel: { From b9dca4c3c07fb40451547fa4e3c38a37ddf7ccef Mon Sep 17 00:00:00 2001 From: laurenreidexpensify <62073721+laurenreidexpensify@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:02:06 +0100 Subject: [PATCH 094/205] Create removing-users.md --- .../removing-users.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/articles/expensify-classic/manage-employees-and-report-approvals/removing-users.md diff --git a/docs/articles/expensify-classic/manage-employees-and-report-approvals/removing-users.md b/docs/articles/expensify-classic/manage-employees-and-report-approvals/removing-users.md new file mode 100644 index 000000000000..0db950680aa8 --- /dev/null +++ b/docs/articles/expensify-classic/manage-employees-and-report-approvals/removing-users.md @@ -0,0 +1,34 @@ +--- +title: Coming Soon +description: Coming Soon +--- + +Removing a member from a workspace disables their ability to use the workspace. Please note that it does not delete their account or deactivate the Expensify Card. + +## How to Remove a Workspace Member +1. Important: Make sure the employee has submitted all Draft reports and the reports have been approved, reimbursed, etc. +2. Go to Settings > Workspaces > Group > [Workspace Name] > Members > Workspace Members +3. Select the member you'd like to remove and click the **Remove** button at the top of the Members table. +4. If this member was an approver, make sure that reports are not routing to them in the workflow. + +# FAQ + +## Will reports from this member on this workspace still be available? +Yes, as long as the reports have been submitted. You can navigate to the Reports page and enter the member's email in the search field to find them. However, Draft reports will be removed from the workspace, so these will no longer be visible to the Workspace Admin. + +## Can members still access their reports on a workspace after they have been removed? +Yes. Any report that has been approved will now show the workspace as “(not shared)” in their account. If it is a Draft Report they will still be able to edit it and add it to a new workspace. If the report is Approved or Reimbursed they will not be able to edit it further. + +## Who can remove members from a workspace? +Only Workspace Admins. It is not possible for a member to add or remove themselves from a workspace. It is not possible for a Domain Admin who is not also a Workspace Admin to remove a member from a workspace. + +## How do I remove a member from a workspace if I am seeing an error message? +If a member is a **preferred exporter, billing owner, report approver** or has **processing reports**, to remove them the workspace you will first need to: + +* **Preferred Exporter** - go to Settings > Workspaces > Group > [Workspace Name] > Connections > Configure and select a different Workspace Admin in the dropdown for **Preferred Exporter**. +* **Billing Owner** - take over billing on the Settings > Workspaces > Group > [Workspace Name] > Overview page. +* **Processing reports** - approve or reject the member’s reports on your Reports page. +* **Approval Workflow** - remove them as a workflow approver on your Settings > Workspaces > Group > [Workspace Name] > Members > Approval Mode > page by changing the "**Submit reports to**" field. + +## How do I remove a user completely from a company account? +If you have a Control Workspace and have Domain Control enabled, you will need to remove them from the domain to delete members' accounts entirely and deactivate the Expensify Card. From af3fec77c3ee39c185aad5a39ef2cde7141a0950 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 3 Oct 2023 10:48:03 -1000 Subject: [PATCH 095/205] Remove unneeded changes. Fix propTypes --- src/components/OptionsList/BaseOptionsList.js | 1 + .../OptionsSelector/BaseOptionsSelector.js | 68 +++++++++---------- src/pages/SearchPage.js | 3 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js index 25965ef5c54c..2521962756a5 100644 --- a/src/components/OptionsList/BaseOptionsList.js +++ b/src/components/OptionsList/BaseOptionsList.js @@ -247,6 +247,7 @@ function BaseOptionsList({ return ; }; + return ( {isLoading ? ( diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index b6ee91e23454..9d2ec7d493c6 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -392,42 +392,40 @@ class BaseOptionsSelector extends Component { ); const optionsList = ( - <> - (this.list = el)} - optionHoveredStyle={this.props.optionHoveredStyle} - onSelectRow={this.props.onSelectRow ? this.selectRow : undefined} - sections={this.props.sections} - focusedIndex={this.state.focusedIndex} - selectedOptions={this.props.selectedOptions} - canSelectMultipleOptions={this.props.canSelectMultipleOptions} - shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton} - multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText} - onAddToSelection={this.addToSelection} - hideSectionHeaders={this.props.hideSectionHeaders} - headerMessage={this.props.headerMessage} - boldStyle={this.props.boldStyle} - showTitleTooltip={this.props.showTitleTooltip} - isDisabled={this.props.isDisabled} - shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator} - highlightSelectedOptions={this.props.highlightSelectedOptions} - onLayout={() => { - if (this.props.selectedOptions.length === 0) { - this.scrollToIndex(this.state.focusedIndex, false); + (this.list = el)} + optionHoveredStyle={this.props.optionHoveredStyle} + onSelectRow={this.props.onSelectRow ? this.selectRow : undefined} + sections={this.props.sections} + focusedIndex={this.state.focusedIndex} + selectedOptions={this.props.selectedOptions} + canSelectMultipleOptions={this.props.canSelectMultipleOptions} + shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton} + multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText} + onAddToSelection={this.addToSelection} + hideSectionHeaders={this.props.hideSectionHeaders} + headerMessage={this.props.headerMessage} + boldStyle={this.props.boldStyle} + showTitleTooltip={this.props.showTitleTooltip} + isDisabled={this.props.isDisabled} + shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator} + highlightSelectedOptions={this.props.highlightSelectedOptions} + onLayout={() => { + if (this.props.selectedOptions.length === 0) { + this.scrollToIndex(this.state.focusedIndex, false); + } + if (this.props.onLayout) { + this.props.onLayout(); } - if (this.props.onLayout) { - this.props.onLayout(); - } - }} - contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]} - listContainerStyles={this.props.listContainerStyles} - listStyles={this.props.listStyles} - isLoading={!this.props.shouldShowOptions} - showScrollIndicator={this.props.showScrollIndicator} - isRowMultilineSupported={this.props.isRowMultilineSupported} - shouldShowHeaderMessage={this.props.shouldShowHeaderMessage} - /> - + }} + contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]} + listContainerStyles={this.props.listContainerStyles} + listStyles={this.props.listStyles} + isLoading={!this.props.shouldShowOptions} + showScrollIndicator={this.props.showScrollIndicator} + isRowMultilineSupported={this.props.isRowMultilineSupported} + shouldShowHeaderMessage={this.props.shouldShowHeaderMessage} + /> ); return ( Date: Tue, 3 Oct 2023 10:49:18 -1000 Subject: [PATCH 096/205] Undo bad whitespace change --- src/components/OptionsSelector/BaseOptionsSelector.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 9d2ec7d493c6..415600397e74 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -415,9 +415,9 @@ class BaseOptionsSelector extends Component { this.scrollToIndex(this.state.focusedIndex, false); } if (this.props.onLayout) { - this.props.onLayout(); - } - }} + this.props.onLayout(); + } + }} contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]} listContainerStyles={this.props.listContainerStyles} listStyles={this.props.listStyles} From 06531cb188c3ef142883fc1fc48de4fe457bc190 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 3 Oct 2023 10:49:44 -1000 Subject: [PATCH 097/205] undo another whitespace change --- src/components/OptionsSelector/BaseOptionsSelector.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 415600397e74..0daf1d5e6d48 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -414,6 +414,7 @@ class BaseOptionsSelector extends Component { if (this.props.selectedOptions.length === 0) { this.scrollToIndex(this.state.focusedIndex, false); } + if (this.props.onLayout) { this.props.onLayout(); } From 95d436b7b98b83c2dc47bb9c518838b306189b50 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 3 Oct 2023 12:51:42 -1000 Subject: [PATCH 098/205] Fix UI race issue with No results found --- src/libs/actions/Report.js | 25 +++++++++++++++---------- src/pages/NewChatPage.js | 6 +++--- src/pages/SearchPage.js | 6 +++--- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 0dc40baabe84..659479253ae5 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2172,9 +2172,10 @@ function clearPrivateNotesError(reportID, accountID) { * @private * @param {string} searchInput */ -function searchInServer(searchInput) { +function searchForReports(searchInput) { // We do not try to make this request while offline because it sets a loading indicator optimistically if (isNetworkOffline) { + Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, false); return; } @@ -2182,13 +2183,6 @@ function searchInServer(searchInput) { 'SearchForReports', {searchInput}, { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - value: true, - }, - ], successData: [ { onyxMethod: Onyx.METHOD.MERGE, @@ -2211,10 +2205,21 @@ function searchInServer(searchInput) { * @private * @param {string} searchInput */ -const debouncedSearchInServer = lodashDebounce(searchInServer, 300, {leading: false}); +const debouncedSearchInServer = lodashDebounce(searchForReports, 300, {leading: false}); + +/** + * @param {string} searchInput + */ +function searchInServer(searchInput) { + // Why not set this in optimistic data? It won't run until the API request happens and while the API request is debounced + // we want to show the loading state right away. Otherwise, we will see a flashing UI where the client options are sorted and + // tell the user there are no options, then we start searching, and tell them there are no options again. + Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, true); + debouncedSearchInServer(searchInput); +} export { - debouncedSearchInServer, + searchInServer, addComment, addAttachment, reconnect, diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 84222d1e26ac..2b34a077fb0b 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -170,9 +170,9 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i }, [reports, personalDetails, searchTerm]); // When search term updates we will fetch any reports - const setSearchTermAndDebounceSearchInServer = useCallback((text = '') => { + const setSearchTermAndSearchInServer = useCallback((text = '') => { if (text.length) { - Report.debouncedSearchInServer(text); + Report.searchInServer(text); } setSearchTerm(text); }, []); @@ -204,7 +204,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i selectedOptions={selectedOptions} value={searchTerm} onSelectRow={(option) => createChat(option)} - onChangeText={setSearchTermAndDebounceSearchInServer} + onChangeText={setSearchTermAndSearchInServer} headerMessage={headerMessage} boldStyle shouldFocusOnSelectRow={!Browser.isMobile()} diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 8ee9aa8c3f57..f57f86c50189 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -2,7 +2,7 @@ import _ from 'underscore'; import React, {Component} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; +import Onyx, {withOnyx} from 'react-native-onyx'; import OptionsSelector from '../components/OptionsSelector'; import * as OptionsListUtils from '../libs/OptionsListUtils'; import * as ReportUtils from '../libs/ReportUtils'; @@ -80,7 +80,7 @@ class SearchPage extends Component { onChangeText(searchValue = '') { if (searchValue.length) { - Report.debouncedSearchInServer(searchValue); + Report.searchInServer(searchValue); } // When the user searches we will @@ -127,7 +127,7 @@ class SearchPage extends Component { data: [{loadingRow: true}], shouldShow: true, indexOffset, - }) + }); } return sections; } From 3d1071046b52f07eab742b6f8ed031a5b75a2260 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 3 Oct 2023 13:20:53 -1000 Subject: [PATCH 099/205] Add loadingRow to NewChat page --- src/pages/NewChatPage.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 2b34a077fb0b..a58748aa7546 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -104,8 +104,16 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i }); } + if (isSearchingForReports) { + sectionsList.push({ + data: [{loadingRow: true}], + shouldShow: true, + indexOffset, + }); + } + return sectionsList; - }, [translate, filteredPersonalDetails, filteredRecentReports, filteredUserToInvite, maxParticipantsReached, selectedOptions]); + }, [translate, filteredPersonalDetails, filteredRecentReports, filteredUserToInvite, maxParticipantsReached, selectedOptions, isSearchingForReports]); /** * Removes a selected option from list if already selected. If not already selected add this option to the list. @@ -215,7 +223,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i onConfirmSelection={createGroup} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} - shouldShowLoader={isSearchingForReports} + shouldShowHeaderMessage={!isSearchingForReports} /> From 1e86e514f8b9cba795398562863fce8fe22ea99c Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 3 Oct 2023 16:27:17 -0700 Subject: [PATCH 100/205] Fix linter --- .../AttachmentCarousel/CarouselItem.js | 3 +++ .../Attachments/AttachmentView/index.js | 4 +++- src/components/DistanceEReceipt.js | 24 +++++++++---------- src/libs/DistanceRequestUtils.js | 2 +- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.js index 2a97b3327e9c..096b6d60d428 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.js @@ -32,6 +32,9 @@ const propTypes = { /** Whether the attachment has been flagged */ hasBeenFlagged: PropTypes.bool, + + /** The id of the transaction related to the attachment */ + transactionID: PropTypes.string, }).isRequired, /** Whether the attachment is currently being viewed in the carousel */ diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 63111b75a17f..8d88efa06714 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -3,6 +3,7 @@ import {View, ActivityIndicator} from 'react-native'; import _ from 'underscore'; import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; +import {withOnyx} from 'react-native-onyx'; import styles from '../../../styles/styles'; import Icon from '../../Icon'; import * as Expensicons from '../../Icon/Expensicons'; @@ -20,7 +21,6 @@ import {attachmentViewPropTypes, attachmentViewDefaultProps} from './propTypes'; import * as TransactionUtils from '../../../libs/TransactionUtils'; import DistanceEReceipt from '../../DistanceEReceipt'; import useNetwork from '../../../hooks/useNetwork'; -import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../../../ONYXKEYS'; const propTypes = { @@ -43,6 +43,8 @@ const propTypes = { /** Denotes whether it is a workspace avatar or not */ isWorkspaceAvatar: PropTypes.bool, + /** The id of the transaction related to the attachment */ + // eslint-disable-next-line react/no-unused-prop-types transactionID: PropTypes.string, }; diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 3a79f33effe1..c461db397b3b 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -36,17 +36,17 @@ function DistanceEReceipt({transaction}) { const formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : translate('common.tbd'); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); - const sortedWaypoints = useMemo(() => { - // The waypoint keys are sometimes out of order - return _.chain(waypoints) - .keys() - .sort((keyA, keyB) => { - return TransactionUtils.getWaypointIndex(keyA) - TransactionUtils.getWaypointIndex(keyB); - }) - .map((key) => ({[key]: waypoints[key]})) - .reduce((result, obj) => Object.assign(result, obj), {}) - .value(); - }, [waypoints]); + const sortedWaypoints = useMemo( + () => + // The waypoint keys are sometimes out of order + _.chain(waypoints) + .keys() + .sort((keyA, keyB) => TransactionUtils.getWaypointIndex(keyA) - TransactionUtils.getWaypointIndex(keyB)) + .map((key) => ({[key]: waypoints[key]})) + .reduce((result, obj) => _.assign(result, obj), {}) + .value(), + [waypoints], + ); return ( @@ -56,7 +56,7 @@ function DistanceEReceipt({transaction}) { pointerEvents="none" /> - {isOffline || !Boolean(thumbnailSource) ? ( + {isOffline || !thumbnailSource ? ( ) : ( Date: Tue, 3 Oct 2023 13:34:17 -1000 Subject: [PATCH 101/205] Clean up --- .../OptionsSelector/BaseOptionsSelector.js | 61 +++++++++---------- src/pages/SearchPage.js | 1 - 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index e024ce6135d8..0e53683e2e99 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -360,38 +360,30 @@ class BaseOptionsSelector extends Component { const shouldShowDefaultConfirmButton = !this.props.footerContent && defaultConfirmButtonText; const safeAreaPaddingBottomStyle = shouldShowFooter ? undefined : this.props.safeAreaPaddingBottomStyle; const textInput = ( - <> - (this.textInput = el)} - value={this.props.value} - label={this.props.textInputLabel} - accessibilityLabel={this.props.textInputLabel} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} - onChangeText={this.props.onChangeText} - onSubmitEditing={this.selectFocusedOption} - placeholder={this.props.placeholderText} - maxLength={this.props.maxLength} - keyboardType={this.props.keyboardType} - onBlur={(e) => { - if (!this.props.shouldFocusOnSelectRow) { - return; - } - this.relatedTarget = e.relatedTarget; - }} - selectTextOnFocus - blurOnSubmit={Boolean(this.state.allOptions.length)} - spellCheck={false} - shouldInterceptSwipe={this.props.shouldTextInputInterceptSwipe} - /> - {Boolean(this.props.textInputAlert) && ( - - )} - + (this.textInput = el)} + value={this.props.value} + label={this.props.textInputLabel} + accessibilityLabel={this.props.textInputLabel} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} + onChangeText={this.props.onChangeText} + onSubmitEditing={this.selectFocusedOption} + placeholder={this.props.placeholderText} + maxLength={this.props.maxLength} + keyboardType={this.props.keyboardType} + onBlur={(e) => { + if (!this.props.shouldFocusOnSelectRow) { + return; + } + this.relatedTarget = e.relatedTarget; + }} + selectTextOnFocus + blurOnSubmit={Boolean(this.state.allOptions.length)} + spellCheck={false} + shouldInterceptSwipe={this.props.shouldTextInputInterceptSwipe} + /> ); + const optionsList = ( (this.list = el)} @@ -451,6 +443,13 @@ class BaseOptionsSelector extends Component { {this.props.children} {this.props.shouldShowTextInput && textInput} + {Boolean(this.props.textInputAlert) && ( + + )} {optionsList} diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index f57f86c50189..46f086bfd854 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -83,7 +83,6 @@ class SearchPage extends Component { Report.searchInServer(searchValue); } - // When the user searches we will this.setState({searchValue}, this.debouncedUpdateOptions); } From 92c0ed48fcfad44f6ca21302ccb93927e37286d4 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 3 Oct 2023 13:35:10 -1000 Subject: [PATCH 102/205] remove new line --- src/components/OptionsSelector/BaseOptionsSelector.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 0e53683e2e99..fa1c1415b256 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -383,7 +383,6 @@ class BaseOptionsSelector extends Component { shouldInterceptSwipe={this.props.shouldTextInputInterceptSwipe} /> ); - const optionsList = ( (this.list = el)} From 058f38e10f5829b995ee0d0cee4f78e5abd41edf Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 3 Oct 2023 17:33:07 -0700 Subject: [PATCH 103/205] Prevent top of merchant text being cut off --- src/styles/styles.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index 6605b6c0ce53..a29636cbf796 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3249,7 +3249,10 @@ const styles = (theme) => ({ eReceiptMerchant: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeXLarge, - lineHeight: variables.lineHeightXLarge, + + // Setting the line height caused the top of the text to be cut off. Here is an issue to investigate and fix this. + // https://github.com/Expensify/App/issues/28772 + // lineHeight: variables.lineHeightXLarge, color: theme.text, }, From b101e17d4fcf8829d124669a850e6b5d19dbcf81 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 3 Oct 2023 16:21:26 -1000 Subject: [PATCH 104/205] Style --- src/pages/SearchPage.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 46f086bfd854..59b26d7469b5 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -2,7 +2,7 @@ import _ from 'underscore'; import React, {Component} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import Onyx, {withOnyx} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import OptionsSelector from '../components/OptionsSelector'; import * as OptionsListUtils from '../libs/OptionsListUtils'; import * as ReportUtils from '../libs/ReportUtils'; @@ -202,7 +202,9 @@ class SearchPage extends Component { showTitleTooltip shouldShowOptions={didScreenTransitionEnd && isOptionsDataReady} textInputLabel={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} - textInputAlert={this.props.network.isOffline ? `${this.props.translate('common.youAppearToBeOffline')} ${this.props.translate('search.resultsAreLimited')}` : ''} + textInputAlert={ + this.props.network.isOffline ? `${this.props.translate('common.youAppearToBeOffline')} ${this.props.translate('search.resultsAreLimited')}` : '' + } onLayout={this.searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus From abb6b2066a8cf0a45234373b87c98ac6c899fde8 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 4 Oct 2023 14:53:28 +0700 Subject: [PATCH 105/205] add description --- src/libs/actions/BankAccounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index e7d9816292c3..fbc720630290 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -57,7 +57,7 @@ function clearOnfidoToken() { /** * Helper method to build the Onyx data required during setup of a Verified Business Bank Account - * @param {String | undefined} currentStep + * @param {String | undefined} currentStep The step that we need to update the data from BE to draft value * @returns {Object} */ function getVBBADataForOnyx(currentStep = undefined) { From bec41b8ab84ee7d6343f95cdcc8d76859a20e36e Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 4 Oct 2023 16:29:12 +0700 Subject: [PATCH 106/205] add more detail description --- src/libs/actions/BankAccounts.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index fbc720630290..3b6507fd938f 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -57,7 +57,7 @@ function clearOnfidoToken() { /** * Helper method to build the Onyx data required during setup of a Verified Business Bank Account - * @param {String | undefined} currentStep The step that we need to update the data from BE to draft value + * @param {String | undefined} currentStep The step that we need to update the data from backend to draft value * @returns {Object} */ function getVBBADataForOnyx(currentStep = undefined) { @@ -79,6 +79,9 @@ function getVBBADataForOnyx(currentStep = undefined) { value: { isLoading: false, errors: null, + // The value of some fields of the currentStep are changed i.e. being trimmed or phone number is parsed when backend returns the data + // So we need to add this field in successData to update the current data from reimbursement account to the draft value of the form + // if currentStep is undefined that means the step doesn't need update to the drafts stepToUpdateToDraft: currentStep, }, }, From 4e75345b7ab7e066cb0c56b1f3ef6f94ec84dc4e Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 4 Oct 2023 16:31:12 +0700 Subject: [PATCH 107/205] fix typo --- src/libs/actions/BankAccounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index 3b6507fd938f..be71082b7592 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -81,7 +81,7 @@ function getVBBADataForOnyx(currentStep = undefined) { errors: null, // The value of some fields of the currentStep are changed i.e. being trimmed or phone number is parsed when backend returns the data // So we need to add this field in successData to update the current data from reimbursement account to the draft value of the form - // if currentStep is undefined that means the step doesn't need update to the drafts + // if currentStep is undefined that means the step doesn't need update to the draft stepToUpdateToDraft: currentStep, }, }, From 3bd0c26b10639a920b765a50637481d347d773eb Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Wed, 4 Oct 2023 08:58:22 -0700 Subject: [PATCH 108/205] Line height can be larger by convention --- src/styles/styles.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index 56593e97817a..104eff3e9874 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3260,10 +3260,7 @@ const styles = (theme) => ({ eReceiptMerchant: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeXLarge, - - // Setting the line height caused the top of the text to be cut off. Here is an issue to investigate and fix this. - // https://github.com/Expensify/App/issues/28772 - // lineHeight: variables.lineHeightXLarge, + lineHeight: variables.lineHeightXXLarge, color: theme.text, }, From 50349b28868034813ceb4fbfcbfe7e8a4cbd9d1f Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Wed, 4 Oct 2023 11:49:57 -0700 Subject: [PATCH 109/205] Show scroll bar at the full width of the modal --- src/components/DistanceEReceipt.js | 7 +++++-- src/styles/styles.js | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index c461db397b3b..ee725ea54f67 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -48,8 +48,11 @@ function DistanceEReceipt({transaction}) { [waypoints], ); return ( - - + + ({ eReceiptPanel: { ...spacing.p5, ...spacing.pb8, + ...spacing.m5, backgroundColor: colors.green800, borderRadius: 20, width: 335, From 007d2d3defdf0287c0fb9b91bc561b438f464668 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 4 Oct 2023 11:49:08 -1000 Subject: [PATCH 110/205] Show loading spinner instead of skeleton --- src/components/OptionsList/BaseOptionsList.js | 15 ++++----------- .../OptionsList/optionsListPropTypes.js | 6 +++--- .../OptionsSelector/BaseOptionsSelector.js | 3 ++- src/components/TextInput/BaseTextInput.js | 5 ++++- src/pages/NewChatPage.js | 12 ++---------- src/pages/SearchPage.js | 9 +-------- 6 files changed, 16 insertions(+), 34 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js index 2521962756a5..358b4a8c77e1 100644 --- a/src/components/OptionsList/BaseOptionsList.js +++ b/src/components/OptionsList/BaseOptionsList.js @@ -66,7 +66,7 @@ function BaseOptionsList({ isDisabled, innerRef, isRowMultilineSupported, - shouldShowHeaderMessage, + isLoadingNewOptions, }) { const flattenedData = useRef(); const previousSections = usePrevious(sections); @@ -191,15 +191,6 @@ function BaseOptionsList({ return option.name === item.searchText; }); - if (item.loadingRow) { - return ( - - ); - } - return ( ) : ( <> - {shouldShowHeaderMessage && headerMessage ? ( + {/* If we are loading new options we will avoid showing any header message. This is mostly because one of the header messages says there are no options. */} + {/* This is confusing because we might be in the process of loading fresh options from the server. */} + {!isLoadingNewOptions && headerMessage ? ( {headerMessage} diff --git a/src/components/OptionsList/optionsListPropTypes.js b/src/components/OptionsList/optionsListPropTypes.js index 967f30d44119..fb40311dfe76 100644 --- a/src/components/OptionsList/optionsListPropTypes.js +++ b/src/components/OptionsList/optionsListPropTypes.js @@ -85,8 +85,8 @@ const propTypes = { /** Whether to wrap large text up to 2 lines */ isRowMultilineSupported: PropTypes.bool, - /** Whether to show the header message below the input */ - shouldShowHeaderMessage: PropTypes.bool, + /** Whether we are loading new options */ + isLoadingNewOptions: PropTypes.bool, }; const defaultProps = { @@ -112,7 +112,7 @@ const defaultProps = { shouldDisableRowInnerPadding: false, showScrollIndicator: false, isRowMultilineSupported: false, - shouldShowHeaderMessage: true, + isLoadingNewOptions: true, }; export {propTypes, defaultProps}; diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index fa1c1415b256..6e0339e4859c 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -381,6 +381,7 @@ class BaseOptionsSelector extends Component { blurOnSubmit={Boolean(this.state.allOptions.length)} spellCheck={false} shouldInterceptSwipe={this.props.shouldTextInputInterceptSwipe} + isLoading={this.props.isLoadingNewOptions} /> ); const optionsList = ( @@ -417,7 +418,7 @@ class BaseOptionsSelector extends Component { isLoading={!this.props.shouldShowOptions} showScrollIndicator={this.props.showScrollIndicator} isRowMultilineSupported={this.props.isRowMultilineSupported} - shouldShowHeaderMessage={this.props.shouldShowHeaderMessage} + isLoadingNewOptions={this.props.isLoadingNewOptions} /> ); return ( diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 91591de7f045..28baed106b1c 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import React, {useState, useRef, useEffect, useCallback, useMemo} from 'react'; -import {Animated, View, StyleSheet} from 'react-native'; +import {Animated, View, StyleSheet, ActivityIndicator} from 'react-native'; import Str from 'expensify-common/lib/str'; import RNTextInput from '../RNTextInput'; import TextInputLabel from './TextInputLabel'; @@ -368,6 +368,9 @@ function BaseTextInput(props) { // `dataset.submitOnEnter` is used to indicate that pressing Enter on this input should call the submit callback. dataSet={{submitOnEnter: isMultiline && props.submitOnEnter}} /> + {props.isLoading && ( + + )} {Boolean(props.secureTextEntry) && ( diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 59b26d7469b5..1cf1e97e5990 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -121,13 +121,6 @@ class SearchPage extends Component { }); } - if (this.props.isSearchingForReports) { - sections.push({ - data: [{loadingRow: true}], - shouldShow: true, - indexOffset, - }); - } return sections; } @@ -208,7 +201,7 @@ class SearchPage extends Component { onLayout={this.searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus - shouldShowHeaderMessage={!this.props.isSearchingForReports} + isLoadingNewOptions={this.props.isSearchingForReports} /> From 4e6826a7faf852647d6c4fbd624d880f5f2dce9e Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 4 Oct 2023 11:51:14 -1000 Subject: [PATCH 111/205] Undo skeleton row changes --- src/components/OptionsList/BaseOptionsList.js | 1 - src/components/OptionsListSkeletonRow.js | 48 ------------------- src/components/OptionsListSkeletonView.js | 33 +++++++++++-- 3 files changed, 28 insertions(+), 54 deletions(-) delete mode 100644 src/components/OptionsListSkeletonRow.js diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js index 358b4a8c77e1..9fa25be0868a 100644 --- a/src/components/OptionsList/BaseOptionsList.js +++ b/src/components/OptionsList/BaseOptionsList.js @@ -10,7 +10,6 @@ import Text from '../Text'; import {propTypes as optionsListPropTypes, defaultProps as optionsListDefaultProps} from './optionsListPropTypes'; import OptionsListSkeletonView from '../OptionsListSkeletonView'; import usePrevious from '../../hooks/usePrevious'; -import OptionsListSkeletonRow from '../OptionsListSkeletonRow'; const propTypes = { /** Determines whether the keyboard gets dismissed in response to a drag */ diff --git a/src/components/OptionsListSkeletonRow.js b/src/components/OptionsListSkeletonRow.js deleted file mode 100644 index d7968ef1ba80..000000000000 --- a/src/components/OptionsListSkeletonRow.js +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import {Rect, Circle} from 'react-native-svg'; -import SkeletonViewContentLoader from 'react-content-loader/native'; -import themeColors from '../styles/themes/default'; -import CONST from '../CONST'; -import styles from '../styles/styles'; - -const propTypes = { - /** Whether to animate the skeleton view */ - shouldAnimate: PropTypes.bool.isRequired, - - /** Line width string */ - lineWidth: PropTypes.string.isRequired, -}; - -function OptionsListSkeletonRow({lineWidth, shouldAnimate}) { - return ( - - - - - - ); -} - -OptionsListSkeletonRow.propTypes = propTypes; -export default OptionsListSkeletonRow; diff --git a/src/components/OptionsListSkeletonView.js b/src/components/OptionsListSkeletonView.js index 833cbc0f372e..15c66affe84d 100644 --- a/src/components/OptionsListSkeletonView.js +++ b/src/components/OptionsListSkeletonView.js @@ -1,9 +1,11 @@ import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; +import {Rect, Circle} from 'react-native-svg'; +import SkeletonViewContentLoader from 'react-content-loader/native'; import CONST from '../CONST'; +import themeColors from '../styles/themes/default'; import styles from '../styles/styles'; -import OptionsListSkeletonRow from './OptionsListSkeletonRow'; const propTypes = { /** Whether to animate the skeleton view */ @@ -54,11 +56,32 @@ class OptionsListSkeletonView extends React.Component { lineWidth = '25%'; } skeletonViewItems.push( - , + animate={this.props.shouldAnimate} + height={CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT} + backgroundColor={themeColors.skeletonLHNIn} + foregroundColor={themeColors.skeletonLHNOut} + style={styles.mr5} + > + + + + , ); } From 96400edcbe72105e0e4b948673864296161efa04 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 4 Oct 2023 13:08:18 -1000 Subject: [PATCH 112/205] run prettier --- src/components/TextInput/BaseTextInput.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 28baed106b1c..826e9ed5e957 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -369,7 +369,11 @@ function BaseTextInput(props) { dataSet={{submitOnEnter: isMultiline && props.submitOnEnter}} /> {props.isLoading && ( - + )} {Boolean(props.secureTextEntry) && ( Date: Thu, 5 Oct 2023 10:49:12 +0100 Subject: [PATCH 113/205] Update removing-users.md title description --- .../manage-employees-and-report-approvals/removing-users.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/manage-employees-and-report-approvals/removing-users.md b/docs/articles/expensify-classic/manage-employees-and-report-approvals/removing-users.md index 0db950680aa8..76ebba9ef76b 100644 --- a/docs/articles/expensify-classic/manage-employees-and-report-approvals/removing-users.md +++ b/docs/articles/expensify-classic/manage-employees-and-report-approvals/removing-users.md @@ -1,6 +1,6 @@ --- -title: Coming Soon -description: Coming Soon +title: Remove a Workspace Member +description: How to remove a member from a Workspace in Expensify --- Removing a member from a workspace disables their ability to use the workspace. Please note that it does not delete their account or deactivate the Expensify Card. From 15f94926fe2729291c27d9a8cb16412fd209c12a Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Thu, 5 Oct 2023 10:43:15 -1000 Subject: [PATCH 114/205] Call OpenApp when the priorityMode changes --- src/libs/actions/App.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index f2017a982fb0..d1a8db889f81 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -209,6 +209,18 @@ function openApp() { }); } +let priorityMode; +Onyx.connect({ + key: ONYXKEYS.NVP_PRIORITY_MODE, + callback: (nextPriorityMode) => { + // When someone switches their priority mode we need to fetch all their chats. This is only possible via the OpenApp command. + if (nextPriorityMode === CONST.PRIORITY_MODE.DEFAULT && priorityMode === CONST.PRIORITY_MODE.GSD) { + openApp(); + } + priorityMode = nextPriorityMode; + }, +}); + /** * Fetches data when the app reconnects to the network * @param {Number} [updateIDFrom] the ID of the Onyx update that we want to start fetching from From d28fcfce884f108ee6f21874e16fd19435da4c20 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 5 Oct 2023 16:04:01 -0700 Subject: [PATCH 115/205] Clean up after review feedback --- src/components/Attachments/AttachmentView/index.js | 2 +- src/components/DistanceEReceipt.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 54b7764e6ac1..a1b07fb99dd8 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -55,7 +55,7 @@ const defaultProps = { onToggleKeyboard: () => {}, containerStyles: [], isWorkspaceAvatar: false, - transactionID: 0, + transactionID: '', }; function AttachmentView({ diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index ee725ea54f67..3a64a987c307 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -31,7 +31,7 @@ const defaultProps = { function DistanceEReceipt({transaction}) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const {thumbnail} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename); + const {thumbnail} = TransactionUtils.hasReceipt(transaction) ? ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename) : {}; const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); const formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : translate('common.tbd'); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); From 93687c93bc94e80a1c4b135ed94a78d7e3fc22e4 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 5 Oct 2023 16:17:16 -0700 Subject: [PATCH 116/205] Show distance amount as TBD on request preview --- src/components/ReportActionItem/MoneyRequestPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 35215cadd15d..e66fce5dd757 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -212,7 +212,7 @@ function MoneyRequestPreview(props) { const getDisplayAmountText = () => { if (isDistanceRequest) { - return CurrencyUtils.convertToDisplayString(TransactionUtils.getAmount(props.transaction), props.transaction.currency); + return requestAmount ? CurrencyUtils.convertToDisplayString(requestAmount, props.transaction.currency) : props.translate('common.tbd'); } if (isScanning) { From 94fc6d787278b26fc10f7de41bf7b918b35ac34c Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 5 Oct 2023 16:40:59 -0700 Subject: [PATCH 117/205] Use TBD amount for distance request view --- src/components/ReportActionItem/MoneyRequestView.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 079bc64d96bf..5e93884205b5 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -93,10 +93,15 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should } = ReportUtils.getTransactionDetails(transaction); const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.UNKNOWN_MERCHANT || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; - const formattedTransactionAmount = transactionAmount && transactionCurrency && CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); + const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); + let formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : ''; + if (isDistanceRequest && !formattedTransactionAmount) { + formattedTransactionAmount = translate('common.tbd'); + } const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); const canEdit = ReportUtils.canEditMoneyRequest(parentReportAction); + // A flag for verifying that the current report is a sub-report of a workspace chat const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]); @@ -130,7 +135,6 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should hasErrors = canEdit && TransactionUtils.hasMissingSmartscanFields(transaction); } - const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); const pendingAction = lodashGet(transaction, 'pendingAction'); const getPendingFieldAction = (fieldPath) => lodashGet(transaction, fieldPath) || pendingAction; From d37453960b43e86de14cc69b175811d3eca6fa12 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 5 Oct 2023 16:41:49 -0700 Subject: [PATCH 118/205] Remove cash from distance amount field description --- src/components/ReportActionItem/MoneyRequestView.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 5e93884205b5..988c61f0aad4 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -114,7 +114,10 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should const shouldShowTag = isPolicyExpenseChat && Permissions.canUseTags(betas) && (transactionTag || OptionsListUtils.hasEnabledOptions(lodashValues(policyTagsList))); const shouldShowBillable = isPolicyExpenseChat && Permissions.canUseTags(betas) && (transactionBillable || !lodashGet(policy, 'disabledFields.defaultBillable', true)); - let description = `${translate('iou.amount')} • ${translate('iou.cash')}`; + let description = `${translate('iou.amount')}`; + if (!isDistanceRequest) { + description += ` • ${translate('iou.cash')}`; + } if (isSettled) { description += ` • ${translate('iou.settledExpensify')}`; } else if (report.isWaitingOnBankAccount) { From 32c88f232229ec62f080145d3d800f915c3b2550 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 6 Oct 2023 18:00:46 +0700 Subject: [PATCH 119/205] add DateTimeFormat polyfills --- src/libs/IntlPolyfill/index.native.ts | 3 ++- src/libs/IntlPolyfill/index.ts | 1 - src/libs/IntlPolyfill/polyfillDateTimeFormat.ts | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/libs/IntlPolyfill/polyfillDateTimeFormat.ts diff --git a/src/libs/IntlPolyfill/index.native.ts b/src/libs/IntlPolyfill/index.native.ts index 9e578558faed..d81b9d90cbe8 100644 --- a/src/libs/IntlPolyfill/index.native.ts +++ b/src/libs/IntlPolyfill/index.native.ts @@ -1,6 +1,7 @@ import polyfillNumberFormat from './polyfillNumberFormat'; import polyfillListFormat from './polyfillListFormat'; import IntlPolyfill from './types'; +import polyfillDateTimeFormat from './polyfillDateTimeFormat'; /** * Polyfill the Intl API, always performed for native devices. @@ -10,8 +11,8 @@ const intlPolyfill: IntlPolyfill = () => { require('@formatjs/intl-getcanonicallocales/polyfill'); require('@formatjs/intl-locale/polyfill'); require('@formatjs/intl-pluralrules/polyfill'); - require('@formatjs/intl-datetimeformat'); polyfillNumberFormat(); + polyfillDateTimeFormat(); polyfillListFormat(); }; diff --git a/src/libs/IntlPolyfill/index.ts b/src/libs/IntlPolyfill/index.ts index bef12ef093e2..982dc5130d77 100644 --- a/src/libs/IntlPolyfill/index.ts +++ b/src/libs/IntlPolyfill/index.ts @@ -8,6 +8,5 @@ import IntlPolyfill from './types'; const intlPolyfill: IntlPolyfill = () => { // Just need to polyfill Intl.NumberFormat for web based platforms polyfillNumberFormat(); - require('@formatjs/intl-datetimeformat'); }; export default intlPolyfill; diff --git a/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts b/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts new file mode 100644 index 000000000000..460f65c8f636 --- /dev/null +++ b/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts @@ -0,0 +1,6 @@ +export default function () { + require('@formatjs/intl-datetimeformat/polyfill-force'); + require('@formatjs/intl-datetimeformat/add-all-tz'); + require('@formatjs/intl-datetimeformat/locale-data/en'); + require('@formatjs/intl-datetimeformat/locale-data/es'); +} From c0e74b9ea020a0792fe35282039126f3c36bfd45 Mon Sep 17 00:00:00 2001 From: dukenv0307 <129500732+dukenv0307@users.noreply.github.com> Date: Fri, 6 Oct 2023 20:09:35 +0700 Subject: [PATCH 120/205] Update src/libs/actions/BankAccounts.js Co-authored-by: Florent De'Neve --- src/libs/actions/BankAccounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index be71082b7592..a43544c6a5bc 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -57,7 +57,7 @@ function clearOnfidoToken() { /** * Helper method to build the Onyx data required during setup of a Verified Business Bank Account - * @param {String | undefined} currentStep The step that we need to update the data from backend to draft value + * @param {String | undefined} currentStep The name of the bank account setup step for which we will update the draft value when we receive the response from the API. * @returns {Object} */ function getVBBADataForOnyx(currentStep = undefined) { From 0f489623c8ec354b600e6216d593c948aa38a2d8 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 6 Oct 2023 20:18:47 +0700 Subject: [PATCH 121/205] rename the variable --- src/libs/actions/BankAccounts.js | 12 +++++++----- src/libs/actions/ReimbursementAccount/index.js | 2 +- .../ReimbursementAccount/ReimbursementAccountPage.js | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index a43544c6a5bc..941e175b1132 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -57,7 +57,7 @@ function clearOnfidoToken() { /** * Helper method to build the Onyx data required during setup of a Verified Business Bank Account - * @param {String | undefined} currentStep The name of the bank account setup step for which we will update the draft value when we receive the response from the API. + * @param {String | undefined} currentStep The name of the bank account setup step for which we will update the draft value when we receive the response from the API. * @returns {Object} */ function getVBBADataForOnyx(currentStep = undefined) { @@ -79,10 +79,12 @@ function getVBBADataForOnyx(currentStep = undefined) { value: { isLoading: false, errors: null, - // The value of some fields of the currentStep are changed i.e. being trimmed or phone number is parsed when backend returns the data - // So we need to add this field in successData to update the current data from reimbursement account to the draft value of the form - // if currentStep is undefined that means the step doesn't need update to the draft - stepToUpdateToDraft: currentStep, + // When setting up a bank account, we save the draft form values in Onyx. + // When we update the information for a step, the value of some fields that are returned from the API + // can be different from the value that we stored as the draft in Onyx (i.e. the phone number is formatted). + // This is why we store the current step used to call the API in order to update the corresponding draft data in Onyx. + // If currentStep is undefined that means this step don't need to update the data of the draft in Onyx. + draftStep: currentStep, }, }, ], diff --git a/src/libs/actions/ReimbursementAccount/index.js b/src/libs/actions/ReimbursementAccount/index.js index 4471d19c8151..68774d0ba8b0 100644 --- a/src/libs/actions/ReimbursementAccount/index.js +++ b/src/libs/actions/ReimbursementAccount/index.js @@ -31,7 +31,7 @@ function setWorkspaceIDForReimbursementAccount(workspaceID) { */ function updateReimbursementAccountDraft(bankAccountData) { Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT, bankAccountData); - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {stepToUpdateToDraft: undefined}); + Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {draftStep: undefined}); } /** diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index f09963129bb1..70717be477c2 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -158,9 +158,9 @@ class ReimbursementAccountPage extends React.Component { } // Update the data that is returned from back-end to draft value - const stepToUpdateToDraft = this.props.reimbursementAccount.stepToUpdateToDraft; - if (stepToUpdateToDraft) { - BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(stepToUpdateToDraft))); + const draftStep = this.props.reimbursementAccount.draftStep; + if (draftStep) { + BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(draftStep))); } const currentStepRouteParam = this.getStepToOpenFromRouteParams(); From 94411aedd97d85c94a9a01953a22c217e88f774b Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 6 Oct 2023 10:16:13 -1000 Subject: [PATCH 122/205] Fix loading when offline --- src/libs/actions/App.js | 25 +++++++++++++------------ src/libs/actions/Report.js | 7 ++++++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index d1a8db889f81..e07845066c6a 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -41,6 +41,19 @@ Onyx.connect({ callback: (val) => (preferredLocale = val), }); +let priorityMode; +Onyx.connect({ + key: ONYXKEYS.NVP_PRIORITY_MODE, + callback: (nextPriorityMode) => { + // When someone switches their priority mode we need to fetch all their chats. This is only possible via the OpenApp command. + if (nextPriorityMode === CONST.PRIORITY_MODE.DEFAULT && priorityMode === CONST.PRIORITY_MODE.GSD) { + // eslint-disable-next-line no-use-before-define + openApp(); + } + priorityMode = nextPriorityMode; + }, +}); + let resolveIsReadyPromise; const isReadyToOpenApp = new Promise((resolve) => { resolveIsReadyPromise = resolve; @@ -209,18 +222,6 @@ function openApp() { }); } -let priorityMode; -Onyx.connect({ - key: ONYXKEYS.NVP_PRIORITY_MODE, - callback: (nextPriorityMode) => { - // When someone switches their priority mode we need to fetch all their chats. This is only possible via the OpenApp command. - if (nextPriorityMode === CONST.PRIORITY_MODE.DEFAULT && priorityMode === CONST.PRIORITY_MODE.GSD) { - openApp(); - } - priorityMode = nextPriorityMode; - }, -}); - /** * Fetches data when the app reconnects to the network * @param {Number} [updateIDFrom] the ID of the Onyx update that we want to start fetching from diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 659479253ae5..e0d325bdb103 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2205,12 +2205,17 @@ function searchForReports(searchInput) { * @private * @param {string} searchInput */ -const debouncedSearchInServer = lodashDebounce(searchForReports, 300, {leading: false}); +const debouncedSearchInServer = lodashDebounce(searchForReports, CONST.TIMING.SEARCH_FOR_REPORTS_DEBOUNCE_TIME, {leading: false}); /** * @param {string} searchInput */ function searchInServer(searchInput) { + if (isNetworkOffline) { + Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, false); + return; + } + // Why not set this in optimistic data? It won't run until the API request happens and while the API request is debounced // we want to show the loading state right away. Otherwise, we will see a flashing UI where the client options are sorted and // tell the user there are no options, then we start searching, and tell them there are no options again. From 3816569e71d305d6c5a52a2894a4ba9c4615b200 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 6 Oct 2023 10:16:35 -1000 Subject: [PATCH 123/205] Add constant for debounce time --- src/CONST.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.ts b/src/CONST.ts index 0a262d868de9..9eaf77ed6fad 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -664,6 +664,7 @@ const CONST = { TOOLTIP_SENSE: 1000, TRIE_INITIALIZATION: 'trie_initialization', COMMENT_LENGTH_DEBOUNCE_TIME: 500, + SEARCH_FOR_REPORTS_DEBOUNCE_TIME: 300, }, PRIORITY_MODE: { GSD: 'gsd', From 919714362d1d0fbaab9dd22d9908e3317e4e84a9 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 9 Oct 2023 10:00:02 +0700 Subject: [PATCH 124/205] fix: 29060 --- src/libs/actions/Report.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index ab2ac7fb0ca2..dbf6be434154 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -26,7 +26,7 @@ import * as UserUtils from '../UserUtils'; import * as Welcome from './Welcome'; import * as PersonalDetailsUtils from '../PersonalDetailsUtils'; import * as Environment from '../Environment/Environment'; -import * as Session from './Session'; +import SidebarUtils from '../../libs/SidebarUtils'; let currentUserAccountID; Onyx.connect({ @@ -1882,7 +1882,7 @@ function openReportFromDeepLink(url, isAuthenticated) { // Navigate to the report after sign-in/sign-up. InteractionManager.runAfterInteractions(() => { - Session.waitForUserSignIn().then(() => { + SidebarUtils.isSidebarLoadedReady().then(() => { if (route === ROUTES.CONCIERGE) { navigateToConciergeChat(); return; From c4543843f62ac5e828698d7f9a83369906b9538e Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 9 Oct 2023 12:24:56 +0700 Subject: [PATCH 125/205] set default timezone --- src/libs/IntlPolyfill/index.ts | 3 +- .../IntlPolyfill/polyfillDateTimeFormat.ts | 48 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/libs/IntlPolyfill/index.ts b/src/libs/IntlPolyfill/index.ts index 982dc5130d77..185f292c3969 100644 --- a/src/libs/IntlPolyfill/index.ts +++ b/src/libs/IntlPolyfill/index.ts @@ -1,12 +1,13 @@ import polyfillNumberFormat from './polyfillNumberFormat'; import IntlPolyfill from './types'; +import polyfillDateTimeFormat from "./polyfillDateTimeFormat"; /** * Polyfill the Intl API if the ICU version is old. * This ensures that the currency data is consistent across platforms and browsers. */ const intlPolyfill: IntlPolyfill = () => { - // Just need to polyfill Intl.NumberFormat for web based platforms polyfillNumberFormat(); + polyfillDateTimeFormat(); }; export default intlPolyfill; diff --git a/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts b/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts index 460f65c8f636..cce78f58d4ac 100644 --- a/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts +++ b/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts @@ -1,6 +1,52 @@ +import Onyx from "react-native-onyx"; +import ONYXKEYS from "../../ONYXKEYS"; +import {Timezone} from "../../types/onyx/PersonalDetails"; +import CONST from "../../CONST"; + +let currentUserAccountID: number | undefined; +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (val) => { + // When signed out, val is undefined + if (!val) { + return; + } + + currentUserAccountID = val.accountID; + }, +}); + +let timezone: Required = CONST.DEFAULT_TIME_ZONE; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (value) => { + if (!currentUserAccountID) { + return; + } + + const personalDetailsTimezone = value?.[currentUserAccountID]?.timezone; + + timezone = { + selected: personalDetailsTimezone?.selected ?? CONST.DEFAULT_TIME_ZONE.selected, + automatic: personalDetailsTimezone?.automatic ?? CONST.DEFAULT_TIME_ZONE.automatic, + }; + }, +}); + export default function () { + // Because JS Engines do not expose default timezone, the polyfill cannot detect local timezone that a browser is in + // We must manually do this by getting the local timezone before adding polyfill + const currentTimezone = timezone.automatic ? Intl.DateTimeFormat().resolvedOptions().timeZone : timezone.selected; + require('@formatjs/intl-datetimeformat/polyfill-force'); - require('@formatjs/intl-datetimeformat/add-all-tz'); require('@formatjs/intl-datetimeformat/locale-data/en'); require('@formatjs/intl-datetimeformat/locale-data/es'); + require('@formatjs/intl-datetimeformat/add-all-tz'); + + if ('__setDefaultTimeZone' in Intl.DateTimeFormat) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line no-underscore-dangle + Intl.DateTimeFormat.__setDefaultTimeZone(currentTimezone); + } } From 2dd92d30f3b4b7cc77a0b57aac1a1fcccce6eb38 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 9 Oct 2023 13:29:39 +0700 Subject: [PATCH 126/205] Revert "fix: 29060" This reverts commit 919714362d1d0fbaab9dd22d9908e3317e4e84a9. --- src/libs/actions/Report.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index dbf6be434154..ab2ac7fb0ca2 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -26,7 +26,7 @@ import * as UserUtils from '../UserUtils'; import * as Welcome from './Welcome'; import * as PersonalDetailsUtils from '../PersonalDetailsUtils'; import * as Environment from '../Environment/Environment'; -import SidebarUtils from '../../libs/SidebarUtils'; +import * as Session from './Session'; let currentUserAccountID; Onyx.connect({ @@ -1882,7 +1882,7 @@ function openReportFromDeepLink(url, isAuthenticated) { // Navigate to the report after sign-in/sign-up. InteractionManager.runAfterInteractions(() => { - SidebarUtils.isSidebarLoadedReady().then(() => { + Session.waitForUserSignIn().then(() => { if (route === ROUTES.CONCIERGE) { navigateToConciergeChat(); return; From 1de6fb3527f6208794bc5be811377c8bbc802286 Mon Sep 17 00:00:00 2001 From: Ayaz Alavi Date: Mon, 9 Oct 2023 12:21:21 +0500 Subject: [PATCH 127/205] added composer focus glitch fix Signed-off-by: Ayaz Alavi --- src/ONYXKEYS.ts | 4 ++ src/libs/actions/InputFocus.ts | 21 +++++++++++ .../ComposerWithSuggestions.js | 12 +++++- .../report/ReportActionItemMessageEdit.js | 37 +++++++++++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/libs/actions/InputFocus.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0a17d3a1d2f7..6d983e77cb81 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -47,6 +47,9 @@ const ONYXKEYS = { // draft status CUSTOM_STATUS_DRAFT: 'customStatusDraft', + // keep edit message focus state + INPUT_FOCUSED: 'inputFocused', + /** Contains all the personalDetails the user has access to, keyed by accountID */ PERSONAL_DETAILS_LIST: 'personalDetailsList', @@ -312,6 +315,7 @@ type OnyxValues = { [ONYXKEYS.MODAL]: OnyxTypes.Modal; [ONYXKEYS.NETWORK]: OnyxTypes.Network; [ONYXKEYS.CUSTOM_STATUS_DRAFT]: OnyxTypes.CustomStatusDraft; + [ONYXKEYS.INPUT_FOCUSED]: boolean; [ONYXKEYS.PERSONAL_DETAILS_LIST]: Record; [ONYXKEYS.PRIVATE_PERSONAL_DETAILS]: OnyxTypes.PrivatePersonalDetails; [ONYXKEYS.TASK]: OnyxTypes.Task; diff --git a/src/libs/actions/InputFocus.ts b/src/libs/actions/InputFocus.ts new file mode 100644 index 000000000000..bf21b068fd23 --- /dev/null +++ b/src/libs/actions/InputFocus.ts @@ -0,0 +1,21 @@ +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '../../ONYXKEYS'; +import _ from 'lodash'; + +let refSave: HTMLElement | undefined; +export function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: any, onyxFocused: boolean) { + if (isFocused && !onyxFocused) { + inputFocusChange(true); + ref.focus(); + } + if (isFocused) { + refSave = ref; + } + if (!isFocused && !onyxFocused && !modal.willAlertModalBecomeVisible && !modal.isVisible && refSave) { + refSave.focus(); + } +} + +export function inputFocusChange(focus: boolean) { + Onyx.set(ONYXKEYS.INPUT_FOCUSED, focus); +} diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index a4777556dda7..10d420b9a356 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -33,6 +33,7 @@ import withKeyboardState from '../../../../components/withKeyboardState'; import {propTypes, defaultProps} from './composerWithSuggestionsProps'; import focusWithDelay from '../../../../libs/focusWithDelay'; import useDebounce from '../../../../hooks/useDebounce'; +import * as InputFocus from '../../../../libs/actions/InputFocus'; const {RNTextInputReset} = NativeModules; @@ -98,6 +99,7 @@ function ComposerWithSuggestions({ animatedRef, forwardedRef, isNextModalWillOpenRef, + editFocused, }) { const {preferredLocale} = useLocalize(); const isFocused = useIsFocused(); @@ -478,9 +480,12 @@ function ComposerWithSuggestions({ return; } + if (editFocused) { + InputFocus.inputFocusChange(false); + return; + } focus(); - }, [focus, prevIsFocused, prevIsModalVisible, isFocused, modal.isVisible, isNextModalWillOpenRef]); - + }, [focus, prevIsFocused, editFocused, prevIsModalVisible, isFocused, modal.isVisible, isNextModalWillOpenRef]); useEffect(() => { if (value.length === 0) { return; @@ -591,6 +596,9 @@ export default compose( key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, selector: EmojiUtils.getPreferredSkinToneIndex, }, + editFocused: { + key: ONYXKEYS.INPUT_FOCUSED, + }, parentReportActions: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, canEvict: false, diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index 24ea8c59f545..bea18a68809d 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -37,6 +37,9 @@ import useReportScrollManager from '../../../hooks/useReportScrollManager'; import * as EmojiPickerAction from '../../../libs/actions/EmojiPickerAction'; import focusWithDelay from '../../../libs/focusWithDelay'; import * as Browser from '../../../libs/Browser'; +import * as InputFocus from '../../../libs/actions/InputFocus'; +import onyxSubscribe from '../../../libs/onyxSubscribe'; +import ONYXKEYS from '../../../ONYXKEYS'; const propTypes = { /** All the data of the action */ @@ -107,6 +110,8 @@ function ReportActionItemMessageEdit(props) { const [selection, setSelection] = useState(getInitialSelection()); const [isFocused, setIsFocused] = useState(false); const [hasExceededMaxCommentLength, setHasExceededMaxCommentLength] = useState(false); + const [modal, setModal] = useState(false); + const [onyxFocused, setOnyxFocused] = useState(false); const textInputRef = useRef(null); const isFocusedRef = useRef(false); @@ -124,6 +129,36 @@ function ReportActionItemMessageEdit(props) { isFocusedRef.current = isFocused; }, [isFocused]); + useEffect(() => { + InputFocus.composerFocusKeepFocusOn(textInputRef.current, isFocused, modal, onyxFocused); + }, [isFocused, modal, onyxFocused]); + + useEffect(() => { + const unsubscribeOnyxModal = onyxSubscribe({ + key: ONYXKEYS.MODAL, + callback: (modalArg) => { + if (_.isNull(modalArg)) { + return; + } + setModal(modalArg); + }, + }); + + const unsubscribeOnyxFocused = onyxSubscribe({ + key: ONYXKEYS.INPUT_FOCUSED, + callback: (modalArg) => { + if (_.isNull(modalArg)) { + return; + } + setOnyxFocused(modalArg); + }, + }); + return () => { + unsubscribeOnyxModal(); + unsubscribeOnyxFocused(); + }; + }, []); + // We consider the report action active if it's focused, its emoji picker is open or its context menu is open const isActive = useCallback( () => isFocusedRef.current || EmojiPickerAction.isActive(props.action.reportActionID) || ReportActionContextMenu.isActiveReportAction(props.action.reportActionID), @@ -230,6 +265,8 @@ function ReportActionItemMessageEdit(props) { * Delete the draft of the comment being edited. This will take the comment out of "edit mode" with the old content. */ const deleteDraft = useCallback(() => { + setIsFocused(false); + InputFocus.inputFocusChange(false); debouncedSaveDraft.cancel(); Report.saveReportActionDraft(props.reportID, props.action, ''); From 64cd3037bb680446ffeac1b7e36b6466111bd5fa Mon Sep 17 00:00:00 2001 From: Ayaz Alavi Date: Mon, 9 Oct 2023 12:45:20 +0500 Subject: [PATCH 128/205] linting fixes Signed-off-by: Ayaz Alavi --- src/libs/actions/InputFocus.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/InputFocus.ts b/src/libs/actions/InputFocus.ts index bf21b068fd23..45dfd89dfd2b 100644 --- a/src/libs/actions/InputFocus.ts +++ b/src/libs/actions/InputFocus.ts @@ -1,9 +1,12 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; -import _ from 'lodash'; + +function inputFocusChange(focus: boolean) { + Onyx.set(ONYXKEYS.INPUT_FOCUSED, focus); +} let refSave: HTMLElement | undefined; -export function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: any, onyxFocused: boolean) { +function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: {willAlertModalBecomeVisible: boolean, isVisible: boolean}, onyxFocused: boolean) { if (isFocused && !onyxFocused) { inputFocusChange(true); ref.focus(); @@ -16,6 +19,5 @@ export function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, m } } -export function inputFocusChange(focus: boolean) { - Onyx.set(ONYXKEYS.INPUT_FOCUSED, focus); -} + +export { composerFocusKeepFocusOn, inputFocusChange } From 36a4a3d33ffb107f22d25bb0cb099883cbb991ec Mon Sep 17 00:00:00 2001 From: Ayaz Alavi Date: Mon, 9 Oct 2023 13:18:50 +0500 Subject: [PATCH 129/205] add browser check Signed-off-by: Ayaz Alavi --- .../home/report/ReportActionItemMessageEdit.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index bea18a68809d..cedba9be0a2a 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -1,6 +1,6 @@ import lodashGet from 'lodash/get'; import React, {useState, useRef, useMemo, useEffect, useCallback} from 'react'; -import {InteractionManager, Keyboard, View} from 'react-native'; +import {InteractionManager, Keyboard, Platform, View} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; @@ -130,7 +130,10 @@ function ReportActionItemMessageEdit(props) { }, [isFocused]); useEffect(() => { - InputFocus.composerFocusKeepFocusOn(textInputRef.current, isFocused, modal, onyxFocused); + if (Platform.OS === "web" && !Browser.isMobile()) { + InputFocus.composerFocusKeepFocusOn(textInputRef.current, isFocused, modal, onyxFocused); + } + return ()=>{} }, [isFocused, modal, onyxFocused]); useEffect(() => { @@ -265,8 +268,10 @@ function ReportActionItemMessageEdit(props) { * Delete the draft of the comment being edited. This will take the comment out of "edit mode" with the old content. */ const deleteDraft = useCallback(() => { - setIsFocused(false); - InputFocus.inputFocusChange(false); + if (Platform.OS === "web" && !Browser.isMobile()) { + setIsFocused(false); + InputFocus.inputFocusChange(false); + } debouncedSaveDraft.cancel(); Report.saveReportActionDraft(props.reportID, props.action, ''); From 8d692b0bd807990ed2b5a4b4e2ea45f82f810fa6 Mon Sep 17 00:00:00 2001 From: dukenv0307 <129500732+dukenv0307@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:07:07 +0700 Subject: [PATCH 130/205] Update src/pages/ReimbursementAccount/ReimbursementAccountPage.js Co-authored-by: Florent De'Neve --- src/pages/ReimbursementAccount/ReimbursementAccountPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index 70717be477c2..b5288879e07f 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -183,7 +183,7 @@ class ReimbursementAccountPage extends React.Component { Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(this.getRouteForCurrentStep(currentStep), policyId, backTo)); } - getFieldsOfCurrentStep(currentStep) { + getFieldsForStep(step) { switch (currentStep) { case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: return ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']; From 26e95528e495666a4195b0dc47b9132b799c6484 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 9 Oct 2023 16:13:46 +0700 Subject: [PATCH 131/205] fix lint --- src/pages/ReimbursementAccount/ReimbursementAccountPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index b5288879e07f..89a8b3f36338 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -160,7 +160,7 @@ class ReimbursementAccountPage extends React.Component { // Update the data that is returned from back-end to draft value const draftStep = this.props.reimbursementAccount.draftStep; if (draftStep) { - BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsOfCurrentStep(draftStep))); + BankAccounts.updateReimbursementAccountDraft(this.getBankAccountFields(this.getFieldsForStep(draftStep))); } const currentStepRouteParam = this.getStepToOpenFromRouteParams(); @@ -184,7 +184,7 @@ class ReimbursementAccountPage extends React.Component { } getFieldsForStep(step) { - switch (currentStep) { + switch (step) { case CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT: return ['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']; case CONST.BANK_ACCOUNT.STEP.COMPANY: From 88dcfbda3d79e36abb965cdd36bcbf2fd16b4aa6 Mon Sep 17 00:00:00 2001 From: Ayaz Alavi Date: Mon, 9 Oct 2023 14:56:39 +0500 Subject: [PATCH 132/205] added prettier fixes Signed-off-by: Ayaz Alavi --- src/libs/actions/InputFocus.ts | 5 ++--- src/pages/home/report/ReportActionItemMessageEdit.js | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/InputFocus.ts b/src/libs/actions/InputFocus.ts index 45dfd89dfd2b..d530202c7b24 100644 --- a/src/libs/actions/InputFocus.ts +++ b/src/libs/actions/InputFocus.ts @@ -6,7 +6,7 @@ function inputFocusChange(focus: boolean) { } let refSave: HTMLElement | undefined; -function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: {willAlertModalBecomeVisible: boolean, isVisible: boolean}, onyxFocused: boolean) { +function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: {willAlertModalBecomeVisible: boolean; isVisible: boolean}, onyxFocused: boolean) { if (isFocused && !onyxFocused) { inputFocusChange(true); ref.focus(); @@ -19,5 +19,4 @@ function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: { } } - -export { composerFocusKeepFocusOn, inputFocusChange } +export {composerFocusKeepFocusOn, inputFocusChange}; diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index cedba9be0a2a..ffe89a3c00ab 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -130,10 +130,10 @@ function ReportActionItemMessageEdit(props) { }, [isFocused]); useEffect(() => { - if (Platform.OS === "web" && !Browser.isMobile()) { + if (Platform.OS === 'web' && !Browser.isMobile()) { InputFocus.composerFocusKeepFocusOn(textInputRef.current, isFocused, modal, onyxFocused); } - return ()=>{} + return () => {}; }, [isFocused, modal, onyxFocused]); useEffect(() => { @@ -268,10 +268,10 @@ function ReportActionItemMessageEdit(props) { * Delete the draft of the comment being edited. This will take the comment out of "edit mode" with the old content. */ const deleteDraft = useCallback(() => { - if (Platform.OS === "web" && !Browser.isMobile()) { + if (Platform.OS === 'web' && !Browser.isMobile()) { setIsFocused(false); InputFocus.inputFocusChange(false); - } + } debouncedSaveDraft.cancel(); Report.saveReportActionDraft(props.reportID, props.action, ''); From a8d62d0fa3d432e2016d653b791e71b9ba004ad3 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 9 Oct 2023 14:16:53 +0200 Subject: [PATCH 133/205] fix: added proper color to receiptDropUIBG --- src/styles/themes/light.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index c2b42e67005f..d6b864e81567 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -61,7 +61,7 @@ const lightTheme = { heroCard: colors.blue400, uploadPreviewActivityIndicator: colors.lightHighlightBackground, dropUIBG: 'rgba(252, 251, 249, 0.92)', - receiptDropUIBG: '', // TODO: add color + receiptDropUIBG: 'rgba(3, 212, 124, 0.84)', checkBox: colors.green400, pickerOptionsTextColor: colors.lightPrimaryText, imageCropBackgroundColor: colors.lightIcons, From 06d8fc3e69386aa7371980e3eca0167cb01c3cff Mon Sep 17 00:00:00 2001 From: Ayaz Alavi Date: Mon, 9 Oct 2023 18:40:06 +0500 Subject: [PATCH 134/205] added browser files Signed-off-by: Ayaz Alavi --- src/libs/actions/InputFocus/index.ts | 5 +++++ .../{InputFocus.ts => InputFocus/index.website.ts} | 13 +++++++++++-- .../home/report/ReportActionItemMessageEdit.js | 12 ++++-------- 3 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 src/libs/actions/InputFocus/index.ts rename src/libs/actions/{InputFocus.ts => InputFocus/index.website.ts} (65%) diff --git a/src/libs/actions/InputFocus/index.ts b/src/libs/actions/InputFocus/index.ts new file mode 100644 index 000000000000..1840b0625626 --- /dev/null +++ b/src/libs/actions/InputFocus/index.ts @@ -0,0 +1,5 @@ +function inputFocusChange() {} +function composerFocusKeepFocusOn() {} +const callback = () => {}; + +export {composerFocusKeepFocusOn, inputFocusChange, callback}; diff --git a/src/libs/actions/InputFocus.ts b/src/libs/actions/InputFocus/index.website.ts similarity index 65% rename from src/libs/actions/InputFocus.ts rename to src/libs/actions/InputFocus/index.website.ts index d530202c7b24..4472191d5a2d 100644 --- a/src/libs/actions/InputFocus.ts +++ b/src/libs/actions/InputFocus/index.website.ts @@ -1,12 +1,19 @@ import Onyx from 'react-native-onyx'; -import ONYXKEYS from '../../ONYXKEYS'; +import ONYXKEYS from '../../../ONYXKEYS'; +import * as Browser from '../../Browser'; function inputFocusChange(focus: boolean) { + if (Browser.isMobile()) { + return; + } Onyx.set(ONYXKEYS.INPUT_FOCUSED, focus); } let refSave: HTMLElement | undefined; function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: {willAlertModalBecomeVisible: boolean; isVisible: boolean}, onyxFocused: boolean) { + if (Browser.isMobile()) { + return; + } if (isFocused && !onyxFocused) { inputFocusChange(true); ref.focus(); @@ -19,4 +26,6 @@ function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: { } } -export {composerFocusKeepFocusOn, inputFocusChange}; +const callback = (method: () => void) => !Browser.isMobile() && method(); + +export {composerFocusKeepFocusOn, inputFocusChange, callback}; diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index ffe89a3c00ab..c0a0e8fbee84 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -1,6 +1,6 @@ import lodashGet from 'lodash/get'; import React, {useState, useRef, useMemo, useEffect, useCallback} from 'react'; -import {InteractionManager, Keyboard, Platform, View} from 'react-native'; +import {InteractionManager, Keyboard, View} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; @@ -130,9 +130,7 @@ function ReportActionItemMessageEdit(props) { }, [isFocused]); useEffect(() => { - if (Platform.OS === 'web' && !Browser.isMobile()) { - InputFocus.composerFocusKeepFocusOn(textInputRef.current, isFocused, modal, onyxFocused); - } + InputFocus.composerFocusKeepFocusOn(textInputRef.current, isFocused, modal, onyxFocused); return () => {}; }, [isFocused, modal, onyxFocused]); @@ -268,10 +266,8 @@ function ReportActionItemMessageEdit(props) { * Delete the draft of the comment being edited. This will take the comment out of "edit mode" with the old content. */ const deleteDraft = useCallback(() => { - if (Platform.OS === 'web' && !Browser.isMobile()) { - setIsFocused(false); - InputFocus.inputFocusChange(false); - } + InputFocus.callback(() => setIsFocused(false)); + InputFocus.inputFocusChange(false); debouncedSaveDraft.cancel(); Report.saveReportActionDraft(props.reportID, props.action, ''); From 83dfeb6d859e11f926cab6ff00f284319e85a684 Mon Sep 17 00:00:00 2001 From: Ayaz Alavi Date: Mon, 9 Oct 2023 18:46:40 +0500 Subject: [PATCH 135/205] removed unnecessary return Signed-off-by: Ayaz Alavi --- src/pages/home/report/ReportActionItemMessageEdit.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index c0a0e8fbee84..077695a339ad 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -131,7 +131,6 @@ function ReportActionItemMessageEdit(props) { useEffect(() => { InputFocus.composerFocusKeepFocusOn(textInputRef.current, isFocused, modal, onyxFocused); - return () => {}; }, [isFocused, modal, onyxFocused]); useEffect(() => { From 2cc1e12235e570dd75682eaead05ddcfd098a391 Mon Sep 17 00:00:00 2001 From: Ayaz Alavi Date: Mon, 9 Oct 2023 20:40:35 +0500 Subject: [PATCH 136/205] added desktop file and updated focus manager Signed-off-by: Ayaz Alavi --- src/libs/actions/InputFocus/index.desktop.ts | 30 +++++++++++++++++++ src/libs/actions/InputFocus/index.website.ts | 13 +++++--- .../ComposerWithSuggestions.js | 1 + 3 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 src/libs/actions/InputFocus/index.desktop.ts diff --git a/src/libs/actions/InputFocus/index.desktop.ts b/src/libs/actions/InputFocus/index.desktop.ts new file mode 100644 index 000000000000..f0548cd92725 --- /dev/null +++ b/src/libs/actions/InputFocus/index.desktop.ts @@ -0,0 +1,30 @@ +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '../../../ONYXKEYS'; +import * as Browser from '../../Browser'; +import ReportActionComposeFocusManager from '../../ReportActionComposeFocusManager'; + +function inputFocusChange(focus: boolean) { + Onyx.set(ONYXKEYS.INPUT_FOCUSED, focus); +} + +let refSave: HTMLElement | undefined; +function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: { willAlertModalBecomeVisible: boolean; isVisible: boolean }, onyxFocused: boolean) { + if (isFocused && !onyxFocused) { + inputFocusChange(true); + ref.focus(); + } + if (isFocused) { + refSave = ref; + } + if (!isFocused && !onyxFocused && !modal.willAlertModalBecomeVisible && !modal.isVisible && refSave) { + if (!ReportActionComposeFocusManager.isFocused()) { + refSave.focus(); + } else { + refSave = undefined; + } + } +} + +const callback = (method: () => void) => method(); + +export {composerFocusKeepFocusOn, inputFocusChange, callback}; diff --git a/src/libs/actions/InputFocus/index.website.ts b/src/libs/actions/InputFocus/index.website.ts index 4472191d5a2d..918aad939d72 100644 --- a/src/libs/actions/InputFocus/index.website.ts +++ b/src/libs/actions/InputFocus/index.website.ts @@ -1,6 +1,7 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../../ONYXKEYS'; import * as Browser from '../../Browser'; +import ReportActionComposeFocusManager from '../../ReportActionComposeFocusManager'; function inputFocusChange(focus: boolean) { if (Browser.isMobile()) { @@ -10,19 +11,23 @@ function inputFocusChange(focus: boolean) { } let refSave: HTMLElement | undefined; -function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: {willAlertModalBecomeVisible: boolean; isVisible: boolean}, onyxFocused: boolean) { +function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: { willAlertModalBecomeVisible: boolean; isVisible: boolean }, onyxFocused: boolean) { if (Browser.isMobile()) { return; } if (isFocused && !onyxFocused) { inputFocusChange(true); ref.focus(); - } + } if (isFocused) { refSave = ref; } - if (!isFocused && !onyxFocused && !modal.willAlertModalBecomeVisible && !modal.isVisible && refSave) { - refSave.focus(); + if (!isFocused && !onyxFocused && !modal.willAlertModalBecomeVisible && !modal.isVisible && refSave) { + if (!ReportActionComposeFocusManager.isFocused()) { + refSave.focus(); + } else { + refSave = undefined; + } } } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 10d420b9a356..a61f424f4bdd 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -374,6 +374,7 @@ function ComposerWithSuggestions({ if (!suggestionsRef.current) { return false; } + InputFocus.inputFocusChange(false) return suggestionsRef.current.setShouldBlockSuggestionCalc(false); }, [suggestionsRef]); From a1274917276d8451f80dda40aeadff5587f39c0f Mon Sep 17 00:00:00 2001 From: Ayaz Alavi Date: Mon, 9 Oct 2023 20:47:28 +0500 Subject: [PATCH 137/205] added linting and prettier fix Signed-off-by: Ayaz Alavi --- src/libs/actions/InputFocus/index.desktop.ts | 9 ++++----- src/libs/actions/InputFocus/index.website.ts | 8 ++++---- .../ReportActionCompose/ComposerWithSuggestions.js | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/InputFocus/index.desktop.ts b/src/libs/actions/InputFocus/index.desktop.ts index f0548cd92725..b6cf1aba6138 100644 --- a/src/libs/actions/InputFocus/index.desktop.ts +++ b/src/libs/actions/InputFocus/index.desktop.ts @@ -1,6 +1,5 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../../ONYXKEYS'; -import * as Browser from '../../Browser'; import ReportActionComposeFocusManager from '../../ReportActionComposeFocusManager'; function inputFocusChange(focus: boolean) { @@ -8,18 +7,18 @@ function inputFocusChange(focus: boolean) { } let refSave: HTMLElement | undefined; -function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: { willAlertModalBecomeVisible: boolean; isVisible: boolean }, onyxFocused: boolean) { +function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: {willAlertModalBecomeVisible: boolean; isVisible: boolean}, onyxFocused: boolean) { if (isFocused && !onyxFocused) { inputFocusChange(true); ref.focus(); - } + } if (isFocused) { refSave = ref; } - if (!isFocused && !onyxFocused && !modal.willAlertModalBecomeVisible && !modal.isVisible && refSave) { + if (!isFocused && !onyxFocused && !modal.willAlertModalBecomeVisible && !modal.isVisible && refSave) { if (!ReportActionComposeFocusManager.isFocused()) { refSave.focus(); - } else { + } else { refSave = undefined; } } diff --git a/src/libs/actions/InputFocus/index.website.ts b/src/libs/actions/InputFocus/index.website.ts index 918aad939d72..17cb1fe2f44d 100644 --- a/src/libs/actions/InputFocus/index.website.ts +++ b/src/libs/actions/InputFocus/index.website.ts @@ -11,21 +11,21 @@ function inputFocusChange(focus: boolean) { } let refSave: HTMLElement | undefined; -function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: { willAlertModalBecomeVisible: boolean; isVisible: boolean }, onyxFocused: boolean) { +function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: {willAlertModalBecomeVisible: boolean; isVisible: boolean}, onyxFocused: boolean) { if (Browser.isMobile()) { return; } if (isFocused && !onyxFocused) { inputFocusChange(true); ref.focus(); - } + } if (isFocused) { refSave = ref; } - if (!isFocused && !onyxFocused && !modal.willAlertModalBecomeVisible && !modal.isVisible && refSave) { + if (!isFocused && !onyxFocused && !modal.willAlertModalBecomeVisible && !modal.isVisible && refSave) { if (!ReportActionComposeFocusManager.isFocused()) { refSave.focus(); - } else { + } else { refSave = undefined; } } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index a61f424f4bdd..7c1d8b2f14de 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -374,7 +374,7 @@ function ComposerWithSuggestions({ if (!suggestionsRef.current) { return false; } - InputFocus.inputFocusChange(false) + InputFocus.inputFocusChange(false); return suggestionsRef.current.setShouldBlockSuggestionCalc(false); }, [suggestionsRef]); From f4f650182d0d846b9e93258c35d4aa3cc940448b Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 9 Oct 2023 18:35:11 +0200 Subject: [PATCH 138/205] fix: lint and type errors --- src/styles/StyleUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index b57c43d4ffe0..32fbb3fcff7b 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1,3 +1,4 @@ +import {CSSProperties} from 'react'; import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; import {EdgeInsets} from 'react-native-safe-area-context'; import {ValueOf} from 'type-fest'; From 07d4e0a60be1577ec29c00970e6d855cb6f4741c Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 10 Oct 2023 01:31:15 +0700 Subject: [PATCH 139/205] pretteir --- src/libs/IntlPolyfill/index.ts | 2 +- src/libs/IntlPolyfill/polyfillDateTimeFormat.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/IntlPolyfill/index.ts b/src/libs/IntlPolyfill/index.ts index 185f292c3969..be3e392b35cd 100644 --- a/src/libs/IntlPolyfill/index.ts +++ b/src/libs/IntlPolyfill/index.ts @@ -1,6 +1,6 @@ import polyfillNumberFormat from './polyfillNumberFormat'; import IntlPolyfill from './types'; -import polyfillDateTimeFormat from "./polyfillDateTimeFormat"; +import polyfillDateTimeFormat from './polyfillDateTimeFormat'; /** * Polyfill the Intl API if the ICU version is old. diff --git a/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts b/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts index cce78f58d4ac..374601143528 100644 --- a/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts +++ b/src/libs/IntlPolyfill/polyfillDateTimeFormat.ts @@ -1,7 +1,7 @@ -import Onyx from "react-native-onyx"; -import ONYXKEYS from "../../ONYXKEYS"; -import {Timezone} from "../../types/onyx/PersonalDetails"; -import CONST from "../../CONST"; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '../../ONYXKEYS'; +import {Timezone} from '../../types/onyx/PersonalDetails'; +import CONST from '../../CONST'; let currentUserAccountID: number | undefined; Onyx.connect({ From a2914cde5e84272dccabead3dc3adf949d25a9b8 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 10 Oct 2023 14:18:46 +0200 Subject: [PATCH 140/205] fix: use theme colors instead of hardcoded --- src/components/SelectionList/BaseSelectionList.js | 3 ++- src/styles/themes/default.ts | 1 + src/styles/themes/light.ts | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/SelectionList/BaseSelectionList.js b/src/components/SelectionList/BaseSelectionList.js index 83379ec82c89..de8d04fddf03 100644 --- a/src/components/SelectionList/BaseSelectionList.js +++ b/src/components/SelectionList/BaseSelectionList.js @@ -23,6 +23,7 @@ import OptionsListSkeletonView from '../OptionsListSkeletonView'; import useActiveElement from '../../hooks/useActiveElement'; import BaseListItem from './BaseListItem'; import ArrowKeyFocusManager from '../ArrowKeyFocusManager'; +import themeColors from '../../styles/themes/default'; const propTypes = { ...keyboardStatePropTypes, @@ -423,7 +424,7 @@ function BaseSelectionList({ onScrollBeginDrag={onScrollBeginDrag} keyExtractor={(item) => item.keyForList} extraData={focusedIndex} - indicatorStyle="white" + indicatorStyle={themeColors.white} keyboardShouldPersistTaps="always" showsVerticalScrollIndicator={showScrollIndicator} initialNumToRender={12} diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index b0c7c728f944..94114882cd46 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -91,6 +91,7 @@ const darkTheme = { [SCREENS.SETTINGS.STATUS]: colors.green700, [SCREENS.SETTINGS.ROOT]: colors.darkHighlightBackground, }, + white: colors.white, } satisfies ThemeBase; export default darkTheme; diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index d6b864e81567..55069b11b83e 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -91,6 +91,7 @@ const lightTheme = { [SCREENS.SETTINGS.STATUS]: colors.green700, [SCREENS.SETTINGS.ROOT]: colors.lightHighlightBackground, }, + white: colors.white, } satisfies ThemeDefault; export default lightTheme; From 5719736b972fd294f11718b5c0f28310e96d7586 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 10 Oct 2023 11:31:17 -1000 Subject: [PATCH 141/205] make requested comment change --- src/components/OptionsList/BaseOptionsList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js index a82217767ab6..edea0b8d1aba 100644 --- a/src/components/OptionsList/BaseOptionsList.js +++ b/src/components/OptionsList/BaseOptionsList.js @@ -247,7 +247,7 @@ function BaseOptionsList({ ) : ( <> {/* If we are loading new options we will avoid showing any header message. This is mostly because one of the header messages says there are no options. */} - {/* This is confusing because we might be in the process of loading fresh options from the server. */} + {/* This is misleading because we might be in the process of loading fresh options from the server. */} {!isLoadingNewOptions && headerMessage ? ( {headerMessage} From 70ead5065931f438fb2dcb2d4db0f7cec7909f81 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 10 Oct 2023 14:55:13 -0700 Subject: [PATCH 142/205] Remove possibly undefined waypoints --- src/components/DistanceEReceipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 3a64a987c307..7c7837b8413d 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -43,7 +43,7 @@ function DistanceEReceipt({transaction}) { .keys() .sort((keyA, keyB) => TransactionUtils.getWaypointIndex(keyA) - TransactionUtils.getWaypointIndex(keyB)) .map((key) => ({[key]: waypoints[key]})) - .reduce((result, obj) => _.assign(result, obj), {}) + .reduce((result, obj) => (obj ? _.assign(result, obj) : result), {}) .value(), [waypoints], ); From 96e9ce7e3d2967b9f7d02b70f41905d8fbbb86c0 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 11 Oct 2023 15:53:47 +0200 Subject: [PATCH 143/205] fix: fix lottie type --- src/styles/styles.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index c86f1e11672d..615046b7a47b 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -33,7 +33,6 @@ import whiteSpace from './utilities/whiteSpace'; import wordBreak from './utilities/wordBreak'; import writingDirection from './utilities/writingDirection'; import variables from './variables'; -import * as LottieAnimations from '../components/LottieAnimations'; type AnchorPosition = { horizontal: number; @@ -73,7 +72,6 @@ type Styles = Record< >; type ValueStructure = T[keyof T]; -type LottieAnimation = ValueStructure; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target const touchCalloutNone: Pick = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; // to prevent vertical text offset in Safari for badges, new lineHeight values have been added @@ -3855,7 +3853,7 @@ const styles = (theme: ThemeDefault) => lineHeight: variables.lineHeightLarge, }, - aspectRatioLottie: (source: LottieAnimation & {uri?: string}) => { + aspectRatioLottie: (source) => { if (!source.uri && typeof source === 'object' && source.w && source.h) { return {aspectRatio: source.w / source.h}; } From 947f0241b707b58e2e78f4895b173533a9dd6435 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 11 Oct 2023 16:05:08 +0200 Subject: [PATCH 144/205] fix: lint problem --- src/styles/styles.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 615046b7a47b..e82a6c120a05 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -70,7 +70,6 @@ type Styles = Record< // eslint-disable-next-line @typescript-eslint/no-explicit-any | ((...args: any[]) => ViewStyle | TextStyle | ImageStyle | AnchorPosition | CustomAnimation | CustomPickerStyle) >; -type ValueStructure = T[keyof T]; // touchCallout is an iOS safari only property that controls the display of the callout information when you touch and hold a target const touchCalloutNone: Pick = Browser.isMobileSafari() ? {WebkitTouchCallout: 'none'} : {}; From 79498de1e88ca750e9e40aa111989258caff12e7 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 11 Oct 2023 16:59:14 +0200 Subject: [PATCH 145/205] add jsDoc --- src/components/MoneyRequestConfirmationList.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index acaa83181bbf..092988bdfbdc 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -80,6 +80,12 @@ const propTypes = { /** IOU Tag */ iouTag: PropTypes.string, + /** IOU isBillable */ + iouIsBillable: PropTypes.bool, + + /** IOU Callback toggle billable state */ + onToggleBillable: PropTypes.func, + /** Selected participants from MoneyRequestModal with login / accountID */ selectedParticipants: PropTypes.arrayOf(optionPropTypes).isRequired, From 3df292fc09fd60c849fef5e042ae31e5754794d0 Mon Sep 17 00:00:00 2001 From: Ayaz Alavi Date: Wed, 11 Oct 2023 20:46:05 +0500 Subject: [PATCH 146/205] removed mobile check Signed-off-by: Ayaz Alavi --- src/libs/actions/InputFocus/index.website.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libs/actions/InputFocus/index.website.ts b/src/libs/actions/InputFocus/index.website.ts index 17cb1fe2f44d..7c044b169a03 100644 --- a/src/libs/actions/InputFocus/index.website.ts +++ b/src/libs/actions/InputFocus/index.website.ts @@ -4,17 +4,11 @@ import * as Browser from '../../Browser'; import ReportActionComposeFocusManager from '../../ReportActionComposeFocusManager'; function inputFocusChange(focus: boolean) { - if (Browser.isMobile()) { - return; - } Onyx.set(ONYXKEYS.INPUT_FOCUSED, focus); } let refSave: HTMLElement | undefined; function composerFocusKeepFocusOn(ref: HTMLElement, isFocused: boolean, modal: {willAlertModalBecomeVisible: boolean; isVisible: boolean}, onyxFocused: boolean) { - if (Browser.isMobile()) { - return; - } if (isFocused && !onyxFocused) { inputFocusChange(true); ref.focus(); From 00e2539a15117e327af473c1ff34121225bc590f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 12 Oct 2023 00:06:23 +0800 Subject: [PATCH 147/205] properly clean up focus timeout --- src/components/TextInput/BaseTextInput.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index cfc042b4f370..a9a7b7bd17d8 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -47,19 +47,11 @@ function BaseTextInput(props) { return; } - let focusTimeout; if (props.shouldDelayFocus) { - focusTimeout = setTimeout(() => input.current.focus(), CONST.ANIMATED_TRANSITION); - return; + const focusTimeout = setTimeout(() => input.current.focus(), CONST.ANIMATED_TRANSITION); + return () => clearTimeout(focusTimeout); } input.current.focus(); - - return () => { - if (!focusTimeout) { - return; - } - clearTimeout(focusTimeout); - }; // We only want this to run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); From 9f890319a4890227f0abeebfc6dd762063e54c52 Mon Sep 17 00:00:00 2001 From: Gray Lewis Date: Wed, 11 Oct 2023 18:14:43 +0200 Subject: [PATCH 148/205] UserCurrentLocationButton onPress fix --- src/components/UserCurrentLocationButton.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/UserCurrentLocationButton.js b/src/components/UserCurrentLocationButton.js index 9ba74ac6c426..85f91e7e71e8 100644 --- a/src/components/UserCurrentLocationButton.js +++ b/src/components/UserCurrentLocationButton.js @@ -88,6 +88,8 @@ function UserCurrentLocationButton({onLocationFetched, onLocationError, onClick, onPress={getUserLocation} accessibilityLabel={translate('location.useCurrent')} disabled={isDisabled} + onMouseDown={e => e.preventDefault()} + onTouchStart={e => e.preventDefault()} > Date: Wed, 11 Oct 2023 18:23:23 +0200 Subject: [PATCH 149/205] Revert "UserCurrentLocationButton onPress fix" This reverts commit 9f890319a4890227f0abeebfc6dd762063e54c52. Reverting --- src/components/UserCurrentLocationButton.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/UserCurrentLocationButton.js b/src/components/UserCurrentLocationButton.js index 85f91e7e71e8..9ba74ac6c426 100644 --- a/src/components/UserCurrentLocationButton.js +++ b/src/components/UserCurrentLocationButton.js @@ -88,8 +88,6 @@ function UserCurrentLocationButton({onLocationFetched, onLocationError, onClick, onPress={getUserLocation} accessibilityLabel={translate('location.useCurrent')} disabled={isDisabled} - onMouseDown={e => e.preventDefault()} - onTouchStart={e => e.preventDefault()} > Date: Wed, 11 Oct 2023 18:26:07 +0200 Subject: [PATCH 150/205] UserCurrentLocationButton onClick fixed --- src/components/UserCurrentLocationButton.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/UserCurrentLocationButton.js b/src/components/UserCurrentLocationButton.js index 9ba74ac6c426..85f91e7e71e8 100644 --- a/src/components/UserCurrentLocationButton.js +++ b/src/components/UserCurrentLocationButton.js @@ -88,6 +88,8 @@ function UserCurrentLocationButton({onLocationFetched, onLocationError, onClick, onPress={getUserLocation} accessibilityLabel={translate('location.useCurrent')} disabled={isDisabled} + onMouseDown={e => e.preventDefault()} + onTouchStart={e => e.preventDefault()} > Date: Wed, 11 Oct 2023 19:00:17 +0200 Subject: [PATCH 151/205] add defaultPrps --- src/components/MoneyRequestConfirmationList.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 092988bdfbdc..64e7ed6b232c 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -168,6 +168,8 @@ const defaultProps = { iouType: CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, iouCategory: '', iouTag: '', + iouIsBillable: false, + onToggleBillable: () => {}, payeePersonalDetails: null, canModifyParticipants: false, isReadOnly: false, From 6ea1a122a49721d8406c09ef797b507e3c36130a Mon Sep 17 00:00:00 2001 From: Gray Lewis Date: Wed, 11 Oct 2023 21:40:54 +0200 Subject: [PATCH 152/205] linted --- src/components/UserCurrentLocationButton.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/UserCurrentLocationButton.js b/src/components/UserCurrentLocationButton.js index 85f91e7e71e8..fa22eb602886 100644 --- a/src/components/UserCurrentLocationButton.js +++ b/src/components/UserCurrentLocationButton.js @@ -88,8 +88,8 @@ function UserCurrentLocationButton({onLocationFetched, onLocationError, onClick, onPress={getUserLocation} accessibilityLabel={translate('location.useCurrent')} disabled={isDisabled} - onMouseDown={e => e.preventDefault()} - onTouchStart={e => e.preventDefault()} + onMouseDown={(e) => e.preventDefault()} + onTouchStart={(e) => e.preventDefault()} > Date: Thu, 12 Oct 2023 08:03:28 +0700 Subject: [PATCH 153/205] Add Unmount clearAccountMessages function --- src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js index 7783b6c58ace..560a395e6844 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js +++ b/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js @@ -39,6 +39,9 @@ function VerifyStep({account, session}) { useEffect(() => { Session.clearAccountMessages(); + return () => { + Session.clearAccountMessages(); + }; }, []); useEffect(() => { From f9ed59fdf8324ba5a337d5317630a82af8dd1852 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 12 Oct 2023 10:47:04 +0200 Subject: [PATCH 154/205] fix: typo --- src/styles/styles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 412e7a4001cb..1cd7faa0642a 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -3324,7 +3324,7 @@ const styles = (theme: ThemeDefault) => eReceiptAmountLarge: { ...headlineFont, fontSize: variables.fontSizeEReceiptLarge, - lineHeight: variables.lineHeightXXsLarge, + lineHeight: variables.lineHeightXXLarge, wordBreak: 'break-word', textAlign: 'center', }, From 83fd154b1d44a0904f64a8b8ee1ad2b72b775b47 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Thu, 31 Aug 2023 15:58:54 +0100 Subject: [PATCH 155/205] feat(Wallet): add empty state and redesign wallet page # Conflicts: # src/components/LottieAnimations.js # src/pages/settings/Wallet/WalletPage/BaseWalletPage.js fix(wallet): item styles not matching designs fix(wallet): PropTypes console errors fix(wallet): popover menu positioning issues fix(wallet): flags used to determine if the empty state should be shown refactor(wallet): code style # Conflicts: # src/pages/settings/Wallet/PaymentMethodList.js fix(wallet): accessing wrong onyx key to look for assigned cards fix(wallet): spanish translations refactor(wallet): apply code style guidelines refactor(wallet): differentiate old wallet from new version using URL # Conflicts: # src/ROUTES.ts # src/libs/Navigation/AppNavigator/ModalStackNavigators.js # src/libs/Navigation/linkingConfig.js add padding top prop to IlustratedHeaderPageLayout # Conflicts: # src/components/IllustratedHeaderPageLayout.js remove header top padding replace hardcoded values with references to SCREENS # Conflicts: # src/SCREENS.ts add scroll to WalletPage, disable scroll in PaymentMethodList lint code # Conflicts: # src/pages/settings/Wallet/PaymentMethodList.js chore: reduce FastMoney animation filesize chore: add wallet backgriound color to light theme refactor: wallet route refactor: remove unused prop refactor(wallet): remove unnecessary wrapper component fix(wallet): linter issue refactor(wallet): remove underscore dependency chore(wallet): use old wallet instead of new one # Conflicts: # src/libs/Navigation/AppNavigator/ModalStackNavigators.js chore: apply prettier # Conflicts: # src/SCREENS.ts chore(wallet): remove card item left padding fix(section): missing subtitle refactor(wallet): use constant screen name for wallet domain cards # Conflicts: # src/libs/Navigation/linkingConfig.js refactor(wallet): use constant screen name for wallet fix(wallet): screen wrapper adding unnecessary vertical padding # Conflicts: # src/pages/settings/Wallet/WalletPage/WalletPage.js fix(wallet): wrong display name type fix(wallet): missing testID for screen wrapper and unnecessary fragment fix(wallet): unused import fix(wallet): status bar background color fix(wallet): revert scrollview removal fix(wallet): wrong section border radius fix(wallet): wrong payment item height fix(wallet): console error with nested Flatlist inside a ScrollView # Conflicts: # src/pages/settings/Wallet/PaymentMethodList.js fix(wallet): section card item styles refactor(wallet): replace direct import from colors with themeColors fix(wallet): popover menu positioning fix(wallet): transfer balance button styles refactor(wallet): remove paypal references fix(wallet): adjust spacings inside wallet section fix(wallet): bank accounts section spacing chore(wallet): fix linter issues refactor: remove unused paypal route # Conflicts: # src/ROUTES.ts chore(wallet): remove old wallet page # Conflicts: # src/pages/settings/Wallet/WalletPage/BaseWalletPage.js chore: rebase with main branch fix(wallet): wrong lineHeight type --- assets/animations/FastMoney.json | 1 + .../simple-illustration__handearth.svg | 30 ++ src/SCREENS.ts | 2 + src/components/Icon/Illustrations.js | 2 + src/components/IllustratedHeaderPageLayout.js | 71 +++- src/components/LottieAnimations.js | 4 +- src/components/Section.js | 44 ++- src/components/WalletSection.js | 45 +++ src/languages/en.ts | 10 + src/languages/es.ts | 10 + src/libs/Navigation/NavigationRoot.js | 2 +- .../settings/Wallet/PaymentMethodList.js | 69 +++- src/pages/settings/Wallet/WalletEmptyState.js | 62 +++ .../{BaseWalletPage.js => WalletPage.js} | 365 ++++++++++-------- src/pages/settings/Wallet/WalletPage/index.js | 10 +- .../Wallet/WalletPage/index.native.js | 4 +- .../Wallet/WalletPage/walletPagePropTypes.js | 1 + src/styles/styles.js | 22 +- src/styles/themes/default.js | 1 + src/styles/themes/light.js | 1 + src/styles/utilities/spacing.ts | 4 + src/styles/variables.ts | 6 +- 22 files changed, 539 insertions(+), 227 deletions(-) create mode 100644 assets/animations/FastMoney.json create mode 100644 assets/images/simple-illustrations/simple-illustration__handearth.svg create mode 100644 src/components/WalletSection.js create mode 100644 src/pages/settings/Wallet/WalletEmptyState.js rename src/pages/settings/Wallet/WalletPage/{BaseWalletPage.js => WalletPage.js} (53%) diff --git a/assets/animations/FastMoney.json b/assets/animations/FastMoney.json new file mode 100644 index 000000000000..95d560319141 --- /dev/null +++ b/assets/animations/FastMoney.json @@ -0,0 +1 @@ +{"v":"5.9.6","fr":24,"ip":0,"op":80,"w":375,"h":240,"nm":"C","assets":[{"id":"comp_0","nm":"C","fr":24,"layers":[{"ind":1,"ty":0,"nm":"E","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[174.5,291,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[42,42,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":0,"op":80,"st":0}]},{"id":"comp_1","nm":"E","fr":24,"layers":[{"ind":1,"ty":0,"nm":"t","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":0,"s":[966,966,0],"to":[0,-20,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":4,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":12,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":24,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":28,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":36,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":40,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":44,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":48,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":56,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":64,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":68,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":72,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":76,"s":[966,846,0],"to":[0,0,0],"ti":[0,-20,0]},{"t":80,"s":[966,966,0]}],"l":2},"a":{"a":0,"k":[966,543.5,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1087,"ip":0,"op":80,"st":0},{"ind":2,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[966,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":0,"op":16,"st":0},{"ind":3,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[966,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":16,"op":32,"st":16},{"ind":4,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[966,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":32,"op":48,"st":32},{"ind":5,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[966,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":48,"op":64,"st":48},{"ind":6,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[966,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":64,"op":80,"st":64},{"ind":7,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1060,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":0,"op":8,"st":-8},{"ind":8,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1060,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":8,"op":24,"st":8},{"ind":9,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1060,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":24,"op":40,"st":24},{"ind":10,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1060,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":40,"op":56,"st":40},{"ind":11,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1060,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":56,"op":72,"st":56},{"ind":12,"ty":0,"nm":"l","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[1060,966,0],"l":2},"a":{"a":0,"k":[966,966,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1932,"ip":72,"op":80,"st":72},{"ind":13,"ty":0,"nm":"t","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":0,"s":[966,966,0],"to":[0,-20,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":4,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":12,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":24,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":28,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":36,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":40,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":44,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":48,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":56,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":64,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":68,"s":[966,846,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":72,"s":[966,966,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":76,"s":[966,846,0],"to":[0,0,0],"ti":[0,-20,0]},{"t":80,"s":[966,966,0]}],"l":2},"a":{"a":0,"k":[966,543.5,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1087,"ip":0,"op":80,"st":0},{"ind":14,"ty":0,"nm":"u","parent":1,"refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[966,543,0],"l":2},"a":{"a":0,"k":[966,543.5,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"w":1932,"h":1087,"ip":0,"op":80,"st":0},{"ind":15,"ty":3,"nm":"s","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":50,"s":[0]},{"t":54,"s":[9]}]},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":50,"s":[1304.5,374.75,0],"to":[12.667,-19,0],"ti":[-13.167,5,0]},{"t":54,"s":[1362.5,335.75,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":50,"s":[100,100,100]},{"t":54,"s":[25,25,100]}],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":16,"ty":3,"nm":"s","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":50,"s":[0]},{"t":54,"s":[6]}]},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":50,"s":[1341.898,405.813,0],"to":[13.5,0.917,0],"ti":[-11.5,-9.417,0]},{"t":54,"s":[1395.898,426.313,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":50,"s":[100,100,100]},{"t":54,"s":[25,25,100]}],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":17,"ty":4,"nm":"s","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-278.97,470.86,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-8.927,0],[1.705,-3.01],[7.797,-1.705]],"o":[[4.514,-11.535],[7.87,0],[-1.705,3.009],[-7.797,1.706]],"v":[[-14.694,12.137],[6.068,-12.137],[12.99,-2.507],[-0.378,5.015]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.553,0.784,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1285.643,129.839]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":50,"op":55,"st":0},{"ind":18,"ty":4,"nm":"s","parent":16,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-316.368,439.797,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[11.736,1.103],[-9.329,0],[0.702,-4.112],[5.019,0.247]],"o":[[5.818,-4.313],[9.328,0],[-0.702,3.01],[-10.432,-0.903]],"v":[[-16.349,-0.224],[4.012,-6.643],[15.647,1.582],[6.72,6.397]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.553,0.784,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1321,152.732]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":50,"op":55,"st":0}]},{"id":"comp_2","nm":"t","fr":24,"layers":[{"ind":1,"ty":3,"nm":"m","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":0},"p":{"a":0,"k":[1028,525.5,0],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[91,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":8,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":12,"s":[91,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":16,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[91,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":24,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":28,"s":[91,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":32,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":36,"s":[91,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":40,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":44,"s":[91,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":52,"s":[91,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":56,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":60,"s":[91,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":64,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":68,"s":[91,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":72,"s":[101,91,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":76,"s":[91,103,100]},{"t":80,"s":[101,91,100]}],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":2,"ty":3,"nm":"b","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[909,531.5,0],"to":[1,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":4,"s":[915,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":8,"s":[909,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":12,"s":[915,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":16,"s":[909,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[915,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[909,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":28,"s":[915,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":32,"s":[909,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":36,"s":[915,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":40,"s":[909,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":44,"s":[915,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":48,"s":[909,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[915,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":56,"s":[909,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[915,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":64,"s":[909,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":68,"s":[915,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":72,"s":[909,531.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":76,"s":[915,531.5,0],"to":[0,0,0],"ti":[1,0,0]},{"t":80,"s":[909,531.5,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[80,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":8,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":12,"s":[80,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":16,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[80,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":24,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":28,"s":[80,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":32,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":36,"s":[80,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":40,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":44,"s":[80,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":52,"s":[80,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":56,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":60,"s":[80,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":64,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":68,"s":[80,103,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":72,"s":[111,65,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":76,"s":[80,103,100]},{"t":80,"s":[111,65,100]}],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":3,"ty":3,"nm":"h","parent":4,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":0},"p":{"a":0,"k":[10,137,0],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":4,"ty":3,"nm":"a","parent":5,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":8,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":28,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":32,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":36,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":40,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":44,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":48,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":52,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":56,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":64,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":68,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":76,"s":[0]},{"t":80,"s":[10]}]},"p":{"a":0,"k":[-36,63,0],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":5,"ty":3,"nm":"a","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[-160]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":8,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[-4]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[-160]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":28,"s":[-4]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":32,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":36,"s":[-160]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":40,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":44,"s":[-4]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":48,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":52,"s":[-160]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":56,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[-4]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":64,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":68,"s":[-160]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[-112]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":76,"s":[-4]},{"t":80,"s":[-112]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[890,395.5,0],"to":[2.5,-2,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":4,"s":[905,383.5,0],"to":[0,0,0],"ti":[-2.5,-1.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":8,"s":[890,395.5,0],"to":[2.5,1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":12,"s":[920,393.5,0],"to":[0,0,0],"ti":[2.5,1.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":16,"s":[890,395.5,0],"to":[-2.5,-1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[905,383.5,0],"to":[0,0,0],"ti":[-2.5,-1.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[890,395.5,0],"to":[2.5,1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":28,"s":[920,393.5,0],"to":[0,0,0],"ti":[2.5,1.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":32,"s":[890,395.5,0],"to":[-2.5,-1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":36,"s":[905,383.5,0],"to":[0,0,0],"ti":[-2.5,-1.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":40,"s":[890,395.5,0],"to":[2.5,1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":44,"s":[920,393.5,0],"to":[0,0,0],"ti":[2.5,1.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":48,"s":[890,395.5,0],"to":[-2.5,-1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[905,383.5,0],"to":[0,0,0],"ti":[-2.5,-1.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":56,"s":[890,395.5,0],"to":[2.5,1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[920,393.5,0],"to":[0,0,0],"ti":[2.5,1.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":64,"s":[890,395.5,0],"to":[-2.5,-1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":68,"s":[905,383.5,0],"to":[0,0,0],"ti":[-2.5,-1.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":72,"s":[890,395.5,0],"to":[2.5,1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":76,"s":[920,393.5,0],"to":[0,0,0],"ti":[5,-0.333,0]},{"t":80,"s":[890,395.5,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":6,"ty":3,"nm":"h","parent":7,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":44,"s":[24]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":47,"s":[0]},{"t":49,"s":[-20]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":0.667},"o":{"x":0.167,"y":0.167},"t":44,"s":[-125,-9.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":47,"s":[-125,-9.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":49,"s":[-125,-9.5,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":7,"ty":3,"nm":"a","parent":8,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":44,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":47,"s":[0]},{"t":49,"s":[-19]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":0.667},"o":{"x":0.167,"y":0.167},"t":44,"s":[75,-34,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":47,"s":[75,-34,0],"to":[0,0,0],"ti":[0,0,0]},{"t":49,"s":[75,-34,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":8,"ty":3,"nm":"a","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":44,"s":[24]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":47,"s":[0]},{"t":49,"s":[30]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":44,"s":[1122,363.5,0],"to":[5,1.333,0],"ti":[-3.333,-2.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":47,"s":[1152,371.5,0],"to":[3.333,2.333,0],"ti":[1.667,-1,0]},{"t":49,"s":[1142,377.5,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":9,"ty":3,"nm":"h","parent":10,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":50,"s":[-10]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":53,"s":[0]},{"t":59,"s":[-54]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":50,"s":[70,-59,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":53,"s":[70,-59,0],"to":[0,0,0],"ti":[0,0,0]},{"t":59,"s":[70,-59,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":10,"ty":3,"nm":"a","parent":11,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":50,"s":[5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":53,"s":[0]},{"t":59,"s":[-11]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":50,"s":[121,-15,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":53,"s":[121,-15,0],"to":[0,0,0],"ti":[0,0,0]},{"t":59,"s":[121,-15,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":11,"ty":3,"nm":"a","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":50,"s":[2]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":53,"s":[0]},{"t":59,"s":[20]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":50,"s":[1150,365.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.659,"y":0.854},"o":{"x":0.297,"y":0},"t":53,"s":[1150,365.5,0],"to":[-1.038,0.389,0],"ti":[2.52,-0.945,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.425,"y":0.211},"t":56,"s":[1144.19,387.679,0],"to":[-3.951,1.482,0],"ti":[1.628,-0.611,0]},{"t":59,"s":[1134,371.5,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":12,"ty":4,"nm":"h","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[208.53,96.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.2,6.65],[-11.98,5.54],[-6.96,-4.61],[0.1,-7.55],[0,0]],"o":[[0,0],[-5.9,0.34],[-0.21,-6.66],[11.99,-5.53],[6.97,4.61],[-0.1,7.56],[0,0]],"v":[[-16.11,18.555],[-16.12,18.555],[-26.52,9.685],[-11.78,-13.365],[18.64,-11.005],[26.63,5.195],[19.56,15.265]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[804.229,508.235]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.55,-2.56]],"o":[[-0.38,4.04],[0,0]],"v":[[0.06,-4.945],[0.32,4.945]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[787.789,521.845]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.59,-0.76],[-0.11,0.24],[0,0]],"o":[[0.76,3.53],[4.3,1.27],[0,0],[0,0]],"v":[[-5.495,-3.85],[-0.485,2.58],[5.495,0.2],[5.495,0.19]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[793.614,530.64]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.48,-2.41],[-2.62,0.23],[-0.21,0.37]],"o":[[-0.42,5.63],[1.84,3],[4.74,-0.43],[0,0]],"v":[[-6.755,-7.855],[-4.845,3.955],[2.435,7.625],[7.175,2.625]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[803.954,526.885]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.77,-2.08],[-2.7,0.54],[2.35,4.24],[0.06,4.63]],"o":[[0.46,4.14],[2.45,2.88],[4.64,-0.93],[-1.44,-2.85],[0,0]],"v":[[-9.3,-3.17],[-5.71,6.09],[2.68,9.45],[6.95,0.08],[4.48,-9.99]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[816.839,523.42]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.64,-0.93],[2.45,2.88],[4.74,-0.43],[1.84,3],[4.3,1.27],[0.76,3.53],[0,0],[0.2,6.65],[-11.98,5.54],[-6.96,-4.61],[0.1,-7.55]],"o":[[2.35,4.24],[-2.7,0.54],[-0.21,0.37],[-2.62,0.23],[-0.11,0.24],[-2.59,-0.76],[0,0],[-5.9,0.34],[-0.21,-6.66],[11.99,-5.53],[6.97,4.61],[-0.1,7.56]],"v":[[19.56,11.46],[15.29,20.83],[6.9,17.47],[2.16,22.47],[-5.12,18.8],[-11.1,21.18],[-16.11,14.75],[-16.12,14.75],[-26.52,5.88],[-11.78,-17.17],[18.64,-14.81],[26.63,1.39]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[804.229,512.04]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":80,"st":0},{"ind":13,"ty":4,"nm":"a","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[168.53,183.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-3.79,-4.94]],"o":[[6.76,-0.53],[0,0]],"v":[[-8.2,-2.515],[8.2,3.045]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[810.199,470.945]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[7.72,-0.59]],"o":[[-3.14,-5.15],[0,0]],"v":[[8.145,3.715],[-8.145,-3.125]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[813.384,459.555]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-4.28,-4.54]],"o":[[6.87,-2.29],[0,0]],"v":[[-8.365,-0.545],[8.365,2.835]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[808.274,482.105]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.37,9.86]],"o":[[-0.37,9.86],[0,0]],"v":[[-7.815,-5.235],[8.185,-4.625]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[806.914,502.335]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.54,4.98],[-0.91,4.23],[-1.27,3.87],[-16.55,15.32]],"o":[[0,-5.38],[0.48,-4.53],[0.88,-4.14],[6.98,-21.31],[0,0]],"v":[[-21.145,47.33],[-20.335,31.79],[-18.245,18.66],[-15.005,6.66],[21.145,-47.33]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[820.244,449.77]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.68,4.15],[-0.85,3.53],[-1.15,3.48],[-12.89,12.79]],"o":[[0.36,-4.37],[0.59,-3.7],[0.89,-3.66],[5.88,-17.68],[0,0]],"v":[[-17.46,40.015],[-15.91,27.245],[-13.75,16.395],[-10.69,5.685],[17.46,-40.015]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[832.559,457.695]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-16.55,15.32],[0,0],[5.88,-17.68],[0,0],[7.72,-0.59]],"o":[[0,0],[-12.89,12.79],[0,0],[-3.14,-5.15],[6.98,-21.31]],"v":[[13.76,-30.47],[22.39,-15.23],[-5.76,30.47],[-6.1,30.36],[-22.39,23.52]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[827.629,432.91]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.28,-4.54],[0,0],[0.36,-4.37],[-0.37,9.86],[-0.54,4.98]],"o":[[0,0],[-0.68,4.15],[-0.37,9.86],[0,-5.38],[6.87,-2.29]],"v":[[8.95,-8.48],[8.96,-8.48],[7.41,4.29],[-8.59,3.68],[-7.78,-11.86]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[807.689,493.42]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.14,-5.15],[0,0],[0.89,-3.66],[0,0],[6.76,-0.53],[-1.27,3.87]],"o":[[0,0],[-1.15,3.48],[0,0],[-3.79,-4.94],[0.88,-4.14],[7.72,-0.59]],"v":[[9.595,-1.695],[9.935,-1.585],[6.875,9.125],[6.465,9.025],[-9.935,3.465],[-6.695,-8.535]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[811.934,464.965]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.79,-4.94],[0,0],[0.59,-3.7],[0,0],[6.87,-2.29],[-0.91,4.23]],"o":[[0,0],[-0.85,3.53],[0,0],[-4.28,-4.54],[0.48,-4.53],[6.76,-0.53]],"v":[[9.04,-2.43],[9.45,-2.33],[7.29,8.52],[7.28,8.52],[-9.45,5.14],[-7.36,-7.99]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[809.359,476.42]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":80,"st":0},{"ind":14,"ty":4,"nm":"a","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.53,196.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33],[-1.52,-8.7]],"o":[[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33],[0,4.67]],"v":[[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22],[52.945,-11.41]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.52,-8.7],[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33]],"o":[[0,4.67],[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33]],"v":[[52.945,-11.41],[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":79,"op":80,"st":0},{"ind":15,"ty":4,"nm":"a","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.53,196.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33],[-1.52,-8.7]],"o":[[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33],[0,4.67]],"v":[[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22],[52.945,-11.41]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.52,-8.7],[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33]],"o":[[0,4.67],[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33]],"v":[[52.945,-11.41],[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":63,"op":74,"st":0},{"ind":16,"ty":4,"nm":"a","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.53,196.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33],[-1.52,-8.7]],"o":[[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33],[0,4.67]],"v":[[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22],[52.945,-11.41]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.52,-8.7],[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33]],"o":[[0,4.67],[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33]],"v":[[52.945,-11.41],[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":47,"op":58,"st":0},{"ind":17,"ty":4,"nm":"a","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.53,196.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33],[-1.52,-8.7]],"o":[[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33],[0,4.67]],"v":[[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22],[52.945,-11.41]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.52,-8.7],[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33]],"o":[[0,4.67],[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33]],"v":[[52.945,-11.41],[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":31,"op":42,"st":0},{"ind":18,"ty":4,"nm":"a","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.53,196.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33],[-1.52,-8.7]],"o":[[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33],[0,4.67]],"v":[[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22],[52.945,-11.41]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.52,-8.7],[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33]],"o":[[0,4.67],[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33]],"v":[[52.945,-11.41],[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":15,"op":26,"st":0},{"ind":19,"ty":4,"nm":"a","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.53,196.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33],[-1.52,-8.7]],"o":[[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33],[0,4.67]],"v":[[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22],[52.945,-11.41]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.52,-8.7],[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33]],"o":[[0,4.67],[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33]],"v":[[52.945,-11.41],[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":10,"st":0},{"ind":20,"ty":4,"nm":"h","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-187.47,521.11,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.16,-0.19],[3.84,0],[3.82,-0.13],[0.38,-2.68],[0,0]],"o":[[-1.74,0.1],[-1.91,0.31],[-6.18,0],[-3.82,0.13],[-0.38,2.67],[0,0]],"v":[[16.135,-3.325],[12.025,-2.855],[4.485,-2.215],[-9.325,-3.665],[-15.755,0.235],[-14.695,3.795]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1164.344,61.285]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[5.22,0.9],[1.21,-0.17],[0.01,-2.03],[-0.25,-0.13],[0,0]],"o":[[-8.92,1.02],[-1.75,-0.3],[-2.4,0.34],[-0.02,2.87],[0.02,0.01],[0,0]],"v":[[14.59,-2.46],[-6.43,-3.61],[-10.88,-3.79],[-14.57,-0.17],[-12.3,3.95],[-12.27,3.96]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1160.539,68.96]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[9.68,0.77],[0.84,-0.37],[-4.65,-0.93]],"o":[[-12.23,1.66],[-1.38,-0.02],[-4.17,1.76],[0,0]],"v":[[15.53,-4.375],[-8.04,-4.375],[-11.36,-3.825],[-8.94,4.395]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1159.599,76.735]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.98,-0.25],[1.1,-1.06],[0,-1],[-5.86,-0.63],[-3.95,0],[-8.15,2.81],[-0.51,6.88],[7.13,1.4],[4.87,3.95],[1.15,-1.91],[-7.42,-3.29]],"o":[[-6.4,0.97],[-2.55,-0.16],[-0.66,0.61],[0,2.68],[5.86,0.64],[3.95,0],[8.15,-2.8],[0.51,-6.88],[-7.13,-1.4],[-4.87,-3.95],[-1.15,1.91],[0,0]],"v":[[-3.255,11.595],[-22.745,12.485],[-28.345,13.875],[-29.365,16.305],[-22.105,22.165],[-5.675,21.025],[15.605,19.235],[28.855,5.095],[18.535,-10.705],[-0.295,-18.855],[-8.475,-20.005],[-2.645,-8.835]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1179.014,67.265]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.91,0.31],[-1.15,1.91],[-4.87,-3.95],[-7.13,-1.4],[0.51,-6.88],[8.15,-2.8],[3.95,0],[5.86,0.64],[0,2.68],[-0.66,0.61],[0,0],[-4.17,1.76],[-0.02,2.87],[-2.4,0.34],[0,0],[-0.38,2.67],[-3.82,0.13],[-6.18,0]],"o":[[-7.42,-3.29],[1.15,-1.91],[4.87,3.95],[7.13,1.4],[-0.51,6.88],[-8.15,2.81],[-3.95,0],[-5.86,-0.63],[0,-1],[0,0],[-4.65,-0.93],[-0.25,-0.13],[0.01,-2.03],[0,0],[0,0],[0.38,-2.68],[3.82,-0.13],[3.84,0]],"v":[[0.145,-8.835],[-5.685,-20.005],[2.495,-18.855],[21.325,-10.705],[31.645,5.095],[18.395,19.235],[-2.885,21.025],[-19.315,22.165],[-26.575,16.305],[-25.555,13.875],[-25.565,13.865],[-27.985,5.645],[-30.255,1.525],[-26.565,-2.095],[-26.575,-2.185],[-27.635,-5.745],[-21.205,-9.645],[-7.395,-8.195]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1176.224,67.265]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":44,"op":50,"st":0},{"ind":21,"ty":4,"nm":"a","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-362.47,461.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[2.48,-5]],"o":[[0,0],[2,5.67],[0,0]],"v":[[-0.94,-8.03],[-0.94,-8.02],[-1.24,8.03]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1233.839,72.2]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[2.58,5.79]],"o":[[2.18,-5.01],[0,0]],"v":[[-0.535,8.145],[-1.645,-8.145]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1219.294,72.085]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[2.74,-4.81]],"o":[[1.86,5.95],[0,0]],"v":[[-0.09,-8.025],[-1.77,8.025]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1247.969,72.955]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-10.36,1.72]],"o":[[-10.36,1.73],[0,0]],"v":[[3.795,-9.17],[6.565,7.45]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1201.674,73.11]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[89.82,6.06],[5.16,0.17],[5.24,0.02],[4.15,-0.07]],"o":[[-13.75,-27.12],[-4.83,-0.32],[-4.93,-0.18],[-3.97,-0.03],[0,0]],"v":[[89.84,32.59],[-47.43,-31.51],[-62.41,-32.26],[-77.66,-32.56],[-89.84,-32.5]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1295.309,96.44]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[60.81,4.09],[4.61,0.12],[4.68,-0.07],[3.54,-0.15]],"o":[[-15.72,-30],[-4.46,-0.3],[-4.54,-0.12],[-3.47,0.04],[0,0]],"v":[[80.59,27.725],[-42.63,-26.945],[-56.23,-27.575],[-70.07,-27.655],[-80.59,-27.365]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1288.829,107.925]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-13.75,-27.12],[0,0],[60.81,4.09],[1.86,5.95]],"o":[[0,0],[-15.72,-30],[2.74,-4.81],[89.82,6.06]],"v":[[69.475,28.74],[53.745,35.36],[-69.475,-19.31],[-67.795,-35.36]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1315.674,100.29]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[2.18,-5.01],[0,0],[3.54,-0.15],[-10.36,1.73],[-3.97,-0.03]],"o":[[2.58,5.79],[0,0],[-3.47,0.04],[-10.36,1.72],[4.15,-0.07],[0,0]],"v":[[9.625,-9.125],[10.735,7.165],[10.735,7.205],[0.215,7.495],[-2.555,-9.125],[9.625,-9.185]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1208.024,73.065]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.83,-0.32],[2.74,-4.81],[4.61,0.12],[0,0],[2,5.67]],"o":[[1.86,5.95],[-4.46,-0.3],[0,0],[2.48,-5],[5.16,0.17]],"v":[[6.71,-7.65],[5.03,8.4],[-8.57,7.77],[-8.57,7.65],[-8.27,-8.4]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1241.169,72.58]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.93,-0.18],[2.48,-5],[0,0],[4.68,-0.07],[0,0],[2.58,5.79],[0,0]],"o":[[2,5.67],[0,0],[-4.54,-0.12],[0,0],[2.18,-5.01],[0,0],[5.24,0.02]],"v":[[6.535,-7.935],[6.235,8.115],[6.235,8.235],[-7.605,8.155],[-7.605,8.115],[-8.715,-8.175],[-8.715,-8.235]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1226.364,72.115]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":44,"op":50,"st":0},{"ind":22,"ty":4,"nm":"m","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-12.47,67.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.462,-5.128],[-0.013,-0.349],[2.171,-0.112],[0.287,5.602],[0,0.45],[-1.984,0.087]],"o":[[0.037,0.337],[0.287,5.602],[-2.17,0.113],[-0.025,-0.474],[0,-4.965],[2.034,-0.1]],"v":[[3.73,-1.229],[3.805,-0.206],[0.386,10.136],[-4.055,0.193],[-4.092,-1.192],[-0.649,-10.149]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1053.325,418.603]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.287,-5.602],[2.171,-0.112],[0.287,5.601],[-2.184,0.112]],"o":[[0.287,5.602],[-2.171,0.112],[-0.287,-5.602],[2.17,-0.112]],"v":[[3.93,-0.206],[0.524,10.149],[-3.93,0.206],[-0.511,-10.149]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1027.587,422.096]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.137,8.271],[-2.358,5.477],[-1.547,0.449],[-0.4,0],[-1.273,-1.847],[-0.698,-8.01],[0.586,-5.365]],"o":[[-2.982,-6.749],[-0.137,-8.434],[0.686,-1.572],[0.399,-0.113],[2.071,0],[3.955,5.751],[0.537,5.938],[0,0]],"v":[[-5.801,24.515],[-10.367,0.911],[-6.911,-21.109],[-3.281,-24.352],[-2.083,-24.515],[3.319,-21.258],[9.968,0.325],[9.744,17.666]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1053.138,416.55]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.424,7.66],[-2.658,6.151],[-1.559,0.449],[-0.399,0],[-1.26,-1.847],[-0.524,-8.746],[1.148,-6.063]],"o":[[-2.982,-6.101],[-0.537,-9.332],[0.673,-1.572],[0.399,-0.113],[2.058,0],[4.33,6.262],[0.474,7.71],[0,0]],"v":[[-5.153,24.802],[-10.168,3.331],[-6.812,-21.396],[-3.182,-24.639],[-1.984,-24.802],[3.405,-21.545],[10.23,2.196],[8.907,23.704]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1027.637,419.332]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.036,8.982],[1.909,-1.559],[-0.112,0.499],[-1.971,2.92],[-0.274,-0.149],[-0.212,-14.971]],"o":[[0.2,-12.014],[-2.033,1.984],[-0.262,-3.007],[0.063,-0.312],[3.693,-0.536],[0.449,0.262],[0,0]],"v":[[-1.004,19.742],[-3.039,-9.762],[-10.161,-1.392],[-10.885,-11.496],[-4.573,-21.079],[7.492,-22.676],[10.997,22.824]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1001.095,419.988]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-17.404,1.036],[-7.723,0.449],[-4.204,0.287],[-0.199,-11.166]],"o":[[9.731,-1.21],[8.92,-0.536],[6.312,-0.349],[3.518,18.789],[0,0]],"v":[[-45.792,-14.684],[-1.054,-18.115],[24.447,-19.587],[40.515,-20.548],[45.792,20.548]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1026.296,406.046]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,-0.062],[-1.048,-8.583],[0.087,-2.27]],"o":[[0.013,0.062],[1.435,8.421],[0.687,5.664],[0,0]],"v":[[-2.651,-20.329],[-2.626,-20.142],[1.528,7.754],[2.564,20.329]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[981.982,411.691]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-10.542,-17.217]],"o":[[1.647,20.111],[0,0]],"v":[[-9.319,-28.514],[9.319,28.514]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1082.319,362.05]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-13.711,-19.325]],"o":[[1.335,23.667],[0,0]],"v":[[-11.59,-33.111],[11.59,33.111]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1077.004,366.934]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.886,-20.972]],"o":[[4.629,22.456],[0,0]],"v":[[-2.315,-31.196],[0.155,31.196]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[961.535,374.276]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.163,-15.108],[0,0]],"o":[[2.907,14.859],[0,0],[0,0]],"v":[[-1.516,-19.706],[1.353,19.606],[1.353,19.706]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[951.267,364.982]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.584,14.41]],"o":[[51.662,64.574],[0,0]],"v":[[-39.479,-28.607],[37.896,-35.967]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1004.426,369.192]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.345,21.433]],"o":[[40.222,51.961],[0,0]],"v":[[-32.73,-24.222],[32.73,-27.739]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1003.253,358.931]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.15,-0.786],[-6.25,0.2],[0.399,6.326],[0.2,0.761],[0.012,0.05],[-0.012,-0.05],[-0.25,-0.761],[-5.526,0.686],[0.461,5.714]],"o":[[0,0.798],[1.135,5.565],[6.276,-0.212],[-0.05,-0.785],[-0.012,-0.05],[0,0.05],[0.025,0.785],[1.672,5.465],[5.776,-0.711],[0,0]],"v":[[-22.961,-4.753],[-22.724,-2.383],[-10.336,7.872],[-0.231,-5.502],[-0.617,-7.834],[-0.655,-7.984],[-0.643,-7.834],[-0.231,-5.502],[13.443,4.055],[22.5,-8.072]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[837.613,133.101]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[-0.012,0.012],[0,0]],"v":[[0.006,-0.013],[0.006,0.013]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[860.107,125.017]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.137,-0.536],[0.125,0.549]],"o":[[-0.05,-0.549],[0.037,0.562]],"v":[[0.131,0.823],[-0.131,-0.823]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[859.982,124.181]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5.751,0.436],[-0.499,5.664],[0.038,0.562],[-0.05,-0.537],[-5.365,0.387],[0.15,4.978],[0.163,0.649],[-0.15,-0.599],[-6.139,0.337],[-0.324,5.464],[0.063,0.636]],"o":[[1.31,5.564],[5.764,-0.425],[0.05,-0.537],[-0.062,0.562],[0.449,5.127],[5.264,-0.374],[-0.025,-0.624],[0.038,0.662],[1.073,4.74],[4.778,-0.275],[0.038,-0.574],[0,0]],"v":[[-32.63,-2.364],[-19.968,7.118],[-8.54,-3.861],[-8.528,-5.508],[-8.54,-3.861],[2.339,4.772],[11.708,-4.784],[11.433,-6.681],[11.708,-4.784],[23.186,2.427],[32.592,-5.745],[32.568,-7.554]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[892.743,127.394]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.51,-2.408],[6.936,-2.396],[-0.088,-4.915],[4.977,0.062],[3.606,-3.318],[-5.827,1.073],[5.277,-2.283],[-8.596,-1.697],[-6.114,0.948],[-5.726,-2.694],[-8.571,-3.243],[3.58,-3.955],[-5.777,-3.219],[4.067,-1.148]],"o":[[-6.413,-3.581],[-4.754,1.646],[-4.804,-1.272],[-4.966,-0.05],[2.233,-5.339],[-4.966,-2.907],[6.5,-5.689],[1.372,-5.863],[6.262,-0.961],[6.238,-6.512],[-5.377,0.649],[6.574,-0.449],[-4.255,0.062],[-4.067,1.148]],"v":[[18.651,6.755],[-2.707,4.859],[-11.989,15.238],[-26.648,12.619],[-40.397,17.197],[-27.197,6.705],[-43.865,5.671],[-19.425,-0.793],[-5.539,-11.072],[13.024,-8.391],[38.288,-13.954],[24.228,-6.743],[43.865,-2.814],[30.578,-1.379]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[912.349,159.082]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.449,-0.936],[-22.631,1.46],[-7.485,-0.012],[0,0]],"o":[[0,0],[1.996,4.105],[8.558,7.136],[11.166,0.025],[0,0]],"v":[[-38.456,-12.208],[-37.846,-10.685],[-5.783,2.901],[19.081,12.182],[38.456,7.466]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[963.181,132.334]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-4.753,9.107],[6.912,13.836],[2.046,8.084],[0.137,0.411],[0,0]],"o":[[0,0],[4.579,-8.771],[19.313,-6.799],[-0.349,-1.385],[-0.05,-0.15],[0,0]],"v":[[-15.601,44.694],[-4.111,30.111],[-3.712,-4.622],[9.912,-41.712],[9.126,-44.47],[9.051,-44.694]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[804.864,172.456]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.41,3.668],[19.05,32.337],[40.684,-11.253],[-18.377,-38.537],[-4.341,-8.795],[-5.177,-12.488],[-4.579,-11.477],[-2.657,-6.038],[-0.324,-0.711],[-9.681,-6.201],[-5.589,-1.934],[-4.117,-0.736],[-3.519,-0.174],[-4.715,0.612],[-3.593,1.01],[-1.56,0.562],[-3.406,2.183],[-2.881,2.844],[-3.655,10.355],[-0.723,3.269],[5.489,15.645]],"o":[[-12.625,-32.923],[-20.822,-35.307],[-47.046,12.999],[4.728,7.673],[5.951,12.077],[5.54,13.349],[3.144,7.872],[0.337,0.748],[4.529,10.018],[4.828,3.094],[3.88,1.372],[3.431,0.649],[4.703,0.25],[3.668,-0.474],[1.584,-0.449],[3.892,-1.397],[3.406,-2.171],[7.399,-7.298],[1.098,-3.106],[3.443,-15.37],[-1.372,-3.917]],"v":[[93.076,18.676],[47.365,-70.413],[-52.878,-120.628],[-85.04,-28.258],[-71.442,-3.369],[-54.737,33.959],[-39.541,71.81],[-30.833,92.857],[-29.86,95.028],[-7.853,119.63],[7.842,127.215],[19.868,130.396],[30.311,131.631],[44.495,131.107],[55.411,128.887],[60.127,127.377],[71.08,121.988],[80.525,114.44],[97.229,87.467],[99.974,77.898],[97.243,30.029]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[992.162,312.777]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-52.473,25.813],[9.956,24.44],[27.634,44.239],[18.502,-2.532],[7.648,-24.839],[-5.551,-18.152],[-9.581,-18.826]],"o":[[22.906,57.189],[53.346,-29.642],[-6.774,-18.801],[-42.28,-67.693],[-25.762,3.531],[-5.577,18.128],[7.548,24.664],[10.717,21.046]],"v":[[-57.956,82.414],[70.657,128.999],[103.455,5.926],[52.966,-87.118],[-66.839,-146.953],[-118.426,-104.285],[-116.081,-52.037],[-83.669,14.934]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1000.297,322.459]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[65.173,27.584]],"o":[[4.104,-129.161],[0,0]],"v":[[85.721,159.689],[-89.825,-159.689]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1072.088,304.481]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[11.952,33.335],[38.75,35.231]],"o":[[-3.531,-44.476],[-22.306,-62.229],[0,0]],"v":[[63.776,138.923],[38.375,16.949],[-63.776,-138.923]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[864.629,341.54]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[8.769,-0.587],[14.896,-0.986],[0.038,0],[5.963,-0.399],[34.994,-2.271],[7.647,-0.499],[9.943,-0.649],[2.907,-0.187],[-0.487,14.06],[0.062,4.965],[0.087,2.433],[23.729,48.481],[29.704,29.218],[3.069,2.845],[27.584,13.649],[-46.087,6.164]],"o":[[-0.587,20.548],[-14.846,0.985],[-0.037,0.012],[-5.277,0.349],[-30.229,2.008],[-8.109,0.536],[-11.915,0.773],[-23.404,1.509],[8.147,-2.844],[0.212,-6.2],[-0.025,-2.433],[-1.797,-53.708],[-19.375,-39.623],[-3.032,-2.981],[-28.894,-26.798],[0,0],[0,0]],"v":[[242.177,154.847],[224.602,184.789],[179.989,187.747],[179.864,187.758],[162.96,188.882],[59.673,195.643],[35.957,197.19],[2.908,199.336],[-39.921,202.106],[-29.154,170.641],[-29.104,153.375],[-29.279,146.089],[-69.576,-10.719],[-145.927,-114.879],[-155.072,-123.612],[-242.178,-185.08],[-136.499,-202.106]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[935.003,332.216]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-4.878,0.623],[-7.697,0.848],[0,0],[-7.56,0.686],[-7.96,0.511],[-6.475,0.237],[-6.724,-0.05],[-10.171,-1.561]],"o":[[4.778,-0.636],[7.585,-0.948],[0,0],[7.623,-0.848],[8.234,-0.761],[6.824,-0.449],[7.348,-0.262],[6.961,0.075],[0,0]],"v":[[-82.507,5.115],[-68.01,3.219],[-45.03,0.499],[-45.005,0.499],[-22.187,-1.809],[2.166,-3.73],[22.152,-4.753],[43.311,-5.065],[82.507,-2.441]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[882,124.768]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5.191,-4.241],[-10.742,-20.323],[-0.612,-96.038],[0.063,-3.206],[0.35,-5.364]],"o":[[14.268,4.61],[52.46,32.985],[21.733,41.145],[0.025,3.132],[-0.112,5.178],[0,0]],"v":[[-106.576,-182.624],[-69.408,-165.199],[43.859,-44.509],[106.55,157.286],[106.487,166.805],[105.801,182.624]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1071.083,304.95]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.445,0.162],[13.062,-0.387],[33.46,4.841],[0.112,-1.735]],"o":[[2.358,-0.2],[-6.761,-3.967],[0,0],[-0.374,7.049],[0,0]],"v":[[28.882,20.728],[36.104,20.18],[6.886,13.967],[-35.256,-20.728],[-36.104,-5.819]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[942.078,508.678]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.684,5.839],[18.165,5.515],[12.9,-1.859],[0,0],[15.919,1.634],[-10.28,0.636],[3.643,0.037],[-12.937,9.419],[-14.272,2.059],[0,0],[-17.055,10.006],[3.132,2.358]],"o":[[0,0],[0,0],[-9.606,1.372],[0,0],[0,0],[0,0],[0,0],[0,0],[14.272,-2.046],[0,0],[0,0],[0,0]],"v":[[47.807,-16.038],[13.885,-16.212],[-1.597,-5.021],[-16.58,-11.833],[-49.978,-2.563],[-37.153,4.835],[-47.183,11.048],[-17.541,6.794],[3.281,13.318],[19.35,0.368],[49.978,-2.9],[38.25,-6.543]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1046.863,493.732]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0.013],[19.862,-2.433],[25.226,8.246],[7.772,-0.4]],"o":[[-0.012,-0.012],[-7.174,-8.546],[0,0],[-5.514,-1.809],[0,0]],"v":[[43.784,25.014],[43.771,24.989],[4.797,12.351],[-23.948,-22.269],[-43.784,-24.614]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[950.905,502.87]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[9.394,-3.668],[3.256,-24.402],[9.182,-18.489]],"o":[[0,0],[-10.729,4.179],[0,0],[0,0]],"v":[[38.906,-28.676],[21.302,-25.93],[-4.947,12.818],[-38.906,29.599]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1136.831,490.925]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[7.91,-0.761],[0.387,-0.025],[7.623,1.821],[2.021,0.849],[3.955,6.337],[2.669,10.591]],"o":[[-3.369,-0.287],[-0.374,0.037],[-7.173,0.637],[-2.133,-0.499],[-5.864,-2.445],[0,0],[0,0]],"v":[[34.645,13.492],[16.742,15.239],[15.594,15.338],[-7.324,14.303],[-13.561,12.294],[-28.657,-0.493],[-34.645,-16.124]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1049.807,318.273]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-3.718,0.512],[-2.969,1.048],[-1.697,1.123],[-1.435,2.396],[-0.487,2.757],[1.172,15.257]],"o":[[3.443,-0.299],[3.243,-0.449],[1.996,-0.686],[2.233,-1.435],[1.21,-1.958],[0,0],[0,0]],"v":[[-17.902,19.961],[-6.998,18.888],[2.445,16.742],[8.01,14.06],[13.586,8.383],[16.156,1.335],[16.731,-19.961]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[956.725,326.201]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1.939,-3.751],[1.94,3.751]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[973.699,287.83]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.202,-3.787],[2.202,3.787]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[992.3,286.011]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,0.374],[0,0],[0,0],[0,0],[7.785,-5.065],[0.499,-0.262],[1.023,-0.386],[0.025,-0.012],[2.345,-0.387],[1.934,0.062],[1.31,0.224]],"o":[[-10.604,-3.156],[0,0],[0,0],[0,0],[-0.437,0.287],[-0.836,0.449],[-0.012,0],[-1.734,0.637],[-2.134,0.349],[-1.435,-0.025],[-1.335,-0.212]],"v":[[-7.785,7.117],[-24.328,-3.999],[7.66,-7.03],[22.743,-8.452],[16.543,4.335],[15.158,5.147],[12.388,6.393],[12.326,6.418],[6.238,7.978],[0.137,8.39],[-3.98,8.004]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[982.438,289.242]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.637,3.113],[-1.636,-3.113]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[992.466,300.133]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.884,5.732],[-1.884,-5.732]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[961.004,268.806]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[6.806,7.803],[-6.805,-7.803]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[999.473,264.328]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.087,-0.324]],"o":[[0,0],[0,0]],"v":[[-0.081,-0.25],[0.081,0.25]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[996.042,238.21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,-0.012],[9.108,-6.488]],"o":[[0.012,0.012],[0.749,2.358],[0,0]],"v":[[1.547,-9.133],[1.559,-9.095],[-4.554,9.133]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[994.589,247.604]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.467,-0.624],[4.529,6.524],[0.013,0.025],[0,0],[-1.771,1.822],[-15.856,-0.723],[-5.838,-2.171],[-0.436,-0.174],[-0.025,-0.012],[-0.149,-0.063]],"o":[[-3.816,2.789],[-18.651,2.595],[-0.025,-0.025],[-2.832,-4.092],[0,0],[4.529,-4.603],[4.804,0.225],[0.425,0.149],[0.013,0.013],[0.137,0.062],[0,0]],"v":[[26.189,10.884],[10.227,16.337],[-22.422,4.485],[-22.472,4.41],[-26.189,-3.2],[-23.52,-6.307],[7.083,-18.209],[23.052,-14.79],[24.337,-14.304],[24.4,-14.279],[24.836,-14.104]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[963.415,246.039]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[50.953,-4.398],[25.989,-2.264],[-17.289,1.429],[-50.953,4.398]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[980.327,274.832]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[13.605,-1.285],[-13.605,1.285]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[955.558,220.494]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.633,-4.554]],"o":[[1.984,0.936],[0,0]],"v":[[-4.154,-3.35],[4.154,3.35]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[991.969,235.11]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.087,-0.163]],"o":[[0.1,0.15],[0,0]],"v":[[-0.131,-0.231],[0.131,0.231]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[996.279,238.74]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.518,-17.179],[1.668,-17.042],[36.612,14.809],[70.157,42.318],[20.032,47.021],[17.075,47.595],[-21.724,50.34],[-70.157,54.045],[-65.755,24.016],[-59.152,-12.563],[-54.486,-33.635],[-49.957,-54.045],[-25.517,-36.541],[-8.488,-24.34]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[995.13,255.127]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.187,-4.117],[5.016,-0.15],[0.187,4.117],[-5.027,0.162]],"o":[[0.187,4.117],[-5.027,0.15],[-0.2,-4.117],[5.016,-0.15]],"v":[[9.095,-0.275],[0.349,7.461],[-9.082,0.287],[-0.337,-7.46]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[744.487,154.473]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.125,-0.125],[8.059,-1.197],[-0.574,0]],"o":[[0.137,0.112],[-8.596,1.16],[-1.809,-0.386],[1.759,0]],"v":[[13.593,-1.959],[13.992,-1.596],[-11.06,1.959],[-13.417,1.572]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[785.502,131.479]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-8.596,1.16],[17.503,-5.414],[9.17,-5.576],[0,0],[-1.734,16.655],[0,0],[1.185,11.153],[3.717,0.798]],"o":[[7.386,7.024],[9.606,16.493],[0,0],[0.399,0.187],[1.759,-17.017],[0,0],[-0.786,-7.51],[8.059,-1.198]],"v":[[14.708,-39.155],[4.029,-3.699],[-4.48,39.155],[-19.151,25.669],[-7.998,13.318],[-22.095,-11.334],[0.798,-24.833],[-10.343,-35.599]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[784.785,169.038]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.349,-1.385],[19.313,-6.799],[4.579,-8.771],[0,0],[0,0],[3.069,2.845],[0,0],[9.606,16.493],[7.386,7.024],[-4.878,0.624]],"o":[[2.046,8.084],[6.912,13.836],[-4.753,9.107],[0,0],[-3.032,-2.982],[0,0],[9.17,-5.576],[17.503,-5.414],[4.778,-0.636],[0.137,0.412]],"v":[[14.578,-41.919],[0.954,-4.829],[0.555,29.904],[-10.935,44.488],[-11.122,44.676],[-20.267,35.942],[-19.893,35.53],[-11.384,-7.324],[-0.705,-42.78],[13.792,-44.676]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[800.198,172.662]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[5.776,-0.711],[1.672,5.464],[0.2,0.761],[-7.561,0.686],[0,0],[-0.137,-0.536]],"o":[[0.462,5.714],[-5.527,0.687],[-0.05,-0.786],[7.623,-0.848],[0,0],[0.037,0.562],[-0.012,0.013]],"v":[[11.328,-5.371],[2.271,6.755],[-11.403,-2.801],[-11.79,-5.134],[11.029,-7.442],[11.066,-7.043],[11.328,-5.396]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[848.785,130.401]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.531,-44.476],[0,0],[7.773,-0.399],[0,0],[23.729,48.481],[29.705,29.218],[0,0],[-4.753,9.107],[0,0],[-22.306,-62.229]],"o":[[0,0],[-5.514,-1.809],[0,0],[-1.796,-53.708],[-19.374,-39.623],[0,0],[0,0],[0,0],[38.75,35.231],[11.952,33.335]],"v":[[69.664,138.88],[68.217,139.017],[48.38,136.672],[46.983,136.722],[6.686,-20.086],[-69.665,-124.246],[-69.478,-124.433],[-57.988,-139.017],[-57.888,-138.967],[44.263,16.905]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[858.741,341.584]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.776,-3.219],[4.067,-1.148],[1.51,-2.408],[6.937,-2.396],[-0.087,-4.915],[4.978,0.063],[3.605,-3.318],[-5.826,1.073],[5.277,-2.284],[-8.596,-1.697],[-6.113,0.948],[-5.726,-2.694],[-8.57,-3.244],[3.581,-3.955]],"o":[[-4.254,0.062],[-4.067,1.147],[-6.412,-3.581],[-4.753,1.646],[-4.803,-1.272],[-4.965,-0.049],[2.233,-5.34],[-4.965,-2.907],[6.499,-5.689],[1.372,-5.864],[6.263,-0.961],[6.238,-6.512],[-5.377,0.649],[6.575,-0.449]],"v":[[43.865,-2.813],[30.577,-1.378],[18.65,6.756],[-2.708,4.86],[-11.99,15.239],[-26.648,12.619],[-40.397,17.198],[-27.198,6.706],[-43.865,5.671],[-19.424,-0.792],[-5.54,-11.072],[13.024,-8.39],[38.287,-13.954],[24.227,-6.743]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[912.349,159.082]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.886,-20.972],[0,0],[3.144,7.872],[2.907,14.858],[0,0],[-2.969,1.048]],"o":[[4.629,22.456],[0,0],[-2.657,-6.038],[0.163,-15.108],[0,0],[3.244,-0.449],[0,0]],"v":[[2.433,-31.208],[4.903,31.183],[4.541,31.345],[-4.167,10.299],[-7.036,-29.012],[-7.061,-29.199],[2.383,-31.345]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[956.788,374.289]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-2.832,-4.092],[-0.025,-0.025],[-8.995,-1.697],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0.013,0.025],[3.306,5.514],[0,0],[0,0]],"v":[[17.242,13.642],[17.392,15.364],[-17.391,18.333],[-16.272,18.246],[-9.668,-18.333],[-8.421,-18.059],[-4.703,-10.449],[-4.653,-10.374],[13.624,1.441],[13.474,2.177]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[945.647,260.897]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-2.919,-0.237],[4.529,-4.604],[-12.576,1.647]],"o":[[-15.857,-0.724],[2.558,-3.893],[3.443,-0.449]],"v":[[15.302,-5.589],[-15.302,6.313],[5.758,-5.327]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[1,0.878,0.839,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[955.197,233.419]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[14.485,7.154],[14.036,7.779],[-13.174,10.349],[-14.484,10.062],[-9.955,-10.349]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[955.128,211.431]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[7.987,-1.328],[4.044,0.58],[1.21,0.374],[0,0]],"o":[[-2.032,3.204],[-4.038,1.029],[-1.335,-0.212],[-4.467,-1.559],[0,0]],"v":[[18.357,-4.305],[5.813,3.276],[-6.312,3.664],[-10.117,2.778],[-18.357,-1.227]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[984.77,293.581]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.435,-0.025],[2.233,0.786],[-1.335,-0.212]],"o":[[-3.019,0.075],[1.21,0.375],[1.31,0.225]],"v":[[3.961,0.599],[-3.961,-0.674],[-0.156,0.212]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[978.614,297.033]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.564,-2.745],[7.822,6.113],[5.302,4.017],[-0.773,-0.013],[-6.849,-2.133]],"o":[[-7.96,0.961],[-6.799,1.909],[0.636,0.025],[10.068,0.785],[5.502,1.709]],"v":[[22.188,5.077],[-3.562,-1.348],[-22.188,-6.039],[-20.055,-5.976],[5.57,-1.66]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[964.953,126.489]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[4.804,0.225],[3.444,-0.449],[2.558,-3.892],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-5.838,-2.171],[-2.919,-0.237],[-12.575,1.647],[-1.771,1.822],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[25.332,0.074],[25.157,0.537],[9.188,-2.882],[-0.356,-2.62],[-21.415,9.02],[-24.084,12.126],[-25.332,11.852],[-20.666,-9.219],[-19.356,-8.934],[7.853,-11.503],[8.303,-12.126]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[961.31,230.712]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-8.344,-4.205],[0.125,0],[5.502,1.709],[10.068,0.786],[0,0]],"o":[[0,0],[-0.125,0.012],[-5.564,-2.745],[-6.849,-2.133],[3.007,0.062],[0,0]],"v":[[-1.572,-3.419],[21.059,5.49],[21.059,5.526],[4.441,-1.211],[-21.184,-5.526],[-10.174,-4.391]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[966.082,126.04]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-2.57,-1.747],[3.494,-6.15],[1.447,-1.322],[2.109,-0.848],[-0.175,2.944],[0,0],[0,0],[-4.192,-1.085]],"o":[[3.418,3.481],[-1.197,2.371],[-1.472,1.048],[-7.273,1.634],[2.969,-2.233],[0,0],[4.716,-9.519],[4.179,1.772]],"v":[[10.087,-8.889],[10.648,5.695],[6.656,11.172],[1.315,14.029],[-13.13,6.693],[-4.036,0.992],[-14.141,-5.009],[0.007,-14.578]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[983.38,245.564]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-0.012],[9.107,-6.487],[-1.198,2.371],[3.418,3.481],[-0.773,-0.499],[0,0]],"o":[[0.749,2.358],[1.447,-1.322],[3.493,-6.15],[0.998,0.674],[0,0],[0.012,0.012]],"v":[[1.56,-8.197],[-4.554,10.031],[-0.562,4.553],[-1.122,-10.031],[1.534,-8.247],[1.547,-8.234]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[1,0.878,0.839,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[994.589,246.706]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.749,2.358],[0.012,0.012],[-0.063,-0.037],[8.197,-7.224],[6.637,-1.247],[5.264,0.985],[3.306,5.514],[-18.651,2.595],[-2.956,1.198],[-1.697,1.547]],"o":[[0,-0.012],[0.062,0.05],[0,0],[-2.882,2.545],[-7.411,1.385],[-8.995,-1.697],[4.528,6.525],[4.466,-0.624],[1.822,-0.412],[9.107,-6.487]],"v":[[25.469,-13.212],[25.457,-13.249],[25.644,-13.125],[21.489,4.242],[7.554,10.217],[-11.409,10.617],[-29.686,-1.198],[2.963,10.654],[14.016,7.872],[19.356,5.015]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[1,0.878,0.839,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[970.679,251.721]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[1.023,-0.387],[0.025,-0.013],[4.18,-0.537],[0.761,-0.012],[1.31,0.225],[0,0],[0,0],[0,0]],"o":[[0,0],[-0.836,0.45],[-0.013,0],[-2.507,0.786],[-0.786,0.1],[-1.435,-0.025],[0,0],[0,0],[0,0],[0,0]],"v":[[9.943,-0.387],[10.979,0.674],[8.209,1.922],[8.146,1.946],[-1.722,3.755],[-4.042,3.917],[-8.159,3.531],[-8.06,2.894],[-10.979,-2.133],[7.885,-3.917]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[986.617,293.714]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.836,0.449],[-0.437,0.287],[1.635,-0.524]],"o":[[0.499,-0.262],[-1.16,0.886],[1.023,-0.387]],"v":[[0.693,-0.219],[2.077,-1.03],[-2.078,1.03]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[996.903,294.607]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.828,-3.044],[0,0],[11.165,0.025],[8.558,7.136],[1.996,4.104],[0.062,0.636],[0,0],[-6.063,-0.474],[0.637,0.025],[-6.799,1.909],[-7.96,0.96]],"o":[[0,0],[0,0],[-7.486,-0.013],[-22.631,1.459],[0.037,-0.574],[0,0],[6.961,0.075],[-0.774,-0.012],[5.303,4.017],[7.822,6.113],[4.853,2.395]],"v":[[38.182,7.629],[38.145,7.679],[18.77,12.395],[-6.094,3.113],[-38.157,-10.473],[-38.182,-12.282],[-38.182,-12.42],[-18.595,-11.609],[-20.729,-11.671],[-2.102,-6.98],[23.648,-0.555]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[963.493,132.122]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[1.472,-1.098],[0.499,-0.262],[0,0]],"o":[[0,0],[-1.061,1.996],[-0.437,0.287],[0,0],[0,0]],"v":[[-4.086,-1.909],[4.086,-2.682],[0.393,1.871],[-0.992,2.682],[-2.028,1.622]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[998.588,291.706]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-7.411,1.385],[-2.882,2.546],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[5.265,0.985],[6.637,-1.247],[0,0],[0,0]],"v":[[23.561,6.019],[23.598,6.456],[-19.68,10.149],[-19.831,8.427],[-23.597,-3.037],[-23.448,-3.773],[-4.484,-4.174],[9.451,-10.149],[9.95,-9.588]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[982.718,266.112]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.212,-14.971],[0,0],[3.88,1.373],[0,0],[1.035,8.983],[1.909,-1.56],[-0.112,0.499],[-1.971,2.919],[-0.274,-0.15]],"o":[[0,0],[-4.117,-0.736],[0,0],[0.2,-12.014],[-2.034,1.984],[-0.262,-3.007],[0.063,-0.312],[3.693,-0.537],[0.449,0.262]],"v":[[10.998,22.643],[10.936,23.006],[-1.091,19.824],[-1.004,19.561],[-3.037,-9.944],[-10.161,-1.572],[-10.885,-11.678],[-4.572,-21.259],[7.492,-22.855]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1001.095,420.168]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-17.404,1.036],[0,0],[0.399,-0.112],[0.673,-1.572],[-0.537,-9.331],[-2.982,-6.1],[0,0],[3.431,0.648],[0,0],[0.449,0.262],[3.693,-0.537],[0.062,-0.312],[-0.262,-3.006],[0,0],[1.435,8.421],[0,0]],"o":[[0,0],[-0.4,0],[-1.559,0.449],[-2.658,6.15],[0.424,7.661],[0,0],[-3.518,-0.175],[0,0],[-0.213,-14.971],[-0.275,-0.15],[-1.971,2.919],[-0.112,0.499],[0,0],[-1.048,-8.584],[0,0],[9.731,-1.21]],"v":[[22.737,-28.239],[23.149,-21.639],[21.951,-21.477],[18.321,-18.233],[14.965,6.493],[19.98,27.964],[19.967,28.239],[9.525,27.004],[9.588,26.642],[6.082,-18.857],[-5.982,-17.26],[-12.295,-7.679],[-11.571,2.426],[-18.994,3.275],[-23.149,-24.621],[-22.001,-24.808]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1002.505,416.17]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.287,-5.602],[2.171,-0.112],[0.287,5.601],[-2.184,0.112]],"o":[[0.287,5.602],[-2.171,0.112],[-0.287,-5.602],[2.17,-0.112]],"v":[[3.93,-0.206],[0.524,10.149],[-3.93,0.206],[-0.511,-10.149]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1027.587,422.096]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[8.196,-7.223],[0,0],[0,0]],"v":[[19.786,14.609],[19.324,15.108],[-5.64,17.241],[-5.677,16.806],[-19.288,1.199],[-19.787,0.637],[-15.633,-16.73],[-15.159,-17.241]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1011.955,255.326]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.864,-2.445],[0,0],[40.222,51.961],[0,0],[-0.487,2.758],[1.172,15.258],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-2.345,21.433],[0,0],[1.21,-1.959],[0,0],[0,0],[0,0],[0,0],[2.67,10.592],[3.955,6.338]],"v":[[32.967,-13.842],[32.705,-13.217],[-32.755,-9.699],[-32.967,-9.824],[-30.397,-16.874],[-29.823,-38.169],[-29.873,-38.942],[8.926,-41.687],[11.883,-42.262],[17.871,-26.629]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1003.279,344.409]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.147,-6.063],[0,0],[4.703,0.25],[0,0],[0.424,7.66],[0,0],[-2.171,0.112],[0.287,5.601]],"o":[[0.474,7.71],[0,0],[-4.716,0.611],[0,0],[-2.981,-6.101],[0,0],[0.287,5.602],[2.171,-0.113],[0,0]],"v":[[9.962,-11.565],[8.64,9.943],[8.752,10.792],[-5.433,11.315],[-5.421,11.041],[-10.436,-10.43],[-4.248,-10.792],[0.206,-0.848],[3.612,-11.203]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1027.905,433.093]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-2.133,-0.499],[0,0],[51.662,64.575],[0,0],[-1.435,2.395],[0,0],[-2.345,21.434],[0,0]],"o":[[0,0],[1.584,14.409],[0,0],[2.233,-1.435],[0,0],[40.222,51.962],[0,0],[2.021,0.849]],"v":[[38.164,-35.288],[38.001,-34.638],[-39.374,-27.279],[-39.586,-27.602],[-34.008,-33.279],[-33.797,-33.154],[31.664,-36.672],[31.925,-37.297]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1004.32,367.864]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.012,-0.349],[2.17,-0.112],[0.287,5.602],[0,0.45],[-1.983,0.087],[-0.461,-5.128]],"o":[[0.287,5.602],[-2.171,0.113],[-0.025,-0.474],[0,-4.965],[2.034,-0.1],[0.038,0.337]],"v":[[3.805,-0.206],[0.387,10.136],[-4.055,0.193],[-4.092,-1.192],[-0.649,-10.149],[3.73,-1.229]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1053.325,418.603]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.16,0.886],[0,0],[0,0],[0,0],[-10.604,-3.156],[-3.019,0.074],[-2.133,0.349],[-1.734,0.637],[-0.013,0]],"o":[[7.785,-5.065],[0,0],[0,0],[0,0],[2.233,0.786],[1.934,0.062],[2.345,-0.387],[0.025,-0.012],[1.634,-0.524]],"v":[[3.851,4.023],[10.051,-8.764],[-5.032,-7.342],[-37.02,-4.311],[-20.477,6.806],[-12.555,8.079],[-6.454,7.666],[-0.366,6.106],[-0.303,6.082]],"c":true}},"nm":"P"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[70.157,7.89],[20.032,12.595],[17.075,13.168],[-21.724,15.913],[-70.157,19.618],[-65.755,-10.41],[-64.533,-10.324],[-32.092,-13.293],[11.187,-16.986],[36.151,-19.119],[36.612,-19.618]],"c":true}},"nm":"P"},{"ty":"mm","mm":1,"nm":"M"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[995.13,289.554]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.699,-8.009],[0,0],[2.034,-0.1],[0,-4.965],[0,0],[-2.358,5.477],[-1.547,0.449],[-0.399,0],[-1.272,-1.846]],"o":[[0,0],[-0.461,-5.127],[-1.983,0.087],[0,0],[-0.138,-8.433],[0.687,-1.572],[0.399,-0.112],[2.071,0],[3.955,5.751]],"v":[[10.236,12.126],[4.185,12.625],[-0.194,3.706],[-3.637,12.662],[-10.099,12.713],[-6.644,-9.307],[-3.013,-12.55],[-1.816,-12.713],[3.586,-9.456]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1052.869,404.748]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.2,-11.166],[0,0],[3.406,-2.171],[0,0],[0.537,5.939],[3.955,5.752],[2.071,0],[0,0],[-4.205,0.287]],"o":[[0,0],[-2.882,2.844],[0,0],[0.586,-5.365],[-0.698,-8.009],[-1.273,-1.846],[0,0],[6.313,-0.35],[3.518,18.788]],"v":[[10.374,16.461],[10.973,17.085],[1.528,24.634],[1.167,24.085],[1.391,6.742],[-5.259,-14.84],[-10.661,-18.097],[-10.973,-23.673],[5.096,-24.634]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1061.715,410.132]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.984,0.936],[0.012,0.012],[0.424,0.15],[0,0],[0,0],[0,0],[0,0],[0,0],[1.937,0.43],[0,0],[0,0],[4.728,7.673],[-47.047,13],[-20.822,-35.306],[-12.625,-32.923],[0,0],[7.909,-0.761],[0.387,-0.025],[7.623,1.821],[2.021,0.849],[3.955,6.337],[2.67,10.592],[0,0],[0,0],[0,0],[0,0],[0.062,0.05]],"o":[[-2.632,-4.554],[-0.025,-0.012],[-0.437,-0.175],[0,0],[0,0],[0,0],[0,0],[0,0],[1.938,0.43],[0,0],[0,0],[-4.342,-8.796],[-18.377,-38.537],[40.683,-11.253],[19.051,32.337],[0,0],[-3.368,-0.287],[-0.375,0.037],[-7.174,0.636],[-2.133,-0.499],[-5.864,-2.445],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.063,-0.037],[0,0]],"v":[[9.133,-19.188],[0.824,-25.887],[0.761,-25.912],[-0.523,-26.399],[-0.35,-26.861],[-17.379,-39.061],[-41.819,-56.564],[-46.348,-36.155],[-51.014,-15.084],[-57.617,21.496],[-62.018,51.525],[-66.271,51.762],[-79.87,26.873],[-47.707,-65.498],[52.535,-15.283],[98.246,73.806],[97.46,74.118],[79.558,75.866],[78.41,75.965],[55.492,74.929],[49.254,72.921],[34.158,60.134],[28.17,44.501],[74.929,39.798],[44.75,12.288],[9.806,-19.561],[9.332,-19.051],[9.145,-19.176]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[986.991,257.647]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.048,-8.584],[0.087,-2.27],[0,0],[4.528,10.018],[0.336,0.749],[0,0],[4.629,22.456],[0,0],[-1.696,1.123],[0,0],[1.584,14.409],[0,0],[-7.173,0.636],[0,0],[-13.711,-19.325],[0,0],[7.398,-7.298],[0,0],[3.518,18.789],[6.313,-0.349],[8.92,-0.536],[9.731,-1.21]],"o":[[1.435,8.421],[0.687,5.664],[0,0],[-9.681,-6.2],[-0.325,-0.711],[0,0],[0.886,-20.972],[0,0],[1.996,-0.686],[0,0],[51.662,64.574],[0,0],[7.623,1.822],[0,0],[1.335,23.666],[0,0],[-3.655,10.354],[0,0],[-0.199,-11.165],[-4.204,0.287],[-7.722,0.449],[-17.404,1.036],[0,0]],"v":[[-44.925,9.057],[-40.771,36.954],[-39.735,49.528],[-39.972,49.915],[-61.979,25.314],[-62.952,23.142],[-62.591,22.98],[-65.061,-39.41],[-65.111,-39.548],[-59.547,-42.23],[-59.334,-41.905],[18.04,-49.267],[18.202,-49.915],[41.12,-48.879],[41.133,-48.668],[64.313,17.553],[65.111,17.754],[48.406,44.725],[47.807,44.101],[42.53,3.007],[26.461,3.967],[0.961,5.439],[-43.777,8.871]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1024.281,382.492]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-10.542,-17.217],[0,0],[1.098,-3.106],[0,0],[1.335,23.667],[0,0],[-0.374,0.038],[0,0]],"o":[[0,0],[-0.724,3.269],[0,0],[-13.711,-19.325],[0,0],[0.387,-0.025],[0,0],[1.647,20.111]],"v":[[12.869,23.685],[13.368,23.798],[10.623,33.366],[9.825,33.168],[-13.355,-33.055],[-13.368,-33.267],[-12.22,-33.366],[-5.77,-33.342]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1078.769,366.878]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.372,-3.917],[3.443,-15.37],[0,0],[1.647,20.111],[0,0],[-3.368,-0.287],[0,0]],"o":[[5.489,15.645],[0,0],[-10.542,-17.216],[0,0],[7.91,-0.761],[0,0],[1.41,3.668]],"v":[[8.341,-18.258],[11.073,29.611],[10.573,29.499],[-8.065,-27.528],[-14.516,-27.552],[3.388,-29.299],[4.173,-29.611]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1081.064,361.064]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[14.272,-2.046],[0,0],[0,0],[0,0],[0,0],[0,0],[-9.606,1.372],[0,0],[0,0]],"o":[[3.131,2.358],[-17.054,10.005],[0,0],[-14.273,2.059],[-12.937,9.419],[3.643,0.037],[-10.28,0.636],[15.919,1.634],[0,0],[12.9,-1.859],[18.165,5.515],[-1.685,5.839]],"v":[[38.25,-6.544],[49.977,-2.901],[19.349,0.368],[3.281,13.317],[-17.542,6.793],[-47.184,11.047],[-37.153,4.835],[-49.978,-2.564],[-16.581,-11.834],[-1.598,-5.022],[13.885,-16.212],[47.807,-16.038]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1046.863,493.732]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-14.846,0.986],[0.012,-0.3],[26.436,-1.747],[-0.225,0.174]],"o":[[-0.013,0.287],[-0.05,1.197],[0.212,-0.187],[14.896,-0.986]],"v":[[22.631,-1.753],[22.594,-0.867],[-22.631,1.753],[-21.982,1.204]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1136.975,518.758]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.391,2.757],[4.624,-10.238],[4.778,-0.312]],"o":[[-0.599,9.22],[-4.073,3.633],[1.048,-17.615]],"v":[[8.64,-14.715],[2.661,8.661],[-8.64,14.715]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1168.245,502.29]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-32.661,6.088],[0.349,-5.364],[1.048,-17.616],[14.896,-0.985],[-13.699,1.385]],"o":[[-0.112,5.177],[-4.392,2.757],[-14.846,0.985],[6.2,-5.177],[0,0]],"v":[[31.289,-24.103],[30.603,-8.284],[13.324,21.147],[-31.289,24.103],[-2.158,13.374]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1146.282,495.859]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.162,0.025],[0,0],[1.011,-0.649],[-0.112,5.177]],"o":[[0,0],[0,0],[0.35,-5.365],[0.163,-0.038]],"v":[[0.362,-7.96],[0.811,7.012],[-0.811,7.96],[-0.125,-7.859]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1177.695,479.615]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.475,7.71],[4.329,6.263],[2.058,0],[0,0],[-7.722,0.45],[0,0],[0.399,-0.112],[0.686,-1.572],[-0.137,-8.434],[-2.982,-6.75],[0,0],[3.668,-0.474]],"o":[[1.148,-6.063],[-0.524,-8.746],[-1.26,-1.846],[0,0],[8.92,-0.537],[0,0],[-0.399,0],[-1.547,0.449],[-2.358,5.477],[0.138,8.271],[0,0],[-3.593,1.01],[0,0]],"v":[[-1.603,27.865],[-0.281,6.357],[-7.105,-17.385],[-12.494,-20.641],[-12.906,-27.24],[12.594,-28.713],[12.906,-23.136],[11.709,-22.974],[8.078,-19.73],[4.622,2.29],[9.189,25.894],[9.426,26.493],[-1.491,28.713]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1038.148,415.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[9.394,-3.668],[0,0],[65.173,27.584],[0,0],[0,0],[0,0],[-10.742,-20.323],[-0.611,-96.039]],"o":[[0,0],[0,0],[4.105,-129.161],[0,0],[11.166,0.025],[0,0],[52.461,32.986],[21.732,41.145],[0,0]],"v":[[95.789,159.876],[78.186,162.622],[77.861,161.797],[-97.685,-157.582],[-97.685,-157.856],[-78.31,-162.572],[-78.273,-162.622],[34.995,-41.932],[97.685,159.865]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1079.948,302.373]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-6.725,-0.05],[0,0],[0.037,-0.574],[4.778,-0.275],[1.073,4.741],[0.162,0.648]],"o":[[7.348,-0.262],[0,0],[0.062,0.637],[-0.325,5.465],[-6.138,0.337],[-0.025,-0.624],[0,0]],"v":[[-10.61,-4.891],[10.549,-5.202],[10.549,-5.065],[10.573,-3.256],[1.167,4.916],[-10.312,-2.295],[-10.586,-4.191]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[914.763,124.905]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[1.21,-1.959],[2.234,-1.434],[1.996,-0.686],[3.244,-0.449],[3.444,-0.299],[0,0],[5.951,12.076],[0,0],[0,0]],"o":[[1.173,15.258],[-0.486,2.757],[-1.435,2.395],[-1.696,1.123],[-2.969,1.048],[-3.718,0.512],[0,0],[-5.177,-12.488],[0,0],[0,0],[0,0]],"v":[[25.781,-19.862],[25.207,1.434],[22.637,8.483],[17.06,14.16],[11.496,16.842],[2.052,18.987],[-8.852,20.061],[-10.249,20.634],[-26.954,-16.693],[-26.467,-16.93],[25.731,-20.634]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[947.674,326.101]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-15.857,-0.724],[-3.618,-1.534],[4.716,-9.519],[0,0],[2.97,-2.234],[-7.273,1.635],[4.466,-0.623],[4.529,6.525],[0.012,0.024],[0.462,2.52],[-1.285,1.959]],"o":[[5.003,0.387],[-4.191,-1.085],[0,0],[0,0],[-0.174,2.944],[-2.956,1.198],[-18.651,2.595],[-0.025,-0.025],[-1.385,-2.333],[0,0],[4.529,-4.604]],"v":[[9.064,-18.208],[21.951,-15.052],[7.804,-5.483],[17.909,0.518],[8.814,6.22],[23.261,13.555],[12.208,16.337],[-20.441,4.485],[-20.491,4.411],[-23.261,-2.95],[-21.539,-6.306]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[1,0.878,0.839,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[961.434,246.038]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-3.718,0.512],[0,0],[0.162,-15.108],[5.539,13.349]],"o":[[3.443,-0.299],[0,0],[2.907,14.859],[-4.578,-11.477],[0,0]],"v":[[-6.281,-18.676],[4.622,-19.749],[4.647,-19.562],[7.516,19.749],[-7.679,-18.102]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[945.104,364.838]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-6.762,-3.967],[2.357,-0.2],[9.943,-0.649],[0,0],[-0.012,0.324],[-0.375,7.049],[0,0]],"o":[[-2.445,0.162],[-11.915,0.773],[-5.077,-20.548],[0,0],[0.113,-1.735],[33.46,4.841],[13.062,-0.387]],"v":[[36.117,19.106],[28.894,19.655],[-4.155,21.801],[-36.118,-6.394],[-36.093,-6.892],[-35.244,-21.801],[6.899,12.894]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[942.066,509.751]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.892,-1.397],[1.585,-0.45],[0,0],[0.137,8.271],[0,0],[-0.025,-0.474],[-2.171,0.112],[0.286,5.602],[0.037,0.337],[0,0],[0.586,-5.365],[0,0]],"o":[[-1.56,0.561],[0,0],[-2.982,-6.75],[0,0],[0,0.449],[0.287,5.602],[2.171,-0.113],[-0.013,-0.349],[0,0],[0.537,5.939],[0,0],[-3.406,2.184]],"v":[[-0.917,10.885],[-5.633,12.395],[-5.87,11.796],[-10.436,-11.808],[-3.974,-11.858],[-3.936,-10.474],[0.505,-0.53],[3.924,-10.873],[3.849,-11.896],[9.899,-12.395],[9.675,4.947],[10.037,5.495]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1053.206,429.27]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-11.914,0.773],[0.324,1.36],[0.037,0.112]],"o":[[-17.74,1.497],[-0.025,-0.113],[9.943,-0.649]],"v":[[16.524,-1.922],[-16.437,0.562],[-16.524,0.225]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[954.436,531.328]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.15,-0.599],[5.265,-0.374],[0.449,5.128],[0.038,0.562],[0,0],[-6.475,0.237],[0,0]],"o":[[0.15,4.978],[-5.365,0.387],[0.05,-0.536],[0,0],[6.825,-0.449],[0,0],[0.037,0.661]],"v":[[10.068,-3.674],[0.699,5.882],[-10.18,-2.751],[-10.168,-4.398],[-10.218,-5.246],[9.769,-6.269],[9.794,-5.57]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[894.384,126.283]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.067,1.148],[-4.255,0.063],[6.575,-0.449],[-5.377,0.649],[6.238,-6.513],[6.263,-0.96],[1.373,-5.863],[6.5,-5.689],[-4.966,-2.907],[2.234,-5.339],[-4.966,-0.05],[-4.803,-1.273],[-4.754,1.647],[-6.412,-3.58]],"o":[[4.067,-1.148],[-5.777,-3.218],[3.58,-3.954],[-8.571,-3.243],[-5.727,-2.695],[-6.113,0.949],[-8.596,-1.696],[5.277,-2.283],[-5.826,1.073],[3.606,-3.318],[4.978,0.062],[-0.088,-4.916],[6.936,-2.395],[1.51,-2.408]],"v":[[-38.406,-167.051],[-25.12,-168.486],[-44.757,-172.415],[-30.697,-179.626],[-55.96,-174.061],[-74.523,-176.744],[-88.41,-166.464],[-112.85,-160.001],[-96.182,-158.966],[-109.382,-148.474],[-95.633,-153.052],[-80.974,-150.432],[-71.692,-160.812],[-50.333,-158.916]],"c":true}},"nm":"P"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[-17.054,10.006],[3.132,2.357],[-1.685,5.839],[18.165,5.514],[12.899,-1.859],[0,0],[15.919,1.635],[-10.28,0.637],[3.643,0.038],[-12.938,9.419],[-14.273,2.058],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-9.607,1.372],[0,0],[0,0],[0,0],[0,0],[0,0],[14.272,-2.046],[0,0]],"v":[[115.506,166.077],[103.779,162.434],[113.336,152.939],[79.414,152.766],[63.932,163.957],[48.948,157.145],[15.551,166.413],[28.376,173.811],[18.345,180.025],[47.988,175.771],[68.81,182.296],[84.878,169.345]],"c":true}},"nm":"P"},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[-52.473,25.812],[9.956,24.44],[27.633,44.239],[18.501,-2.533],[7.647,-24.839],[-5.552,-18.152],[-9.581,-18.826],[0,0]],"o":[[53.346,-29.642],[-6.774,-18.801],[-42.281,-67.694],[-25.763,3.531],[-5.577,18.127],[7.548,24.665],[10.717,21.047],[22.905,57.188]],"v":[[89.62,126.704],[122.418,3.631],[71.929,-89.413],[-47.876,-149.247],[-99.463,-106.58],[-97.117,-54.332],[-64.706,12.638],[-38.993,80.12]],"c":true}},"nm":"P"},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[9.182,-18.489],[0,0],[34.995,-2.271],[19.862,-2.433],[25.226,8.247],[0,0],[11.951,33.335],[38.75,35.231],[0,0],[6.911,13.836],[2.046,8.084],[0,0],[-6.251,0.2],[0.399,6.325],[-5.527,0.687],[0.462,5.714],[-5.751,0.436],[-0.499,5.664],[-5.365,0.387],[0.149,4.978],[-6.139,0.337],[-0.324,5.464],[-22.631,1.46],[-7.485,-0.012],[0,0],[4.105,-129.162],[0,0],[3.256,-24.403]],"o":[[0,0],[-30.229,2.008],[-7.173,-8.546],[0,0],[0,0],[-3.53,-44.476],[-22.307,-62.229],[0,0],[4.579,-8.771],[19.312,-6.799],[0,0],[1.136,5.564],[6.275,-0.212],[1.672,5.464],[5.776,-0.711],[1.31,5.564],[5.764,-0.425],[0.449,5.127],[5.264,-0.374],[1.073,4.74],[4.778,-0.275],[1.996,4.105],[8.558,7.136],[0,0],[65.173,27.584],[0,0],[-10.729,4.18],[0,0]],"v":[[116.592,195.77],[116.629,196.344],[13.342,203.105],[-25.632,190.467],[-54.376,155.847],[-52.929,155.71],[-78.329,33.735],[-180.481,-122.137],[-180.581,-122.187],[-180.181,-156.921],[-166.557,-194.01],[-166.445,-194.035],[-154.057,-183.78],[-143.952,-197.154],[-130.278,-187.598],[-121.221,-199.724],[-108.557,-190.242],[-97.13,-201.221],[-86.251,-192.588],[-76.882,-202.145],[-65.404,-194.934],[-55.997,-203.105],[-23.935,-189.52],[0.93,-180.237],[0.93,-179.962],[176.475,139.417],[176.8,140.24],[150.551,178.99]],"c":true}},"nm":"P"},{"ty":"mm","mm":1,"nm":"M"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[981.333,324.754]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.028,0.15],[0.187,4.117],[5.015,-0.15],[-0.199,-4.117]],"o":[[5.015,-0.15],[-0.188,-4.117],[-5.028,0.162],[0.187,4.117]],"v":[[5.04,-9.089],[13.786,-16.824],[4.354,-24.009],[-4.392,-16.262]],"c":true}},"nm":"P"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[27.584,13.648],[-46.235,6.85],[-0.786,-7.51],[0,0],[1.76,-17.017],[0.399,0.187],[0,0]],"o":[[-28.893,-26.798],[0,0],[3.718,0.799],[1.185,11.153],[0,0],[-1.734,16.655],[0,0],[0,0]],"v":[[40.134,37.584],[-46.971,-23.884],[34.645,-37.584],[45.786,-26.816],[22.893,-13.317],[36.99,11.335],[25.837,23.686],[40.509,37.171]],"c":true}},"nm":"P"},{"ty":"mm","mm":1,"nm":"M"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[739.797,171.022]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-6.774,-18.801],[53.347,-29.642],[22.905,57.189],[10.717,21.046],[7.548,24.664],[-5.577,18.128],[-25.762,3.531],[-42.281,-67.694]],"o":[[9.956,24.44],[-52.473,25.813],[0,0],[-9.581,-18.826],[-5.551,-18.153],[7.647,-24.839],[18.502,-2.532],[27.633,44.239]],"v":[[103.455,5.926],[70.656,128.999],[-57.956,82.415],[-83.669,14.934],[-116.081,-52.036],[-118.426,-104.285],[-66.839,-146.952],[52.966,-87.118]],"c":true}},"nm":"P"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.579,-11.477],[-2.658,-6.038],[-0.324,-0.711],[-9.681,-6.201],[-5.589,-1.934],[-4.117,-0.736],[-3.519,-0.174],[-4.715,0.612],[-3.593,1.01],[-1.56,0.562],[-3.406,2.183],[-2.882,2.844],[-3.655,10.355],[-0.724,3.269],[5.489,15.645],[1.41,3.668],[19.05,32.337],[40.684,-11.253],[-18.377,-38.537],[-4.341,-8.795],[-5.177,-12.488]],"o":[[3.144,7.872],[0.337,0.748],[4.529,10.018],[4.828,3.094],[3.88,1.372],[3.431,0.649],[4.703,0.25],[3.668,-0.474],[1.584,-0.449],[3.892,-1.397],[3.406,-2.171],[7.398,-7.298],[1.098,-3.106],[3.443,-15.37],[-1.372,-3.917],[-12.625,-32.923],[-20.822,-35.307],[-47.046,13],[4.728,7.673],[5.951,12.077],[5.54,13.349]],"v":[[-47.676,62.129],[-38.968,83.176],[-37.995,85.347],[-15.988,109.949],[-0.293,117.534],[11.733,120.715],[22.176,121.95],[36.36,121.426],[47.277,119.206],[51.993,117.696],[62.946,112.307],[72.39,104.759],[89.095,77.786],[91.84,68.217],[89.108,20.348],[84.941,8.995],[39.23,-80.094],[-61.013,-130.309],[-93.175,-37.939],[-79.577,-13.05],[-62.872,24.278]],"c":true}},"nm":"P"},{"ty":"mm","mm":1,"nm":"M"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1000.297,322.458]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.135,5.564],[0,0],[0.137,0.412],[-7.697,0.848],[-0.25,-0.761],[6.276,-0.212]],"o":[[0,0],[-0.35,-1.385],[7.585,-0.948],[0.025,0.786],[0.399,6.325],[-6.25,0.2]],"v":[[-10.997,-2.502],[-11.109,-2.477],[-11.895,-5.234],[11.085,-7.953],[11.497,-5.62],[1.391,7.753]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[825.886,133.22]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.335,-0.848],[-1.31,-1.36],[4.179,1.772]],"o":[[2.059,0.898],[-2.57,-1.747],[1.933,0.487]],"v":[[-0.05,-0.537],[5.04,2.845],[-5.04,-2.845]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[988.426,233.831]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[6.2,-5.177],[0.038,0],[5.963,-0.399],[0,0],[0,0],[-10.729,4.179],[0,0],[0,0],[0.063,-3.207],[0,0]],"o":[[-0.037,0.012],[-5.277,0.35],[0,0],[9.182,-18.489],[3.256,-24.402],[9.394,-3.668],[0,0],[0.025,3.131],[-32.661,6.088],[-13.699,1.385]],"v":[[-22.799,28.75],[-22.924,28.763],[-39.828,29.885],[-39.866,29.312],[-5.907,12.531],[20.342,-26.217],[37.944,-28.962],[39.842,-28.974],[39.778,-19.456],[6.332,18.022]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1137.792,491.211]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.05,-0.537],[5.764,-0.424],[1.31,5.564],[-0.013,0.012],[0.125,0.549],[0,0],[-7.959,0.511]],"o":[[-0.062,0.561],[-0.499,5.664],[-5.752,0.437],[0,0],[-0.05,-0.549],[0,0],[8.234,-0.761],[0,0]],"v":[[12.201,-6.107],[12.189,-4.46],[0.761,6.518],[-11.902,-2.963],[-11.902,-2.988],[-12.164,-4.635],[-12.202,-5.034],[12.151,-6.955]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[872.015,127.993]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.171,-0.112],[-0.287,-5.602],[0,0],[-2.658,6.15],[-1.559,0.449],[-0.4,0],[-1.26,-1.846],[-0.525,-8.745],[0,0]],"o":[[-2.183,0.112],[0,0],[-0.537,-9.332],[0.673,-1.572],[0.399,-0.112],[2.058,0],[4.329,6.263],[0,0],[-0.287,-5.601]],"v":[[-0.324,3.349],[-3.742,13.704],[-9.93,14.066],[-6.574,-10.66],[-2.945,-13.905],[-1.747,-14.066],[3.642,-10.811],[10.468,12.931],[4.116,13.293]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1027.4,408.597]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.829,3.094],[0,0],[0.686,5.664],[0,0],[-2.034,1.983],[0.2,-12.014]],"o":[[-5.589,-1.934],[0,0],[0.087,-2.271],[0,0],[1.909,-1.559],[1.035,8.982],[0,0]],"v":[[8.103,14.883],[-7.592,7.299],[-7.354,6.911],[-8.39,-5.664],[-0.967,-6.513],[6.157,-14.883],[8.19,14.622]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[991.9,425.109]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.907,-0.187],[-0.487,14.06],[0,0],[0,0],[-5.077,-20.547]],"o":[[8.147,-2.844],[0,0],[-0.012,0.325],[0,0],[-23.404,1.51]],"v":[[-21.414,15.732],[-10.648,-15.732],[-10.523,-15.732],[-10.549,-15.233],[21.414,12.962]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[916.497,518.59]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[33.46,4.841],[0.112,-1.735],[0,0],[0.062,4.965],[0.087,2.433],[0,0],[-5.514,-1.809],[0,0],[-7.173,-8.546],[7.647,-0.499],[-2.445,0.162],[13.062,-0.387]],"o":[[-0.374,7.049],[0,0],[0.212,-6.201],[-0.025,-2.433],[0,0],[7.773,-0.399],[25.226,8.246],[19.861,-2.433],[-8.109,0.536],[2.358,-0.2],[-6.762,-3.967],[0,0]],"v":[[-43.378,-15.682],[-44.226,-0.773],[-44.351,-0.773],[-44.301,-18.04],[-44.476,-25.326],[-43.079,-25.376],[-23.242,-23.03],[5.502,11.59],[44.476,24.228],[20.76,25.775],[27.983,25.226],[-1.235,19.013]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0,0.698,0.408,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[950.2,503.631]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-4.466,-1.56]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-10.605,-3.156]],"v":[[-18.196,-4.042],[13.792,-7.074],[13.792,-7.061],[18.196,0.512],[-0.668,2.296],[-8.777,3.069],[-1.653,7.074]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[976.306,289.285]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.06,1.996],[0,0],[0,0],[0,0],[0,0],[7.785,-5.066]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[1.472,-1.098]],"v":[[4.242,1.84],[-3.93,2.613],[-8.334,-4.959],[-8.334,-4.972],[6.75,-6.394],[0.549,6.394]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[998.431,287.183]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":80,"st":0},{"ind":23,"ty":4,"nm":"a","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.53,196.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33],[-1.52,-8.7]],"o":[[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33],[0,4.67]],"v":[[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22],[52.945,-11.41]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.52,-8.7],[2.2,0.33],[18.19,-7.67],[10.85,6.16],[-10.17,6.5],[-18.01,1.33]],"o":[[0,4.67],[-18.61,-2.32],[-24.82,9.99],[-11.12,-7.86],[10.18,-6.5],[18.02,-1.33]],"v":[[52.945,-11.41],[48.855,-5.73],[-7.545,1.83],[-41.825,17.17],[-29.705,-6.04],[18.515,-22]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[882.434,400.25]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":80,"st":0},{"ind":24,"ty":4,"nm":"b","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[106.53,61.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.88,24.951],[0,0],[0,0],[25.866,-7.144]],"o":[[0,0],[24.365,5.284],[0,0],[0,0],[-30.822,8.513]],"v":[[-33.188,8.953],[-9.998,-5.137],[12.312,-28.621],[28.163,-27.74],[7.321,20.108]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":1,"ml":10,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[894.218,513.63]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.189,4.119],[-5.021,0.155],[-0.188,-4.118],[5.022,-0.155]],"o":[[-0.189,-4.119],[5.022,-0.155],[0.189,4.119],[-5.021,0.155]],"v":[[-9.092,0.28],[-0.342,-7.458],[9.092,-0.281],[0.342,7.457]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":1,"lj":1,"ml":10,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.043,0.106,0.204,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[873.947,500.296]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-26.316,0.733],[-22.016,0.44],[7.682,30.609],[0,0],[0,0]],"o":[[1.018,18.715],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-42.772,-24.016],[-10.703,24.048],[27.606,22.728],[35.091,-24.387],[35.229,-24.662],[-42.766,-24.782]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"fl","c":{"a":0,"k":[0.031,0.322,0.224,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[894.189,510.13]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":80,"st":0},{"ind":26,"ty":4,"nm":"a","parent":8,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-337.47,377.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-6.92,6.26],[-0.44,18.57],[2.07,4.77],[-6.4,3.28],[-3.12,-7.87],[0,-14],[10.37,-9.68],[4.19,4.89]],"o":[[6.93,-6.25],[0.6,-15.94],[-2.07,-4.77],[6.4,-3.28],[3.12,7.88],[-0.35,27.66],[-5.48,4.86],[-4.19,-4.89]],"v":[[-14.19,32.465],[7.93,-11.985],[3.24,-36.115],[4.43,-49.915],[20.26,-40.685],[24.46,-13.995],[-4.37,43.805],[-20.27,48.305]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1367.229,174.165]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.12,-7.87],[0,-14],[10.37,-9.68],[4.19,4.89],[-6.92,6.26],[-0.44,18.57],[2.07,4.77],[-6.4,3.28]],"o":[[3.12,7.88],[-0.35,27.66],[-5.48,4.86],[-4.19,-4.89],[6.93,-6.25],[0.6,-15.94],[-2.07,-4.77],[6.4,-3.28]],"v":[[20.26,-40.685],[24.46,-13.995],[-4.37,43.805],[-20.27,48.305],[-14.19,32.465],[7.93,-11.985],[3.24,-36.115],[4.43,-49.915]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1367.229,174.165]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":44,"op":50,"st":0},{"ind":27,"ty":4,"nm":"h","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-327.47,181.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-6,3.3],[-3.14,1.95],[-1.46,-2.02],[0.11,-0.22],[0,0]],"o":[[1.73,-10.14],[6,-3.3],[3.15,-1.95],[1.4,1.95],[0,0],[0,0]],"v":[[-19.38,13.575],[-2.52,-2.815],[10.09,-11.625],[17.98,-11.425],[17.99,-6.595],[17.98,-6.585]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1356.769,387.245]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.49,1.76]],"o":[[8.06,-2.61],[0,0]],"v":[[-7.06,3.495],[7.06,-3.495]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1367.689,384.155]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.09,0],[2.34,-3],[0,0]],"o":[[1.87,-1.32],[4.39,0],[0,0],[0,0]],"v":[[-5.37,-1.205],[-0.15,-3.365],[3.03,3.355],[3.03,3.365]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1380.129,381.855]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-3.76,2.29]],"o":[[7.52,-0.89],[0,0]],"v":[[-8.52,2.91],[8.52,-2.91]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1374.639,388.13]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.74,0.55],[0.62,-5.61]],"o":[[1.08,-0.65],[3.32,-2.45],[0,0]],"v":[[-5.23,0.79],[-2.5,-1.03],[4.61,3.48]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1388.389,384.42]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-13.18,0.39],[-8.88,-2.35],[-0.13,3],[5.22,-0.13],[0,1.96],[-8.35,-0.26],[-3.79,6.27],[4.31,-1.31],[14.36,-0.13]],"o":[[-1.43,9.4],[13.18,-0.39],[6.78,0.65],[0.13,-3],[-5.22,0.14],[0,-1.96],[8.35,0.26],[3.78,-6.26],[-4.31,1.3],[0,0]],"v":[[-31.135,-3.98],[-15.205,16.25],[6.205,17.56],[14.035,12.99],[6.205,7.5],[-1.895,3.33],[6.855,-1.11],[28.785,-7.9],[24.475,-16.9],[3.985,-10.64]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1368.524,404.8]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.34,-3],[-0.74,0.55],[0.62,-5.61],[3.78,-6.26],[8.35,0.26],[0,-1.96],[-5.22,0.14],[0.13,-3],[6.78,0.65],[13.18,-0.39],[-1.43,9.4],[-6,3.3],[-3.14,1.95],[-1.46,-2.02],[0.11,-0.22],[-2.09,0]],"o":[[1.08,-0.65],[3.32,-2.45],[4.31,-1.31],[-3.79,6.27],[-8.35,-0.26],[0,1.96],[5.22,-0.13],[-0.13,3],[-8.88,-2.35],[-13.18,0.39],[1.73,-10.14],[6,-3.3],[3.15,-1.95],[1.4,1.95],[1.87,-1.32],[4.39,0]],"v":[[14.635,-13.13],[17.365,-14.95],[24.475,-10.44],[28.785,-1.44],[6.855,5.35],[-1.895,9.79],[6.205,13.96],[14.035,19.45],[6.205,24.02],[-15.205,22.71],[-31.135,2.48],[-14.275,-13.91],[-1.665,-22.72],[6.225,-22.52],[6.235,-17.69],[11.455,-19.85]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1368.523,398.34]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":50,"op":60,"st":0},{"ind":28,"ty":4,"nm":"a","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-307.47,72.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[-5.08,2.86]],"o":[[0,0],[4.98,3.32],[0,0]],"v":[[-7.525,-2.78],[-7.515,-2.78],[7.525,-0.08]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1343.894,447.37]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5.68,1.63]],"o":[[4.65,3.8],[0,0]],"v":[[-7.74,-2.515],[7.74,0.885]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1343.679,461.675]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-6.15,2.36]],"o":[[4.61,4.25],[0,0]],"v":[[-7.99,-2.95],[7.99,0.59]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1344.369,432.32]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.72,-16.11]],"o":[[-0.14,-13.51],[0,0]],"v":[[-7.92,5.085],[8.06,8.425]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1343.359,408.045]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.45,24.03],[-0.14,5.24],[0.14,4.94],[0.47,5.65]],"o":[[11.89,-18.09],[0.5,-4.92],[0.13,-4.67],[-0.16,-5.31],[0,0]],"v":[[-11.115,54.29],[10.025,-8.2],[10.985,-23.44],[10.955,-37.85],[10.015,-54.29]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1341.404,470.76]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.45,24.25],[-0.02,5.15],[0.17,5.37],[0.3,5.73]],"o":[[12.33,-18.83],[0.28,-4.59],[0.01,-4.79],[-0.16,-5.12],[0,0]],"v":[[-9.785,53.19],[9.335,-7.16],[9.775,-21.73],[9.535,-36.94],[8.835,-53.19]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1326.604,466.32]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.68,1.63],[0,0],[11.89,-18.09],[0,0],[-1.45,24.25]],"o":[[0,0],[-2.45,24.03],[0,0],[12.33,-18.83],[4.65,3.8]],"v":[[17.295,-29.545],[17.305,-29.545],[-3.835,32.945],[-17.305,27.405],[1.815,-32.945]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1334.124,492.105]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.08,2.86],[0,0],[0.5,-4.92],[0,0],[4.65,3.8],[-0.02,5.15]],"o":[[0,0],[-0.14,5.24],[0,0],[-5.68,1.63],[0.28,-4.59],[4.98,3.32]],"v":[[7.255,-7.1],[8.225,-7.07],[7.265,8.17],[7.255,8.17],[-8.225,4.77],[-7.785,-9.8]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1344.164,454.39]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.72,-16.11],[-0.16,-5.31],[4.61,4.25],[0,0],[0.3,5.73]],"o":[[0.47,5.65],[-6.15,2.36],[0,0],[-0.16,-5.12],[-0.14,-13.51]],"v":[[7.59,-0.975],[8.53,15.465],[-7.45,11.925],[-7.69,11.935],[-8.39,-4.315]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1343.828,417.445]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-6.15,2.36],[0.13,-4.67],[0,0],[4.98,3.32],[0.17,5.37],[0,0]],"o":[[0.14,4.94],[0,0],[-5.08,2.86],[0.01,-4.79],[0,0],[4.61,4.25]],"v":[[8.03,-6.85],[8.06,7.56],[7.09,7.53],[-7.95,4.83],[-8.19,-10.38],[-7.95,-10.39]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1344.329,439.76]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":50,"op":60,"st":0},{"ind":29,"ty":4,"nm":"a","parent":11,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-236.47,7.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-8.38,11.87],[-5.84,-2.17],[7.86,-10.82],[21.86,-3.46],[1.34,4.81]],"o":[[30.14,-14.58],[6.71,-8.68],[5.85,2.17],[-15.05,21.51],[-3.83,0.29],[-1.33,-4.81]],"v":[[-34.315,26.33],[25.055,-29.36],[39.485,-38.52],[35.535,-18.38],[-34.685,40.4],[-44.005,35.65]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1289.524,551.75]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.84,-2.17],[7.86,-10.82],[21.86,-3.46],[1.34,4.81],[0,0],[-8.38,11.87]],"o":[[5.85,2.17],[-15.05,21.51],[-3.83,0.29],[-1.33,-4.81],[30.14,-14.58],[6.71,-8.68]],"v":[[39.485,-38.52],[35.535,-18.38],[-34.685,40.4],[-44.005,35.65],[-34.315,26.33],[25.055,-29.36]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1289.524,551.75]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":50,"op":60,"st":0}]},{"id":"comp_3","nm":"l","fr":24,"layers":[{"ind":1,"ty":3,"nm":"l","parent":2,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[10]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":7,"s":[19]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":8,"s":[19]},{"i":{"x":[0.593],"y":[0.843]},"o":{"x":[0.175],"y":[0.072]},"t":9,"s":[19]},{"i":{"x":[0.698],"y":[1]},"o":{"x":[0.351],"y":[1.186]},"t":10,"s":[6.793]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[4]},{"t":15,"s":[-34]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":0.667},"o":{"x":0.167,"y":0.167},"t":1,"s":[-79,97,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":4,"s":[-79,97,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":7,"s":[-79,97,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.167,"y":0.167},"t":8,"s":[-79,97,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.593,"y":0.593},"o":{"x":0.167,"y":0.167},"t":9,"s":[-79,97,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.698,"y":0.698},"o":{"x":0.351,"y":0.351},"t":10,"s":[-79,97,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":12,"s":[-79,97,0],"to":[0,0,0],"ti":[0,0,0]},{"t":15,"s":[-79,97,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":7,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":8,"s":[100,100,100]},{"i":{"x":[0.593,0.593,0.593],"y":[1,1,1]},"o":{"x":[0.175,0.175,0.175],"y":[0,0,0]},"t":9,"s":[100,100,100]},{"i":{"x":[0.698,0.698,0.698],"y":[1,1,1]},"o":{"x":[0.351,0.351,0.351],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":12,"s":[100,100,100]},{"t":15,"s":[100,100,100]}],"l":2}},"ao":0,"ip":0,"op":16,"st":0},{"ind":2,"ty":3,"nm":"l","parent":3,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[-6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":7,"s":[11]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":8,"s":[39]},{"i":{"x":[0.593],"y":[1]},"o":{"x":[0.175],"y":[0]},"t":9,"s":[-5]},{"i":{"x":[0.698],"y":[1]},"o":{"x":[0.351],"y":[0]},"t":10,"s":[-5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[-5]},{"t":15,"s":[-14]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":0.667},"o":{"x":0.167,"y":0.167},"t":1,"s":[10,135,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":4,"s":[10,135,0],"to":[-0.206,-0.114,0],"ti":[0.564,0.209,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":7,"s":[8.763,134.314,0],"to":[-0.564,-0.209,0],"ti":[-0.206,-0.114,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[6.619,133.749,0],"to":[0.206,0.114,0],"ti":[-0.564,-0.209,0]},{"i":{"x":0.593,"y":0.593},"o":{"x":0.167,"y":0.167},"t":9,"s":[10,135,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.698,"y":0.698},"o":{"x":0.351,"y":0.351},"t":10,"s":[10,135,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":12,"s":[10,135,0],"to":[0,0,0],"ti":[0,0,0]},{"t":15,"s":[10,135,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":7,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":8,"s":[100,100,100]},{"i":{"x":[0.593,0.593,0.593],"y":[1,1,1]},"o":{"x":[0.175,0.175,0.175],"y":[0,0,0]},"t":9,"s":[100,100,100]},{"i":{"x":[0.698,0.698,0.698],"y":[1,1,1]},"o":{"x":[0.351,0.351,0.351],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":12,"s":[100,100,100]},{"t":15,"s":[100,100,100]}],"l":2}},"ao":0,"ip":0,"op":16,"st":0},{"ind":3,"ty":3,"nm":"l","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[5]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[16]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":7,"s":[16]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":8,"s":[-54]},{"i":{"x":[0.593],"y":[0.843]},"o":{"x":[0.175],"y":[0.072]},"t":9,"s":[-65]},{"i":{"x":[0.698],"y":[1]},"o":{"x":[0.351],"y":[1.186]},"t":10,"s":[-93.482]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[-100]},{"t":15,"s":[-81]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":1,"s":[993,865,0],"to":[-4,-9.5,0],"ti":[-3.5,-10,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":4,"s":[969,808,0],"to":[3.5,10,0],"ti":[-3.833,-15.833,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":7,"s":[1014,925,0],"to":[3.833,15.833,0],"ti":[-1.333,7.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[992,903,0],"to":[1.333,-7.667,0],"ti":[-1.5,15,0]},{"i":{"x":0.593,"y":0.843},"o":{"x":0.167,"y":0.167},"t":9,"s":[1022,879,0],"to":[1.036,-10.364,0],"ti":[6.778,15.785,0]},{"i":{"x":0.698,"y":1},"o":{"x":0.296,"y":1},"t":10,"s":[1009.1,821.8,0],"to":[-3.032,-7.061,0],"ti":[2.112,-2.266,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":12,"s":[1001,813,0],"to":[-6.833,7.333,0],"ti":[3.333,-18.333,0]},{"t":15,"s":[981,923,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":7,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":8,"s":[100,100,100]},{"i":{"x":[0.593,0.593,0.593],"y":[1,1,1]},"o":{"x":[0.175,0.175,0.175],"y":[0,0,0]},"t":9,"s":[100,100,100]},{"i":{"x":[0.698,0.698,0.698],"y":[1,1,1]},"o":{"x":[0.351,0.351,0.351],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":12,"s":[100,100,100]},{"t":15,"s":[100,100,100]}],"l":2}},"ao":0,"ip":0,"op":16,"st":0},{"ind":4,"ty":3,"nm":"l","parent":5,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":0},"p":{"a":0,"k":[4,171,0],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":16,"st":0},{"ind":5,"ty":3,"nm":"l","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":0},"p":{"a":0,"k":[114,137,0],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":16,"st":0},{"ind":6,"ty":3,"nm":"l","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":2},"p":{"a":0,"k":[1005,948,0],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":16,"st":0},{"ind":7,"ty":4,"nm":"l","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[353.53,-209.39,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-3.87],[3.87,0],[0,3.88],[-3.87,0]],"o":[[0,3.88],[-3.87,0],[0,-3.87],[3.87,0]],"v":[[7.01,0],[0,7.01],[-7.01,0],[0,-7.01]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[653.369,810.09]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[3.57,-4.41],[-3.65,-27.85],[-5.48,0],[0,0]],"o":[[-4.08,-2.78],[-3.57,4.42],[4.7,24.38],[5.48,0],[0,0]],"v":[[9.305,-42.03],[-4.715,-37.92],[-12.695,11.43],[10.865,44.81],[16.345,40.13]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[638.504,835.55]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-3.67,6.05]],"o":[[5.61,2.49],[0,0]],"v":[[-8.92,1.38],[8.92,-3.87]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[660.379,853.62]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5,3.48]],"o":[[5.97,3.15],[0,0]],"v":[[-9.235,-0.935],[9.235,-2.215]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[657.774,844.495]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-6.18,3.71],[-0.03,0.02]],"o":[[3.23,2.81],[0.03,-0.01],[0,0]],"v":[[-8.855,-2.215],[8.765,-1.495],[8.855,-1.545]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[657.064,833.335]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.13,-1.55],[3.22,-8.76],[-3.27,-12.43],[-8.88,-1.56],[-3.14,2.01],[2.23,7.26],[0.41,1.22],[0.21,4.39],[-1.83,3.66]],"o":[[-6.1,0.15],[-4.96,1.85],[-5.89,16.08],[2.85,11.54],[3.48,0.62],[8.64,-5.55],[-0.63,-2.07],[-0.94,-3.96],[-0.29,-6.33],[0,0]],"v":[[11.25,-42.18],[-3.93,-40.41],[-15.9,-25.83],[-17.53,19.4],[3.04,41.56],[13.15,39.62],[17.49,15.64],[15.8,10.61],[14.02,-2.27],[16.15,-18.31]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[651.809,834.11]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-8.88,-1.56],[0,0],[5.48,0],[4.7,24.38],[-3.57,4.42],[-4.08,-2.78],[0,0],[3.22,-8.76],[-3.27,-12.43]],"o":[[0,0],[0,0],[-5.48,0],[-3.65,-27.85],[3.57,-4.41],[0,0],[-4.96,1.85],[-5.89,16.08],[2.85,11.54]],"v":[[16.345,40.12],[16.345,40.13],[10.865,44.81],[-12.695,11.43],[-4.715,-37.92],[9.305,-42.03],[9.375,-41.85],[-2.595,-27.27],[-4.225,17.96]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[638.504,835.55]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.87,0],[0,-3.87],[3.87,0],[0,3.88]],"o":[[3.87,0],[0,3.88],[-3.87,0],[0,-3.87]],"v":[[0,-7.01],[7.01,0],[0,7.01],[-7.01,0]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[653.369,810.09]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,3.88],[3.87,0],[0,-3.87],[-3.87,0]],"o":[[0,-3.87],[-3.87,0],[0,3.88],[3.87,0]],"v":[[8.57,-24.02],[1.56,-31.03],[-5.45,-24.02],[1.56,-17.01]],"c":true}},"nm":"P"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.29,-6.33],[-0.94,-3.96],[-0.63,-2.07],[8.64,-5.55],[3.48,0.62],[2.85,11.54],[-5.89,16.08],[-4.96,1.85],[-6.1,0.15],[0,0]],"o":[[0.21,4.39],[0.41,1.22],[2.23,7.26],[-3.14,2.01],[-8.88,-1.56],[-3.27,-12.43],[3.22,-8.76],[4.13,-1.55],[0,0],[-1.83,3.66]],"v":[[14.02,-2.27],[15.8,10.61],[17.49,15.64],[13.15,39.62],[3.04,41.56],[-17.53,19.4],[-15.9,-25.83],[-3.93,-40.41],[11.25,-42.18],[16.15,-18.31]],"c":true}},"nm":"P"},{"ty":"mm","mm":1,"nm":"M"},{"ty":"fl","c":{"a":0,"k":[0.996,0.839,0.027,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[651.809,834.11]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":1,"op":16,"st":0},{"ind":8,"ty":4,"nm":"l","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[224.53,-162.39,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.28,-2.57]],"o":[[-0.74,3.44],[0,0]],"v":[[-0.14,-4.455],[0.88,4.455]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[673.919,796.385]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.82,-11.57]],"o":[[11.77,-3.09],[0,0]],"v":[[-9.94,-3.275],[9.12,6.365]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[684.739,804.115]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5.04,-4.79]],"o":[[1.34,6.88],[0,0]],"v":[[-4.355,-8.905],[4.355,8.905]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[746.554,785.915]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.55,7.69]],"o":[[-3.12,-3.73],[0,0]],"v":[[4.335,9.085],[-4.335,-9.085]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[738.144,789.425]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-4.42,-5.91]],"o":[[0.61,6.84],[0,0]],"v":[[-3.86,-9.71],[3.86,9.71]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[730.019,792.67]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-4.9,-5.76]],"o":[[0.83,5.76],[0,0]],"v":[[-4.185,-8.42],[4.185,8.42]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[754.804,781.74]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.04,-0.04],[14.57,-7.41],[2.97,-1.28],[0,0],[2.62,-1.01],[0.31,-0.11],[2.4,-0.81],[26.95,-1.17],[3.58,-0.11],[-2.74,-8.72],[-5.32,2.37],[-16.13,1.82],[-1.12,0.14],[-12.94,4.79],[-3.1,1.39],[-1.86,0.9],[-0.99,0.52],[-2.74,1.67],[-5.43,6.05]],"o":[[-0.04,0.04],[-11.85,10.76],[-2.59,1.32],[0,0],[-2.33,1.01],[-0.32,0.11],[-2.73,0.94],[-15.05,5.03],[-3.1,0.14],[-6.17,0.42],[2.73,8.72],[4.17,-4.04],[1.03,-0.12],[14.02,-1.72],[2.74,-1.01],[1.71,-0.76],[0.97,-0.47],[2.65,-1.38],[18.46,-11.15],[0,0]],"v":[[58.025,-35.74],[57.895,-35.62],[22.145,-9.36],[13.825,-5.46],[13.825,-5.45],[6.405,-2.42],[5.455,-2.09],[-2.195,0.53],[-54.595,9.14],[-64.605,9.5],[-71.285,25],[-60.395,33.37],[-34.495,28.05],[-31.265,27.66],[5.525,19.95],[14.255,16.37],[19.615,13.88],[22.565,12.4],[30.685,7.81],[74.025,-25.71]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[728.354,782.43]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.28,-2.57],[0.82,-11.57],[4.17,-4.04],[2.73,8.72],[-6.17,0.42],[-3.1,0.14],[0,0]],"o":[[11.77,-3.09],[-16.13,1.82],[-5.32,2.37],[-2.74,-8.72],[3.58,-0.11],[0,0],[-0.74,3.44]],"v":[[0.295,-4.03],[19.355,5.61],[-6.545,10.93],[-17.435,2.56],[-10.755,-12.94],[-0.745,-13.3],[-0.725,-12.94]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0.996,0.839,0.027,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[674.504,804.87]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-15.05,5.03],[-4.42,-5.91],[14.02,-1.72],[1.03,-0.12],[11.77,-3.09],[-0.74,3.44],[0,0]],"o":[[0.61,6.84],[-12.94,4.79],[-1.12,0.14],[0.82,-11.57],[-1.28,-2.57],[0,0],[26.95,-1.17]],"v":[[22.7,-13.76],[30.42,5.66],[-6.37,13.37],[-9.6,13.76],[-28.66,4.12],[-29.68,-4.79],[-29.7,-5.15]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[703.459,796.72]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.12,-3.73],[0,0],[2.74,-1.01],[0.61,6.84],[-2.73,0.94]],"o":[[0,0],[-3.1,1.39],[-4.42,-5.91],[2.4,-0.81],[1.55,7.69]],"v":[[8.095,7.15],[8.225,7.44],[-0.505,11.02],[-8.225,-8.4],[-0.575,-11.02]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[734.384,791.36]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.04,-4.79],[0,0],[0.97,-0.47],[1.71,-0.76],[0,0],[1.55,7.69],[-0.32,0.11],[-2.33,1.01],[0,0]],"o":[[0,0],[-0.99,0.52],[-1.86,0.9],[0,0],[-3.12,-3.73],[0.31,-0.11],[2.62,-1.01],[0,0],[1.34,6.88]],"v":[[8.545,6.93],[8.555,6.94],[5.605,8.42],[0.245,10.91],[0.115,10.62],[-8.555,-7.55],[-7.605,-7.88],[-0.185,-10.91],[-0.165,-10.88]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[742.364,787.89]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.9,-5.76],[0,0],[2.65,-1.38],[0,0],[1.34,6.88],[0,0],[0,0],[-2.59,1.32],[0,0]],"o":[[0,0],[-2.74,1.67],[0,0],[-5.04,-4.79],[0,0],[0,0],[2.97,-1.28],[0,0],[0.83,5.76]],"v":[[8.38,6.21],[8.43,6.29],[0.31,10.88],[0.3,10.87],[-8.41,-6.94],[-8.43,-6.97],[-8.43,-6.98],[-0.11,-10.88],[0.01,-10.63]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[750.609,783.95]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[18.46,-11.15],[0,0],[0.83,5.76],[0,0],[-11.85,10.76]],"o":[[0,0],[0,0],[-5.43,6.05],[0,0],[-4.9,-5.76],[0,0],[14.57,-7.41],[0,0]],"v":[[9.9,-21.575],[25.98,-11.945],[25.9,-11.805],[-17.44,21.715],[-17.49,21.635],[-25.86,4.795],[-25.98,4.545],[9.77,-21.715]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[776.479,768.525]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":1,"op":16,"st":0},{"ind":9,"ty":4,"nm":"l","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[184.53,-77.39,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[4.88,0],[0.42,-9.78],[14.11,-21.77],[-6.21,-7.49],[-6.67,6.4],[-8.91,14.32],[0.72,10.97]],"o":[[-7.79,0],[-0.42,9.78],[-14.11,21.78],[4.01,5.64],[5.38,-4.95],[16.59,-28.29],[-0.72,-10.97]],"v":[[20.42,-52.47],[10.32,-36.16],[-8.19,13.2],[-25.76,46.83],[-8.19,45.01],[14.51,14.79],[31.25,-39.33]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[810.569,711.71]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.72,-10.97],[16.59,-28.29],[5.38,-4.95],[4.01,5.64],[-14.11,21.78],[-0.42,9.78],[-7.79,0]],"o":[[0.72,10.97],[-8.91,14.32],[-6.67,6.4],[-6.21,-7.49],[14.11,-21.77],[0.42,-9.78],[4.88,0]],"v":[[31.25,-39.33],[14.51,14.79],[-8.19,45.01],[-25.76,46.83],[-8.19,13.2],[10.32,-36.16],[20.42,-52.47]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[810.569,711.71]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":1,"op":16,"st":0},{"ind":10,"ty":4,"nm":"l","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-272.47,-255.39,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-4.09],[4.08,0],[0,4.09],[-4.09,0]],"o":[[0,4.09],[-4.09,0],[0,-4.09],[4.08,0]],"v":[[7.4,0],[0,7.4],[-7.4,0],[0,-7.4]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1292.479,858.84]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[2.71,4.93]],"o":[[1.52,-7.73],[0,0]],"v":[[0.135,9.5],[-1.655,-9.5]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1331.114,855.78]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.42,-7.86]],"o":[[1.88,4.97],[0,0]],"v":[[-1.335,-9.375],[-0.085,9.375]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1341.204,854.165]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.89,6.41]],"o":[[1.97,-6.4],[0,0]],"v":[[-1.005,9.605],[-0.885,-9.605]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1320.884,857.185]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-7.27,-4.65],[-25.22,0.66],[-4.16,4.3],[3.44,1.87]],"o":[[-3.96,1.59],[7.27,4.64],[24.66,0.06],[4.15,-4.3],[0,0]],"v":[[-44.125,-8.175],[-40.285,3.765],[1.895,10.145],[43.935,-1.525],[43.935,-10.805]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1315.384,874.345]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.79,-6.43],[-5.04,-2.26],[-12.46,5.79],[-1.81,3.86],[0.59,3.54],[11.85,-1.75],[2.06,-0.35],[3.07,-0.26],[5.42,0.88]],"o":[[-3.45,6.89],[1.28,2.99],[15.88,7.12],[6.96,-3.23],[1.45,-3.06],[-1.33,-8.01],[-2.43,0.36],[-3.38,0.56],[-4.14,0.35],[0,0]],"v":[[-38.64,-17.155],[-43.44,3.815],[-34.34,11.785],[31.99,11.785],[44.62,1.185],[45.64,-8.695],[21.45,-17.155],[14.76,-16.075],[5.3,-14.775],[-8.52,-15.375]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1314.698,862.355]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.09,0],[0,-4.09],[4.08,0],[0,4.09]],"o":[[4.08,0],[0,4.09],[-4.09,0],[0,-4.09]],"v":[[0,-7.4],[7.4,0],[0,7.4],[-7.4,0]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1292.479,858.84]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.81,3.86],[4.15,-4.3],[24.66,0.06],[7.27,4.64],[-3.96,1.59],[-5.04,-2.26],[-12.46,5.79]],"o":[[3.44,1.87],[-4.16,4.3],[-25.22,0.66],[-7.27,-4.65],[1.28,2.99],[15.88,7.12],[6.96,-3.23]],"v":[[43.935,-10.805],[43.935,-1.525],[1.895,10.145],[-40.285,3.765],[-44.125,-8.175],[-35.025,-0.205],[31.305,-0.205]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1315.384,874.345]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,4.09],[4.08,0],[0,-4.09],[-4.09,0]],"o":[[0,-4.09],[-4.09,0],[0,4.09],[4.08,0]],"v":[[-14.82,-3.515],[-22.22,-10.915],[-29.62,-3.515],[-22.22,3.885]],"c":true}},"nm":"P"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.38,0.56],[-2.43,0.36],[-1.33,-8.01],[1.45,-3.06],[6.96,-3.23],[15.88,7.12],[1.28,2.99],[-3.45,6.89],[0,0],[-4.14,0.35]],"o":[[2.06,-0.35],[11.85,-1.75],[0.59,3.54],[-1.81,3.86],[-12.46,5.79],[-5.04,-2.26],[-2.79,-6.43],[0,0],[5.42,0.88],[3.07,-0.26]],"v":[[14.76,-16.075],[21.45,-17.155],[45.64,-8.695],[44.62,1.185],[31.99,11.785],[-34.34,11.785],[-43.44,3.815],[-38.64,-17.155],[-8.52,-15.375],[5.3,-14.775]],"c":true}},"nm":"P"},{"ty":"mm","mm":1,"nm":"M"},{"ty":"fl","c":{"a":0,"k":[0.996,0.839,0.027,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1314.699,862.355]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":1,"st":0},{"ind":11,"ty":4,"nm":"l","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-318.47,-134.39,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-13.78,-0.14],[0,0]],"o":[[5.39,4.71],[0,0],[0,0]],"v":[[-11.665,-3.32],[11.595,3.32],[11.665,3.32]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1330.394,784.82]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-10.94,-0.02]],"o":[[4.88,4.66],[0,0]],"v":[[-11.595,-3.715],[11.595,3.715]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1327.094,792.145]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-10.49,0.76]],"o":[[4.87,6.32],[0,0]],"v":[[-11.525,-4.55],[11.525,3.79]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1323.494,799.4]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-9.69,1.43]],"o":[[6.2,3.77],[0,0]],"v":[[-11.62,-2.91],[11.62,1.48]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1333.009,778.23]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5.89,-2.26]],"o":[[3.16,2.26],[0,0]],"v":[[-6.79,-3.385],[6.79,3.385]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1289.779,838.855]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-6.64,-7.83]],"o":[[7.52,-12.63],[0,0]],"v":[[-10.71,9.56],[10.71,-1.73]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1307.279,832.68]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.27,-12.72],[2.21,-6.83],[0.92,-2.19],[1.25,-2.43],[1.32,-2.24],[2.29,-3.36],[5.58,-7.23],[0.9,-1.82],[-8.27,-0.63],[-10.81,13.47],[-4.47,6.49],[-5.06,9.57],[-1.13,2.41],[-0.95,2.49],[-0.61,2.63],[7.6,26.4]],"o":[[3.32,13.67],[-0.59,5.85],[-0.65,2.01],[-0.91,2.2],[-1.04,2.05],[-1.74,2.97],[-9.12,13.39],[-3.56,4.62],[-2.32,4.7],[8.27,0.64],[3.16,-3.93],[5.75,-8.36],[1.31,-2.47],[1.24,-2.65],[1.13,-2.97],[4.77,-20.67],[0,0]],"v":[[10.655,-65.52],[13.215,-33.08],[9.565,-14.48],[7.235,-8.19],[4.005,-1.26],[0.475,5.16],[-5.545,14.63],[-28.505,45.78],[-35.435,55.51],[-31.315,70.61],[-5.315,57.29],[6.495,41.26],[23.525,13.5],[27.195,6.17],[30.495,-1.55],[33.135,-9.98],[31.985,-71.25]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1311.494,789.69]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.89,-2.26],[-6.64,-7.83],[3.16,-3.93],[8.27,0.64],[-2.32,4.7],[-3.56,4.62]],"o":[[7.52,-12.63],[-4.47,6.49],[-10.81,13.47],[-8.27,-0.63],[0.9,-1.82],[3.16,2.26]],"v":[[1.62,0.21],[23.04,-11.08],[11.23,4.95],[-14.77,18.27],[-18.89,3.17],[-11.96,-6.56]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0.996,0.839,0.027,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1294.948,842.03]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.74,2.97],[-10.49,0.76],[5.75,-8.36],[7.52,-12.63],[3.16,2.26],[-9.12,13.39]],"o":[[4.87,6.32],[-5.06,9.57],[-6.64,-7.83],[-5.89,-2.26],[5.58,-7.23],[2.29,-3.36]],"v":[[2.965,-23.695],[26.015,-15.355],[8.985,12.405],[-12.435,23.695],[-26.015,16.925],[-3.055,-14.225]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1309.004,818.545]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-10.94,-0.02],[1.31,-2.47],[4.87,6.32],[-1.04,2.05]],"o":[[-1.13,2.41],[-10.49,0.76],[1.32,-2.24],[4.88,4.66]],"v":[[13.36,-0.33],[9.69,7],[-13.36,-1.34],[-9.83,-7.76]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1325.329,796.19]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-13.78,-0.14],[1.24,-2.65],[4.88,4.66],[-0.91,2.2]],"o":[[-0.95,2.49],[-10.94,-0.02],[1.25,-2.43],[5.39,4.71]],"v":[[13.245,-0.54],[9.945,7.18],[-13.245,-0.25],[-10.015,-7.18]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1328.744,788.68]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-9.69,1.43],[1.13,-2.97],[5.39,4.71],[-0.65,2.01],[0,0]],"o":[[-0.61,2.63],[-13.78,-0.14],[0.92,-2.19],[0,0],[6.2,3.77]],"v":[[12.95,-1.965],[10.31,6.465],[-12.95,-0.175],[-10.62,-6.465],[-10.29,-6.355]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1331.679,781.675]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.77,-20.67],[6.2,3.77],[0,0],[-0.59,5.85],[3.32,13.67]],"o":[[7.6,26.4],[-9.69,1.43],[0,0],[2.21,-6.83],[1.27,-12.72],[0,0]],"v":[[7.41,-31.35],[8.56,29.92],[-14.68,25.53],[-15.01,25.42],[-11.36,6.82],[-13.92,-25.62]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1336.069,749.79]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":1,"st":0},{"ind":12,"ty":4,"nm":"l","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":-8},"p":{"a":0,"k":[-294.274,-127.049,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,176,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.86,18.12]],"o":[[4.86,18.11],[0,0]],"v":[[-13.095,-6.19],[8.235,-11.92]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1335.244,730.36]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-9.81,-9.55]],"o":[[-10.99,-7.08],[0,0]],"v":[[13.125,-2.715],[-3.315,9.795]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1267.284,637.015]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[27.65,20.4]],"o":[[-13.33,-35.3],[0,0]],"v":[[31.535,42.07],[-31.535,-42.07]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1311.944,676.37]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[23.79,22.12]],"o":[[-12.16,-31.02],[0,0]],"v":[[29.09,38.68],[-29.09,-38.68]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1293.059,685.49]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-13.33,-35.3],[4.86,18.11],[23.79,22.12],[-10.99,-7.08]],"o":[[4.86,18.12],[-12.16,-31.02],[-9.81,-9.55],[27.65,20.4]],"v":[[42.23,33.69],[20.9,39.42],[-37.28,-37.94],[-20.84,-50.45]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1301.249,684.75]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":1,"st":0}]},{"id":"comp_4","nm":"u","fr":24,"layers":[{"ind":1,"ty":3,"nm":"h","parent":2,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.5,-21.5,0],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":2,"ty":3,"nm":"a","parent":3,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":8,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":28,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":32,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":36,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":40,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":44,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":48,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":52,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":56,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":64,"s":[-10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":68,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":76,"s":[0]},{"t":80,"s":[-10]}]},"p":{"a":0,"k":[148,23,0],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":3,"ty":3,"nm":"a","sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[152]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":8,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[152]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":28,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":32,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":36,"s":[152]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":40,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":44,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":48,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":52,"s":[152]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":56,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":64,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":68,"s":[152]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":76,"s":[0]},{"t":80,"s":[89]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[1132,358.5,0],"to":[0,-0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":4,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":8,"s":[1132,358.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":12,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":16,"s":[1132,358.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[1132,358.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":28,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":32,"s":[1132,358.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":36,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":40,"s":[1132,358.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":44,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":48,"s":[1132,358.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":56,"s":[1132,358.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":64,"s":[1132,358.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":68,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":72,"s":[1132,358.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":76,"s":[1132,353.5,0],"to":[0,0,0],"ti":[0,-0.833,0]},{"t":80,"s":[1132,358.5,0]}],"l":2},"a":{"a":0,"k":[50,50,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"ip":0,"op":80,"st":0},{"ind":4,"ty":4,"nm":"h","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-246.97,338.11,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[3.01,-3.81],[0,0]],"o":[[-4.07,-4.27],[-3.01,3.81],[0,0]],"v":[[7.035,-2.385],[-4.025,-1.525],[-6.335,6.655]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1256.284,224.005]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.79,2.34]],"o":[[2.34,0.83],[0,0]],"v":[[-3.33,-0.2],[3.33,-1.17]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1265.639,242.16]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[2.86,-2.46],[0.64,-1.17],[-0.49,-1.89],[0,0]],"o":[[-4,-3.97],[-0.96,0.83],[-2.55,4.64],[0,0],[0,0]],"v":[[7.95,-3.705],[-2.97,-4.695],[-5.4,-1.655],[-7.37,7.665],[-7.37,7.675]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1266.479,226.535]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.01,0],[3.34,-2.1],[0.87,-1.23],[-2.19,-4.06],[-0.58,-0.48],[-0.18,0.28],[0,0]],"o":[[0,0],[-2.89,-4.76],[-1.23,0.76],[-2.76,3.95],[0.35,0.67],[4.39,3.17],[0,0],[0,0]],"v":[[9.105,-7.665],[9.095,-7.675],[-1.395,-10.435],[-4.595,-7.375],[-6.915,7.625],[-5.515,9.365],[2.055,7.855],[2.065,7.845]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1275.884,233.365]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.43,4.27],[-0.06,0.13],[2.7,1.95]],"o":[[2.7,2.32],[0.07,-0.12],[1.29,-2.82],[0,0]],"v":[[-7.1,1.37],[5.62,1.74],[5.81,1.37],[2.42,-6.01]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1256.689,240.22]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.7,0.57],[1.23,0.04],[1.21,-6.18],[-12.11,-3.78],[-8.62,14.11],[4.17,1.79],[1.87,-0.89],[0.96,-3.7],[-0.39,-1.74],[-1,-0.54],[-0.59,7.74]],"o":[[-1.85,-1.34],[-1,-0.33],[-3.88,-0.14],[-1.59,8.15],[13.19,3.27],[7.15,-13.9],[-1.66,-0.71],[-2.83,1.35],[-0.87,3.36],[0.33,1.46],[2.18,1.2],[0,0]],"v":[[-9.74,-9.17],[-15.48,-12.1],[-18.9,-12.71],[-28.39,-4.96],[-11.12,15.5],[22.83,4.38],[21.64,-18.06],[16.13,-17.68],[9.73,-9.72],[9.09,-2.15],[11.12,0.8],[19.73,-7.63]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1268.849,243.37]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.18,0.28],[-0.87,3.36],[-2.83,1.35],[-1.66,-0.71],[7.15,-13.9],[13.19,3.27],[-1.59,8.15],[-3.88,-0.14],[-1,-0.33],[-1.85,-1.34],[0,0],[1.29,-2.82],[0.07,-0.12],[-2.79,2.34],[-0.58,-0.48]],"o":[[-0.39,-1.74],[0.96,-3.7],[1.87,-0.89],[4.17,1.79],[-8.62,14.11],[-12.11,-3.78],[1.21,-6.18],[1.23,0.04],[1.7,0.57],[0,0],[2.7,1.95],[-0.06,0.13],[2.34,0.83],[0.35,0.67],[4.39,3.17]],"v":[[9.09,-2.15],[9.73,-9.72],[16.13,-17.68],[21.64,-18.06],[22.83,4.38],[-11.12,15.5],[-28.39,-4.96],[-18.9,-12.71],[-15.48,-12.1],[-9.74,-9.17],[-9.74,-9.16],[-6.35,-1.78],[-6.54,-1.41],[0.12,-2.38],[1.52,-0.64]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1268.849,243.37]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4,-3.97],[0,0],[0.87,-1.23],[-2.19,-4.06],[2.34,0.83],[-0.06,0.13],[2.7,1.95],[0,0],[-2.55,4.64],[-0.96,0.83]],"o":[[0,0],[-1.23,0.76],[-2.76,3.95],[-2.79,2.34],[0.07,-0.12],[1.29,-2.82],[0,0],[-0.49,-1.89],[0.64,-1.17],[2.86,-2.46]],"v":[[7.92,-8.265],[7.98,-8.165],[4.78,-5.105],[2.46,9.895],[-4.2,10.865],[-4.01,10.495],[-7.4,3.115],[-7.4,3.105],[-5.43,-6.215],[-3,-9.255]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1266.509,231.095]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.07,-4.27],[0,0],[0.64,-1.17],[-0.49,-1.89],[1.7,0.57],[1.23,0.04],[-3.01,3.81]],"o":[[0,0],[-0.96,0.83],[-2.55,4.64],[-1.85,-1.34],[-1,-0.33],[0,0],[3.01,-3.81]],"v":[[6.94,-4.155],[7.13,-3.935],[4.7,-0.895],[2.73,8.425],[-3.01,5.495],[-6.43,4.885],[-4.12,-3.295]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1256.379,225.775]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.23,0.76],[-2.89,-4.76],[0.96,-3.7],[-0.39,-1.74],[4.39,3.17],[0.35,0.67],[-2.76,3.95]],"o":[[3.34,-2.1],[-2.83,1.35],[-0.87,3.36],[-0.18,0.28],[-0.58,-0.48],[-2.19,-4.06],[0.87,-1.23]],"v":[[-1.39,-10.435],[9.1,-7.675],[2.7,0.285],[2.06,7.855],[-5.51,9.365],[-6.91,7.625],[-4.59,-7.375]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1275.879,233.365]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":44,"st":0},{"ind":5,"ty":4,"nm":"a","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-214.47,266.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-6.13,0.64]],"o":[[4.18,5.01],[0,0]],"v":[[-7.73,-3.595],[7.73,2.955]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1257.679,280.725]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5.39,0.92],[0,0]],"o":[[5.39,3.9],[0,0],[0,0]],"v":[[-8.085,-2.695],[8.075,1.775],[8.085,1.775]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1260.544,269.825]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[-6.42,-1.15]],"o":[[0,0],[3.14,4.74],[0,0]],"v":[[-7.165,-4.425],[-7.165,-4.415],[7.165,4.425]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1252.934,293.195]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.63,0.66]],"o":[[0.36,-5.98],[0,0]],"v":[[3.075,4.925],[-3.435,-4.925]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1268.064,251.725]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5.3,-0.31]],"o":[[1.23,-7.85],[0,0]],"v":[[-5.175,5.245],[5.175,-4.935]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1259.454,251.735]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-7.05,15.66],[-1.54,4.9],[-0.91,4.19],[-0.62,5.21]],"o":[[11.12,-10.34],[1.99,-4.4],[1.23,-3.86],[1.06,-4.76],[0,0]],"v":[[-19.435,39.785],[8.395,1.185],[13.705,-12.755],[16.915,-24.835],[19.435,-39.785]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1251.704,296.435]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.68,-3.22],[0.94,-3.16],[1.58,-3.65]],"o":[[-0.53,3.56],[-0.74,3.51],[-1.22,4.13],[0,0]],"v":[[4.255,-15.895],[2.435,-5.745],[-0.075,4.255],[-4.255,15.895]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1250.024,272.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[9.56,-10.05]],"o":[[-5.12,11.94],[0,0]],"v":[[10.875,-15.895],[-10.875,15.895]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1234.894,304.675]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-6.42,-1.15],[11.12,-10.34],[0,0],[-5.12,11.94]],"o":[[-7.05,15.66],[0,0],[9.56,-10.05],[3.14,4.74]],"v":[[18.04,-14.88],[-9.79,23.72],[-18.04,8.07],[3.71,-23.72]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1242.059,312.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.22,4.13],[-6.13,0.64],[1.99,-4.4],[3.14,4.74],[0,0]],"o":[[4.18,5.01],[-1.54,4.9],[-6.42,-1.15],[0,0],[1.58,-3.65]],"v":[[-5.64,-10.245],[9.82,-3.695],[4.51,10.245],[-9.82,1.405],[-9.82,1.395]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1255.589,287.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.3,-0.31],[0.36,-5.98],[1.06,-4.76],[5.39,3.9],[-0.53,3.56]],"o":[[4.63,0.66],[-0.62,5.21],[-5.39,0.92],[0.68,-3.22],[1.23,-7.85]],"v":[[2.65,-12.705],[9.16,-2.855],[6.64,12.095],[-9.52,7.625],[-7.7,-2.525]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1261.979,259.505]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.74,3.51],[-5.39,0.92],[1.23,-3.86],[4.18,5.01]],"o":[[5.39,3.9],[-0.91,4.19],[-6.13,0.64],[0.94,-3.16]],"v":[[-6.825,-8.595],[9.335,-4.125],[6.125,7.955],[-9.335,1.405]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1259.284,275.725]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":44,"st":0},{"ind":6,"ty":4,"nm":"a","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-116.47,239.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-25.92,23.58],[-1.5,-2.76],[8.92,-8.56],[30.96,-0.76],[0.38,8.8]],"o":[[50.97,3.27],[5.48,-4.99],[1.5,2.76],[-7.4,7.61],[-30.96,0.76],[-0.38,-8.79]],"v":[[-50.885,6.21],[42.105,-19.95],[54.035,-19.35],[48.835,-2.67],[-18.395,24.18],[-57.375,14.62]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1181.913,340.52]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.5,-2.76],[8.92,-8.56],[30.96,-0.76],[0.38,8.8],[0,0],[-25.92,23.58]],"o":[[1.5,2.76],[-7.4,7.61],[-30.96,0.76],[-0.38,-8.79],[50.97,3.27],[5.48,-4.99]],"v":[[54.035,-19.35],[48.835,-2.67],[-18.395,24.18],[-57.375,14.62],[-50.885,6.21],[42.105,-19.95]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1181.914,340.52]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":0,"op":44,"st":0},{"ind":7,"ty":4,"nm":"h","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-246.97,338.11,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[3.01,-3.81],[0,0]],"o":[[-4.07,-4.27],[-3.01,3.81],[0,0]],"v":[[7.035,-2.385],[-4.025,-1.525],[-6.335,6.655]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1256.284,224.005]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.79,2.34]],"o":[[2.34,0.83],[0,0]],"v":[[-3.33,-0.2],[3.33,-1.17]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1265.639,242.16]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[2.86,-2.46],[0.64,-1.17],[-0.49,-1.89],[0,0]],"o":[[-4,-3.97],[-0.96,0.83],[-2.55,4.64],[0,0],[0,0]],"v":[[7.95,-3.705],[-2.97,-4.695],[-5.4,-1.655],[-7.37,7.665],[-7.37,7.675]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1266.479,226.535]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.01,0],[3.34,-2.1],[0.87,-1.23],[-2.19,-4.06],[-0.58,-0.48],[-0.18,0.28],[0,0]],"o":[[0,0],[-2.89,-4.76],[-1.23,0.76],[-2.76,3.95],[0.35,0.67],[4.39,3.17],[0,0],[0,0]],"v":[[9.105,-7.665],[9.095,-7.675],[-1.395,-10.435],[-4.595,-7.375],[-6.915,7.625],[-5.515,9.365],[2.055,7.855],[2.065,7.845]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1275.884,233.365]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.43,4.27],[-0.06,0.13],[2.7,1.95]],"o":[[2.7,2.32],[0.07,-0.12],[1.29,-2.82],[0,0]],"v":[[-7.1,1.37],[5.62,1.74],[5.81,1.37],[2.42,-6.01]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1256.689,240.22]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.7,0.57],[1.23,0.04],[1.21,-6.18],[-12.11,-3.78],[-8.62,14.11],[4.17,1.79],[1.87,-0.89],[0.96,-3.7],[-0.39,-1.74],[-1,-0.54],[-0.59,7.74]],"o":[[-1.85,-1.34],[-1,-0.33],[-3.88,-0.14],[-1.59,8.15],[13.19,3.27],[7.15,-13.9],[-1.66,-0.71],[-2.83,1.35],[-0.87,3.36],[0.33,1.46],[2.18,1.2],[0,0]],"v":[[-9.74,-9.17],[-15.48,-12.1],[-18.9,-12.71],[-28.39,-4.96],[-11.12,15.5],[22.83,4.38],[21.64,-18.06],[16.13,-17.68],[9.73,-9.72],[9.09,-2.15],[11.12,0.8],[19.73,-7.63]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1268.849,243.37]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.18,0.28],[-0.87,3.36],[-2.83,1.35],[-1.66,-0.71],[7.15,-13.9],[13.19,3.27],[-1.59,8.15],[-3.88,-0.14],[-1,-0.33],[-1.85,-1.34],[0,0],[1.29,-2.82],[0.07,-0.12],[-2.79,2.34],[-0.58,-0.48]],"o":[[-0.39,-1.74],[0.96,-3.7],[1.87,-0.89],[4.17,1.79],[-8.62,14.11],[-12.11,-3.78],[1.21,-6.18],[1.23,0.04],[1.7,0.57],[0,0],[2.7,1.95],[-0.06,0.13],[2.34,0.83],[0.35,0.67],[4.39,3.17]],"v":[[9.09,-2.15],[9.73,-9.72],[16.13,-17.68],[21.64,-18.06],[22.83,4.38],[-11.12,15.5],[-28.39,-4.96],[-18.9,-12.71],[-15.48,-12.1],[-9.74,-9.17],[-9.74,-9.16],[-6.35,-1.78],[-6.54,-1.41],[0.12,-2.38],[1.52,-0.64]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1268.849,243.37]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4,-3.97],[0,0],[0.87,-1.23],[-2.19,-4.06],[2.34,0.83],[-0.06,0.13],[2.7,1.95],[0,0],[-2.55,4.64],[-0.96,0.83]],"o":[[0,0],[-1.23,0.76],[-2.76,3.95],[-2.79,2.34],[0.07,-0.12],[1.29,-2.82],[0,0],[-0.49,-1.89],[0.64,-1.17],[2.86,-2.46]],"v":[[7.92,-8.265],[7.98,-8.165],[4.78,-5.105],[2.46,9.895],[-4.2,10.865],[-4.01,10.495],[-7.4,3.115],[-7.4,3.105],[-5.43,-6.215],[-3,-9.255]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1266.509,231.095]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-4.07,-4.27],[0,0],[0.64,-1.17],[-0.49,-1.89],[1.7,0.57],[1.23,0.04],[-3.01,3.81]],"o":[[0,0],[-0.96,0.83],[-2.55,4.64],[-1.85,-1.34],[-1,-0.33],[0,0],[3.01,-3.81]],"v":[[6.94,-4.155],[7.13,-3.935],[4.7,-0.895],[2.73,8.425],[-3.01,5.495],[-6.43,4.885],[-4.12,-3.295]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1256.379,225.775]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.23,0.76],[-2.89,-4.76],[0.96,-3.7],[-0.39,-1.74],[4.39,3.17],[0.35,0.67],[-2.76,3.95]],"o":[[3.34,-2.1],[-2.83,1.35],[-0.87,3.36],[-0.18,0.28],[-0.58,-0.48],[-2.19,-4.06],[0.87,-1.23]],"v":[[-1.39,-10.435],[9.1,-7.675],[2.7,0.285],[2.06,7.855],[-5.51,9.365],[-6.91,7.625],[-4.59,-7.375]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1275.879,233.365]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":60,"op":80,"st":0},{"ind":8,"ty":4,"nm":"a","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-214.47,266.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-6.13,0.64]],"o":[[4.18,5.01],[0,0]],"v":[[-7.73,-3.595],[7.73,2.955]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1257.679,280.725]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5.39,0.92],[0,0]],"o":[[5.39,3.9],[0,0],[0,0]],"v":[[-8.085,-2.695],[8.075,1.775],[8.085,1.775]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1260.544,269.825]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[-6.42,-1.15]],"o":[[0,0],[3.14,4.74],[0,0]],"v":[[-7.165,-4.425],[-7.165,-4.415],[7.165,4.425]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1252.934,293.195]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.63,0.66]],"o":[[0.36,-5.98],[0,0]],"v":[[3.075,4.925],[-3.435,-4.925]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1268.064,251.725]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-5.3,-0.31]],"o":[[1.23,-7.85],[0,0]],"v":[[-5.175,5.245],[5.175,-4.935]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1259.454,251.735]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-7.05,15.66],[-1.54,4.9],[-0.91,4.19],[-0.62,5.21]],"o":[[11.12,-10.34],[1.99,-4.4],[1.23,-3.86],[1.06,-4.76],[0,0]],"v":[[-19.435,39.785],[8.395,1.185],[13.705,-12.755],[16.915,-24.835],[19.435,-39.785]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1251.704,296.435]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.68,-3.22],[0.94,-3.16],[1.58,-3.65]],"o":[[-0.53,3.56],[-0.74,3.51],[-1.22,4.13],[0,0]],"v":[[4.255,-15.895],[2.435,-5.745],[-0.075,4.255],[-4.255,15.895]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1250.024,272.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[9.56,-10.05]],"o":[[-5.12,11.94],[0,0]],"v":[[10.875,-15.895],[-10.875,15.895]],"c":false}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1234.894,304.675]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-6.42,-1.15],[11.12,-10.34],[0,0],[-5.12,11.94]],"o":[[-7.05,15.66],[0,0],[9.56,-10.05],[3.14,4.74]],"v":[[18.04,-14.88],[-9.79,23.72],[-18.04,8.07],[3.71,-23.72]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1242.059,312.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.22,4.13],[-6.13,0.64],[1.99,-4.4],[3.14,4.74],[0,0]],"o":[[4.18,5.01],[-1.54,4.9],[-6.42,-1.15],[0,0],[1.58,-3.65]],"v":[[-5.64,-10.245],[9.82,-3.695],[4.51,10.245],[-9.82,1.405],[-9.82,1.395]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1255.589,287.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-5.3,-0.31],[0.36,-5.98],[1.06,-4.76],[5.39,3.9],[-0.53,3.56]],"o":[[4.63,0.66],[-0.62,5.21],[-5.39,0.92],[0.68,-3.22],[1.23,-7.85]],"v":[[2.65,-12.705],[9.16,-2.855],[6.64,12.095],[-9.52,7.625],[-7.7,-2.525]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.443,0.004,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1261.979,259.505]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.74,3.51],[-5.39,0.92],[1.23,-3.86],[4.18,5.01]],"o":[[5.39,3.9],[-0.91,4.19],[-6.13,0.64],[0.94,-3.16]],"v":[[-6.825,-8.595],[9.335,-4.125],[6.125,7.955],[-9.335,1.405]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[1,0.98,0.941,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1259.284,275.725]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":60,"op":80,"st":0},{"ind":9,"ty":4,"nm":"a","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[-116.47,239.61,0],"l":2},"a":{"a":0,"k":[965.53,543.11,0],"l":2},"s":{"a":0,"k":[100,100,100],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-25.92,23.58],[-1.5,-2.76],[8.92,-8.56],[30.96,-0.76],[0.38,8.8]],"o":[[50.97,3.27],[5.48,-4.99],[1.5,2.76],[-7.4,7.61],[-30.96,0.76],[-0.38,-8.79]],"v":[[-50.885,6.21],[42.105,-19.95],[54.035,-19.35],[48.835,-2.67],[-18.395,24.18],[-57.375,14.62]],"c":true}},"nm":"P"},{"ty":"st","c":{"a":0,"k":[0,0.129,0.251,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":2,"nm":"S"},{"ty":"tr","p":{"a":0,"k":[1181.913,340.52]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.5,-2.76],[8.92,-8.56],[30.96,-0.76],[0.38,8.8],[0,0],[-25.92,23.58]],"o":[[1.5,2.76],[-7.4,7.61],[-30.96,0.76],[-0.38,-8.79],[50.97,3.27],[5.48,-4.99]],"v":[[54.035,-19.35],[48.835,-2.67],[-18.395,24.18],[-57.375,14.62],[-50.885,6.21],[42.105,-19.95]],"c":true}},"nm":"P"},{"ty":"fl","c":{"a":0,"k":[0,0.549,0.349,1]},"o":{"a":0,"k":100},"r":1,"nm":"F"},{"ty":"tr","p":{"a":0,"k":[1181.914,340.52]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"G"}],"ip":60,"op":80,"st":0}]}],"layers":[{"ind":1,"ty":0,"nm":"C","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[187.5,120,0],"l":2},"a":{"a":0,"k":[187.5,200,0],"l":2},"s":{"a":0,"k":[60,60,100],"l":2}},"ao":0,"w":375,"h":400,"ip":0,"op":80,"st":0}],"markers":[]} \ No newline at end of file diff --git a/assets/images/simple-illustrations/simple-illustration__handearth.svg b/assets/images/simple-illustrations/simple-illustration__handearth.svg new file mode 100644 index 000000000000..f79e3f73293b --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__handearth.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 0346168f0407..59a5b7417f12 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -17,6 +17,8 @@ export default { WORKSPACES: 'Settings_Workspaces', SECURITY: 'Settings_Security', STATUS: 'Settings_Status', + WALLET: 'Settings_Wallet', + WALLET_DOMAIN_CARDS: 'Settings_Wallet_DomainCards', }, SAVE_THE_WORLD: { ROOT: 'SaveTheWorld_Root', diff --git a/src/components/Icon/Illustrations.js b/src/components/Icon/Illustrations.js index 0cd4e80396c9..0e39872a3da6 100644 --- a/src/components/Icon/Illustrations.js +++ b/src/components/Icon/Illustrations.js @@ -45,6 +45,7 @@ import MoneyBadge from '../../../assets/images/simple-illustrations/simple-illus import TreasureChest from '../../../assets/images/simple-illustrations/simple-illustration__treasurechest.svg'; import ThumbsUpStars from '../../../assets/images/simple-illustrations/simple-illustration__thumbsupstars.svg'; import Hands from '../../../assets/images/product-illustrations/home-illustration-hands.svg'; +import HandEarth from '../../../assets/images/simple-illustrations/simple-illustration__handearth.svg'; export { Abracadabra, @@ -94,4 +95,5 @@ export { TreasureChest, ThumbsUpStars, Hands, + HandEarth, }; diff --git a/src/components/IllustratedHeaderPageLayout.js b/src/components/IllustratedHeaderPageLayout.js index c45f5e2452dd..4e11e8253074 100644 --- a/src/components/IllustratedHeaderPageLayout.js +++ b/src/components/IllustratedHeaderPageLayout.js @@ -1,10 +1,17 @@ +import _ from 'underscore'; import React from 'react'; import PropTypes from 'prop-types'; -import Lottie from './Lottie'; +import {ScrollView, View} from 'react-native'; +import Lottie from 'lottie-react-native'; import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBackButtonPropTypes'; +import HeaderWithBackButton from './HeaderWithBackButton'; +import ScreenWrapper from './ScreenWrapper'; import styles from '../styles/styles'; import themeColors from '../styles/themes/default'; -import HeaderPageLayout from './HeaderPageLayout'; +import * as StyleUtils from '../styles/StyleUtils'; +import useWindowDimensions from '../hooks/useWindowDimensions'; +import FixedFooter from './FixedFooter'; +import useNetwork from '../hooks/useNetwork'; const propTypes = { ...headerWithBackButtonPropTypes, @@ -23,37 +30,59 @@ const propTypes = { /** Overlay content to display on top of animation */ overlayContent: PropTypes.func, + + /** Whether to include padding top */ + includePaddingTop: PropTypes.bool, }; const defaultProps = { backgroundColor: themeColors.appBG, footer: null, overlayContent: null, + includePaddingTop: true, }; -function IllustratedHeaderPageLayout({backgroundColor, children, illustration, footer, overlayContent, ...propsToPassToHeader}) { +function IllustratedHeaderPageLayout({backgroundColor, children, illustration, footer, overlayContent, includePaddingTop, ...propsToPassToHeader}) { + const {windowHeight} = useWindowDimensions(); + const {isOffline} = useNetwork(); return ( - + {({safeAreaPaddingBottomStyle}) => ( <> - - {overlayContent && overlayContent()} + + + + + + {overlayContent && overlayContent()} + + {children} + + {!_.isNull(footer) && {footer}} + - } - headerContainerStyles={[styles.justifyContentCenter, styles.w100]} - footer={footer} - // eslint-disable-next-line react/jsx-props-no-spreading - {...propsToPassToHeader} - > - {children} - + )} + ); } diff --git a/src/components/LottieAnimations.js b/src/components/LottieAnimations.js index 167b1078c3ca..fdb8af8af64d 100644 --- a/src/components/LottieAnimations.js +++ b/src/components/LottieAnimations.js @@ -1,4 +1,5 @@ const ExpensifyLounge = require('../../assets/animations/ExpensifyLounge.json'); +const FastMoney = require('../../assets/animations/FastMoney.json'); const Fireworks = require('../../assets/animations/Fireworks.json'); const Hands = require('../../assets/animations/Hands.json'); const PreferencesDJ = require('../../assets/animations/PreferencesDJ.json'); @@ -6,6 +7,5 @@ const ReviewingBankInfo = require('../../assets/animations/ReviewingBankInfo.jso const WorkspacePlanet = require('../../assets/animations/WorkspacePlanet.json'); const SaveTheWorld = require('../../assets/animations/SaveTheWorld.json'); const Safe = require('../../assets/animations/Safe.json'); -const Magician = require('../../assets/animations/Magician.json'); -export {ExpensifyLounge, Fireworks, Hands, PreferencesDJ, ReviewingBankInfo, SaveTheWorld, WorkspacePlanet, Safe, Magician}; +export {ExpensifyLounge, FastMoney, Fireworks, Hands, PreferencesDJ, ReviewingBankInfo, SaveTheWorld, WorkspacePlanet, Safe}; diff --git a/src/components/Section.js b/src/components/Section.js index cd390be0d00b..c0b07d1c1453 100644 --- a/src/components/Section.js +++ b/src/components/Section.js @@ -14,6 +14,9 @@ const propTypes = { /** The text to display in the title of the section */ title: PropTypes.string.isRequired, + /** The text to display in the subtitle of the section */ + subtitle: PropTypes.string, + /** The icon to display along with the title */ icon: PropTypes.func, @@ -27,6 +30,18 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types containerStyles: PropTypes.arrayOf(PropTypes.object), + /** Customize the Section container */ + // eslint-disable-next-line react/forbid-prop-types + titleStyles: PropTypes.arrayOf(PropTypes.object), + + /** Customize the Section container */ + // eslint-disable-next-line react/forbid-prop-types + subtitleStyles: PropTypes.arrayOf(PropTypes.object), + + /** Customize the Section container */ + // eslint-disable-next-line react/forbid-prop-types + childrenStyles: PropTypes.arrayOf(PropTypes.object), + /** Customize the Icon container */ // eslint-disable-next-line react/forbid-prop-types iconContainerStyles: PropTypes.arrayOf(PropTypes.object), @@ -39,21 +54,24 @@ const defaultProps = { IconComponent: null, containerStyles: [], iconContainerStyles: [], + titleStyles: [], + subtitleStyles: [], + childrenStyles: [], + subtitle: null, }; -function Section(props) { - const IconComponent = props.IconComponent; +function Section({children, childrenStyles, containerStyles, icon, IconComponent, iconContainerStyles, menuItems, subtitle, subtitleStyles, title, titleStyles}) { return ( <> - - + + - {props.title} + {title} - - {Boolean(props.icon) && ( + + {Boolean(icon) && ( @@ -62,9 +80,15 @@ function Section(props) { - {props.children} + {Boolean(subtitle) && ( + + {subtitle} + + )} + + {children} - {Boolean(props.menuItems) && } + {Boolean(menuItems) && } ); diff --git a/src/components/WalletSection.js b/src/components/WalletSection.js new file mode 100644 index 000000000000..ec8a1680937c --- /dev/null +++ b/src/components/WalletSection.js @@ -0,0 +1,45 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import Section from './Section'; +import styles from '../styles/styles'; + +const propTypes = { + /** Contents to display inside the section */ + children: PropTypes.node, + + /** The icon to display along with the title */ + icon: PropTypes.func, + + /** The text to display in the subtitle of the section */ + subtitle: PropTypes.string, + + /** The text to display in the title of the section */ + title: PropTypes.string.isRequired, +}; + +const defaultProps = { + children: null, + icon: null, + subtitle: null, +}; + +function WalletSection({children, icon, subtitle, title}) { + return ( +
+ {children} +
+ ); +} + +WalletSection.defaultProps = defaultProps; +WalletSection.displayName = 'WalletSection'; +WalletSection.propTypes = propTypes; + +export default WalletSection; diff --git a/src/languages/en.ts b/src/languages/en.ts index f4c7bdc6ee60..46acb0306be2 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -834,6 +834,16 @@ export default { setDefaultFailure: 'Something went wrong. Please chat with Concierge for further assistance.', }, addBankAccountFailure: 'An unexpected error occurred while trying to add your bank account. Please try again.', + getPaidFaster: 'Get paid faster', + addPaymentMethod: 'Add a payment method to send and receive payments directly in the app.', + getPaidBackFaster: 'Get paid back faster', + secureAccessToYourMoney: 'Secure access to your money', + receiveMoney: 'Receive money in your local currency', + expensifyWallet: 'Expensify Wallet', + sendAndReceiveMoney: 'Send and receive money from your Expensify Wallet.', + bankAccounts: 'Bank accounts', + addBankAccountToSendAndReceive: 'Add a bank account to send and receive payments directly in the app.', + addBankAccount: 'Add bank account', }, cardPage: { expensifyCard: 'Expensify Card', diff --git a/src/languages/es.ts b/src/languages/es.ts index 1bbb056e82ef..9f81ee69b163 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -830,6 +830,16 @@ export default { setDefaultFailure: 'No se ha podido configurar el método de pago.', }, addBankAccountFailure: 'Ocurrió un error inesperado al intentar añadir la cuenta bancaria. Inténtalo de nuevo.', + getPaidFaster: 'Cobra más rápido', + addPaymentMethod: 'Añade un método de pago para enviar y recibir pagos directamente en la aplicación.', + getPaidBackFaster: 'Recibe tus pagos más rápido', + secureAccessToYourMoney: 'Acceso seguro a tu dinero', + receiveMoney: 'Recibe dinero en tu moneda local', + expensifyWallet: 'Billetera Expensify', + sendAndReceiveMoney: 'Envía y recibe dinero desde tu Billetera Expensify.', + bankAccounts: 'Cuentas bancarias', + addBankAccountToSendAndReceive: 'Añade una cuenta bancaria para enviar y recibir pagos directamente en la aplicación.', + addBankAccount: 'Agregar cuenta bancaria', }, cardPage: { expensifyCard: 'Tarjeta Expensify', diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index 34a52adfeca9..c7a3b14e4fb0 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -101,7 +101,7 @@ function NavigationRoot(props) { const animateStatusBarBackgroundColor = () => { const currentRoute = navigationRef.getCurrentRoute(); - const currentScreenBackgroundColor = themeColors.PAGE_BACKGROUND_COLORS[currentRoute.name] || themeColors.appBG; + const currentScreenBackgroundColor = (currentRoute.params && currentRoute.params.backgroundColor) || themeColors.PAGE_BACKGROUND_COLORS[currentRoute.name] || themeColors.appBG; prevStatusBarBackgroundColor.current = statusBarBackgroundColor.current; statusBarBackgroundColor.current = currentScreenBackgroundColor; diff --git a/src/pages/settings/Wallet/PaymentMethodList.js b/src/pages/settings/Wallet/PaymentMethodList.js index d757db370b05..abee54d9fcd4 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.js +++ b/src/pages/settings/Wallet/PaymentMethodList.js @@ -22,6 +22,7 @@ import FormAlertWrapper from '../../../components/FormAlertWrapper'; import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; import * as PaymentMethods from '../../../libs/actions/PaymentMethods'; import Log from '../../../libs/Log'; +import stylePropTypes from '../../../styles/stylePropTypes'; const propTypes = { /** What to do when a menu item is pressed */ @@ -36,6 +37,9 @@ const propTypes = { /** Whether the add Payment button be shown on the list */ shouldShowAddPaymentMethodButton: PropTypes.bool, + /** Whether the empty list message should be shown when the list is empty */ + shouldShowEmptyListMessage: PropTypes.bool, + /** Are we loading payment methods? */ isLoadingPaymentMethods: PropTypes.bool, @@ -69,6 +73,12 @@ const propTypes = { /** React ref being forwarded to the PaymentMethodList Button */ buttonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + /** To enable/disable scrolling */ + shouldEnableScroll: PropTypes.bool, + + /** List container style */ + style: stylePropTypes, + ...withLocalizePropTypes, }; @@ -81,6 +91,7 @@ const defaultProps = { }, isLoadingPaymentMethods: true, shouldShowAddPaymentMethodButton: true, + shouldShowEmptyListMessage: true, filterType: '', actionPaymentMethodType: '', activePaymentMethodID: '', @@ -88,6 +99,8 @@ const defaultProps = { listHeaderComponent: null, buttonRef: () => {}, onListContentSizeChange: () => {}, + shouldEnableScroll: true, + style: {}, }; /** @@ -143,9 +156,26 @@ function shouldShowDefaultBadge(filteredPaymentMethods, isDefault = false) { function isPaymentMethodActive(actionPaymentMethodType, activePaymentMethodID, paymentMethod) { return paymentMethod.accountType === actionPaymentMethodType && paymentMethod.methodID === activePaymentMethodID; } -function PaymentMethodList(props) { - const {actionPaymentMethodType, activePaymentMethodID, bankAccountList, fundList, filterType, network, onPress, shouldShowSelectedState, selectedMethodID, translate} = props; - +function PaymentMethodList({ + actionPaymentMethodType, + activePaymentMethodID, + bankAccountList, + buttonRef, + fundList, + filterType, + isLoadingPaymentMethods, + listHeaderComponent, + network, + onListContentSizeChange, + onPress, + shouldEnableScroll, + shouldShowSelectedState, + shouldShowAddPaymentMethodButton, + shouldShowEmptyListMessage, + selectedMethodID, + style, + translate, +}) { const filteredPaymentMethods = useMemo(() => { const paymentCardList = fundList || {}; // Hide any billing cards that are not P2P debit cards for now because you cannot make them your default method, or delete them @@ -183,6 +213,18 @@ function PaymentMethodList(props) { */ const renderListEmptyComponent = useCallback(() => {translate('paymentMethodList.addFirstPaymentMethod')}, [translate]); + const renderListFooterComponent = useCallback( + () => ( + + ), + [onPress, translate], + ); + /** * Create a menuItem for each passed paymentMethod * @@ -209,13 +251,13 @@ function PaymentMethodList(props) { iconHeight={item.iconSize} iconWidth={item.iconSize} badgeText={shouldShowDefaultBadge(filteredPaymentMethods, item.isDefault) ? translate('paymentMethodList.defaultPaymentMethod') : null} - wrapperStyle={item.wrapperStyle} + wrapperStyle={styles.paymentMethod} shouldShowSelectedState={shouldShowSelectedState} isSelected={selectedMethodID === item.methodID} /> ), - [shouldShowSelectedState, selectedMethodID, filteredPaymentMethods, translate], + [filteredPaymentMethods, translate, shouldShowSelectedState, selectedMethodID], ); return ( @@ -224,25 +266,28 @@ function PaymentMethodList(props) { data={filteredPaymentMethods} renderItem={renderItem} keyExtractor={(item) => item.key} - ListEmptyComponent={renderListEmptyComponent(translate)} - ListHeaderComponent={props.listHeaderComponent} - onContentSizeChange={props.onListContentSizeChange} + ListEmptyComponent={shouldShowEmptyListMessage ? renderListEmptyComponent(translate) : null} + ListHeaderComponent={listHeaderComponent} + ListFooterComponent={renderListFooterComponent} + onContentSizeChange={onListContentSizeChange} + scrollEnabled={shouldEnableScroll} + style={style} /> - {props.shouldShowAddPaymentMethodButton && ( + {shouldShowAddPaymentMethodButton && ( {(isOffline) => (