From 37cf275a7f4769b0f339c0e8ee58b958c03e95f9 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 18 Oct 2024 19:32:02 -0700 Subject: [PATCH 1/5] Mobile: Use a FontAwesome icon for the trash can See https://github.com/laurent22/joplin/issues/11202 --- .../components/side-menu-content.tsx | 141 +++++++++++------- 1 file changed, 86 insertions(+), 55 deletions(-) diff --git a/packages/app-mobile/components/side-menu-content.tsx b/packages/app-mobile/components/side-menu-content.tsx index a40e6fb2e1c..70734cfbff2 100644 --- a/packages/app-mobile/components/side-menu-content.tsx +++ b/packages/app-mobile/components/side-menu-content.tsx @@ -1,13 +1,14 @@ -const React = require('react'); +import * as React from 'react'; import { useMemo, useEffect, useCallback, useContext } from 'react'; -const { Easing, Animated, TouchableOpacity, Text, StyleSheet, ScrollView, View, Image } = require('react-native'); +import { Easing, Animated, TouchableOpacity, Text, StyleSheet, ScrollView, View, Image } from 'react-native'; const { connect } = require('react-redux'); -const Icon = require('react-native-vector-icons/Ionicons').default; +const IonIcon = require('react-native-vector-icons/Ionicons').default; +import Icon from './Icon'; import Folder from '@joplin/lib/models/Folder'; import Synchronizer from '@joplin/lib/Synchronizer'; import NavService from '@joplin/lib/services/NavService'; import { _ } from '@joplin/lib/locale'; -import { ThemeStyle, themeStyle } from './global-style'; +import { themeStyle } from './global-style'; import { buildFolderTree, isFolderSelected, renderFolders } from '@joplin/lib/components/shared/side-menu-shared'; import { FolderEntity, FolderIcon, FolderIconType } from '@joplin/lib/services/database/types'; import { AppState } from '../utils/types'; @@ -19,6 +20,7 @@ import restoreItems from '@joplin/lib/services/trash/restoreItems'; import emptyTrash from '@joplin/lib/services/trash/emptyTrash'; import { ModelType } from '@joplin/lib/BaseModel'; import { DialogContext } from './DialogManager'; +import { TextStyle, ViewStyle } from 'react-native'; const { TouchableRipple } = require('react-native-paper'); const { substrWithEllipsis } = require('@joplin/lib/string-utils'); @@ -63,27 +65,44 @@ const SideMenuContentComponent = (props: Props) => { const styles_ = useMemo(() => { const theme = themeStyle(props.themeId); - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const styles: any = { + const buttonStyle: ViewStyle = { + flex: 1, + flexDirection: 'row', + flexBasis: 'auto', + height: 36, + alignItems: 'center', + paddingLeft: theme.marginLeft, + paddingRight: theme.marginRight, + }; + const buttonTextStyle: TextStyle = { + flex: 1, + color: theme.color, + paddingLeft: 10, + fontSize: theme.fontSize, + }; + const sidebarIconStyle: TextStyle = { + fontSize: 22, + color: theme.color, + width: 26, + textAlign: 'center', + textAlignVertical: 'center', + }; + const folderButtonStyle: ViewStyle = { + ...buttonStyle, + paddingLeft: 0, + }; + const sideButtonStyle: ViewStyle = { + ...buttonStyle, + flex: 0, + }; + + const styles = StyleSheet.create({ menu: { flex: 1, backgroundColor: theme.backgroundColor, }, - button: { - flex: 1, - flexDirection: 'row', - flexBasis: 'auto', - height: 36, - alignItems: 'center', - paddingLeft: theme.marginLeft, - paddingRight: theme.marginRight, - }, - buttonText: { - flex: 1, - color: theme.color, - paddingLeft: 10, - fontSize: theme.fontSize, - }, + button: buttonStyle, + buttonText: buttonTextStyle, syncStatus: { paddingLeft: theme.marginLeft, paddingRight: theme.marginRight, @@ -91,31 +110,42 @@ const SideMenuContentComponent = (props: Props) => { fontSize: theme.fontSizeSmaller, flex: 0, }, - sidebarIcon: { - fontSize: 22, - color: theme.color, + sidebarIcon: sidebarIconStyle, + folderButton: folderButtonStyle, + folderButtonText: { + ...buttonTextStyle, + paddingLeft: 0, + }, + folderButtonSelected: { + ...folderButtonStyle, + backgroundColor: theme.selectedColor, + }, + folderToggleIcon: { + ...theme.icon, + color: theme.colorFaded, + paddingTop: 3, + }, + sideButton: sideButtonStyle, + sideButtonSelected: { + ...sideButtonStyle, + }, + sideButtonText: { + ...buttonTextStyle, + }, + folderTextIcon: { + ...sidebarIconStyle, + marginRight: folderIconRightMargin, width: 26, - textAlign: 'center', - textAlignVertical: 'center', }, - }; - - styles.folderButton = { ...styles.button }; - styles.folderButton.paddingLeft = 0; - styles.folderButtonText = { ...styles.buttonText, paddingLeft: 0 }; - styles.folderButtonSelected = { ...styles.folderButton }; - styles.folderButtonSelected.backgroundColor = theme.selectedColor; - styles.folderIcon = { ...theme.icon }; - styles.folderIcon.color = theme.colorFaded; // '#0072d5'; - styles.folderIcon.paddingTop = 3; - - styles.sideButton = { ...styles.button, flex: 0 }; - styles.sideButtonSelected = { ...styles.sideButton, backgroundColor: theme.selectedColor }; - styles.sideButtonText = { ...styles.buttonText }; - - styles.emptyFolderIcon = { ...styles.sidebarIcon, marginRight: folderIconRightMargin, width: 26 }; + folderImageIcon: { + marginRight: folderIconRightMargin, + width: 27, + height: 20, + resizeMode: 'contain', + }, + }); - return StyleSheet.create(styles); + return styles; }, [props.themeId]); useEffect(() => { @@ -374,21 +404,23 @@ const SideMenuContentComponent = (props: Props) => { if (actionDone === 'auth') props.dispatch({ type: 'SIDE_MENU_CLOSE' }); }, [performSync, props.dispatch]); - const renderFolderIcon = (folderId: string, theme: ThemeStyle, folderIcon: FolderIcon) => { + const renderFolderIcon = (folderId: string, folderIcon: FolderIcon) => { if (!folderIcon) { if (folderId === getTrashFolderId()) { - folderIcon = getTrashFolderIcon(FolderIconType.Emoji); + folderIcon = getTrashFolderIcon(FolderIconType.FontAwesome); } else if (alwaysShowFolderIcons) { - return ; + return ; } else { return null; } } - if (folderIcon.type === 1) { // FolderIconType.Emoji - return {folderIcon.emoji}; - } else if (folderIcon.type === 2) { // FolderIconType.DataUrl - return ; + if (folderIcon.type === FolderIconType.Emoji) { + return {folderIcon.emoji}; + } else if (folderIcon.type === FolderIconType.DataUrl) { + return ; + } else if (folderIcon.type === FolderIconType.FontAwesome) { + return ; } else { throw new Error(`Unsupported folder icon type: ${folderIcon.type}`); } @@ -419,12 +451,11 @@ const SideMenuContentComponent = (props: Props) => { const collapsed = props.collapsedFolderIds.indexOf(folder.id) >= 0; const iconName = collapsed ? 'chevron-down' : 'chevron-up'; - const iconComp = ; + const iconComp = ; iconWrapper = !hasChildren ? null : ( { if (hasChildren) folder_togglePress(folder); }} @@ -455,7 +486,7 @@ const SideMenuContentComponent = (props: Props) => { role='button' > - {renderFolderIcon(folder.id, theme, folderIcon)} + {renderFolderIcon(folder.id, folderIcon)} {Folder.displayTitle(folder)} @@ -467,8 +498,8 @@ const SideMenuContentComponent = (props: Props) => { }; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - const renderSidebarButton = (key: string, title: string, iconName: string, onPressHandler: Function = null, selected = false) => { - let icon = ; + const renderSidebarButton = (key: string, title: string, iconName: string, onPressHandler: ()=> void = null, selected = false) => { + let icon = ; if (key === 'synchronize_button') { icon = {icon}; From 754862a89649cb02aa6ec9e54c09e6e3f01b72b5 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 18 Oct 2024 19:44:05 -0700 Subject: [PATCH 2/5] Style adjustments --- .../components/side-menu-content.tsx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/app-mobile/components/side-menu-content.tsx b/packages/app-mobile/components/side-menu-content.tsx index 70734cfbff2..5fffb929e36 100644 --- a/packages/app-mobile/components/side-menu-content.tsx +++ b/packages/app-mobile/components/side-menu-content.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useMemo, useEffect, useCallback, useContext } from 'react'; -import { Easing, Animated, TouchableOpacity, Text, StyleSheet, ScrollView, View, Image } from 'react-native'; +import { Easing, Animated, TouchableOpacity, Text, StyleSheet, ScrollView, View, Image, ImageStyle } from 'react-native'; const { connect } = require('react-redux'); const IonIcon = require('react-native-vector-icons/Ionicons').default; import Icon from './Icon'; @@ -87,6 +87,10 @@ const SideMenuContentComponent = (props: Props) => { textAlign: 'center', textAlignVertical: 'center', }; + const folderIconBase: ViewStyle&ImageStyle = { + marginRight: folderIconRightMargin, + width: 27, + }; const folderButtonStyle: ViewStyle = { ...buttonStyle, paddingLeft: 0, @@ -132,14 +136,17 @@ const SideMenuContentComponent = (props: Props) => { sideButtonText: { ...buttonTextStyle, }, - folderTextIcon: { + folderBaseIcon: { + ...sidebarIconStyle, + ...folderIconBase, + }, + folderEmojiIcon: { ...sidebarIconStyle, - marginRight: folderIconRightMargin, - width: 26, + ...folderIconBase, + fontSize: theme.fontSize, }, folderImageIcon: { - marginRight: folderIconRightMargin, - width: 27, + ...folderIconBase, height: 20, resizeMode: 'contain', }, @@ -409,18 +416,18 @@ const SideMenuContentComponent = (props: Props) => { if (folderId === getTrashFolderId()) { folderIcon = getTrashFolderIcon(FolderIconType.FontAwesome); } else if (alwaysShowFolderIcons) { - return ; + return ; } else { return null; } } if (folderIcon.type === FolderIconType.Emoji) { - return {folderIcon.emoji}; + return {folderIcon.emoji}; } else if (folderIcon.type === FolderIconType.DataUrl) { return ; } else if (folderIcon.type === FolderIconType.FontAwesome) { - return ; + return ; } else { throw new Error(`Unsupported folder icon type: ${folderIcon.type}`); } From 0c0b1bd1badd19bbd9ff993e53f1ced7ce618b0b Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 18 Oct 2024 19:57:30 -0700 Subject: [PATCH 3/5] Stronger types --- .../components/side-menu-content.tsx | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/app-mobile/components/side-menu-content.tsx b/packages/app-mobile/components/side-menu-content.tsx index 5fffb929e36..c1a9ab1b6ef 100644 --- a/packages/app-mobile/components/side-menu-content.tsx +++ b/packages/app-mobile/components/side-menu-content.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import { useMemo, useEffect, useCallback, useContext } from 'react'; import { Easing, Animated, TouchableOpacity, Text, StyleSheet, ScrollView, View, Image, ImageStyle } from 'react-native'; -const { connect } = require('react-redux'); +import { Dispatch } from 'redux'; +import { connect } from 'react-redux'; const IonIcon = require('react-native-vector-icons/Ionicons').default; import Icon from './Icon'; import Folder from '@joplin/lib/models/Folder'; @@ -21,21 +22,19 @@ import emptyTrash from '@joplin/lib/services/trash/emptyTrash'; import { ModelType } from '@joplin/lib/BaseModel'; import { DialogContext } from './DialogManager'; import { TextStyle, ViewStyle } from 'react-native'; +import { StateDecryptionWorker, StateResourceFetcher } from '@joplin/lib/reducer'; const { TouchableRipple } = require('react-native-paper'); const { substrWithEllipsis } = require('@joplin/lib/string-utils'); interface Props { syncStarted: boolean; themeId: number; - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - dispatch: Function; + dispatch: Dispatch; collapsedFolderIds: string[]; // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied syncReport: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - decryptionWorker: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - resourceFetcher: any; + decryptionWorker: StateDecryptionWorker; + resourceFetcher: StateResourceFetcher; syncOnlyOverWifi: boolean; isOnMobileData: boolean; notesParentType: string; @@ -56,8 +55,7 @@ const syncIconRotation = syncIconRotationValue.interpolate({ const folderIconRightMargin = 10; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied -let syncIconAnimation: any; +let syncIconAnimation: Animated.CompositeAnimation|null = null; const SideMenuContentComponent = (props: Props) => { const alwaysShowFolderIcons = useMemo(() => Folder.shouldShowFolderIcons(props.folders), [props.folders]); @@ -414,7 +412,7 @@ const SideMenuContentComponent = (props: Props) => { const renderFolderIcon = (folderId: string, folderIcon: FolderIcon) => { if (!folderIcon) { if (folderId === getTrashFolderId()) { - folderIcon = getTrashFolderIcon(FolderIconType.FontAwesome); + folderIcon = getTrashFolderIcon(FolderIconType.Emoji); } else if (alwaysShowFolderIcons) { return ; } else { @@ -436,8 +434,7 @@ const SideMenuContentComponent = (props: Props) => { const renderFolderItem = (folder: FolderEntity, hasChildren: boolean, depth: number) => { const theme = themeStyle(props.themeId); - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const folderButtonStyle: any = { + const folderButtonStyle: ViewStyle = { flex: 1, flexDirection: 'row', flexBasis: 'auto', @@ -450,8 +447,7 @@ const SideMenuContentComponent = (props: Props) => { if (selected) folderButtonStyle.backgroundColor = theme.selectedColor; folderButtonStyle.paddingLeft = depth * 10 + theme.marginLeft; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const iconWrapperStyle: any = { paddingLeft: 10, paddingRight: 10 }; + const iconWrapperStyle: ViewStyle = { paddingLeft: 10, paddingRight: 10 }; if (selected) iconWrapperStyle.backgroundColor = theme.selectedColor; let iconWrapper = null; @@ -504,7 +500,6 @@ const SideMenuContentComponent = (props: Props) => { ); }; - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied const renderSidebarButton = (key: string, title: string, iconName: string, onPressHandler: ()=> void = null, selected = false) => { let icon = ; From cc37d7341da3163f92c692b060563db4e39a114b Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 18 Oct 2024 19:58:16 -0700 Subject: [PATCH 4/5] Avoid changing styles in this pull request --- packages/app-mobile/components/side-menu-content.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/app-mobile/components/side-menu-content.tsx b/packages/app-mobile/components/side-menu-content.tsx index c1a9ab1b6ef..c2bebfd5a1b 100644 --- a/packages/app-mobile/components/side-menu-content.tsx +++ b/packages/app-mobile/components/side-menu-content.tsx @@ -141,6 +141,7 @@ const SideMenuContentComponent = (props: Props) => { folderEmojiIcon: { ...sidebarIconStyle, ...folderIconBase, + textAlign: undefined, fontSize: theme.fontSize, }, folderImageIcon: { From e77b461a0d86394b17d04c765d7175ac68e849bd Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Wed, 23 Oct 2024 09:12:08 -0700 Subject: [PATCH 5/5] Remove unused opacity prop --- packages/app-mobile/components/side-menu-content.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/app-mobile/components/side-menu-content.tsx b/packages/app-mobile/components/side-menu-content.tsx index c2bebfd5a1b..b780ac7faa0 100644 --- a/packages/app-mobile/components/side-menu-content.tsx +++ b/packages/app-mobile/components/side-menu-content.tsx @@ -39,7 +39,6 @@ interface Props { isOnMobileData: boolean; notesParentType: string; folders: FolderEntity[]; - opacity: number; profileConfig: ProfileConfig; inboxJopId: string; selectedFolderId: string; @@ -624,7 +623,7 @@ const SideMenuContentComponent = (props: Props) => { return ( - + {items}