mirror of
https://github.com/standardnotes/mobile.git
synced 2026-04-24 08:07:00 -04:00
feat: show listed on mobile (#554)
* feat: show listed on mobile * fix: avoid endless loop when fetching listed items * refactor: render side menu items without checking item's index * feat: implement new blog addition * feat: ui improvements, show appropriate message when no action available for a blog * feat: refresh listed menu items when user executes blog-related action * feat: UI improvements - add icons, better distinction between Listed sections * feat: show loading indicators on async actions, near respective items * chore: update dependencies * fix: updates regarding last snjs changes * fix: remove unnecessary html tags from message
This commit is contained in:
@@ -26,8 +26,8 @@
|
||||
"@react-navigation/native": "^5.9.3",
|
||||
"@react-navigation/stack": "^5.14.3",
|
||||
"@standardnotes/sncrypto-common": "1.7.0",
|
||||
"@standardnotes/snjs": "2.61.0",
|
||||
"@standardnotes/stylekit": "5.5.0",
|
||||
"@standardnotes/snjs": "2.63.1",
|
||||
"@standardnotes/stylekit": "5.9.0",
|
||||
"js-base64": "^3.5.2",
|
||||
"moment": "^2.29.1",
|
||||
"react": "17.0.2",
|
||||
@@ -69,8 +69,8 @@
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@react-native-community/eslint-config": "^2.0.0",
|
||||
"@standardnotes/components": "^1.7.3",
|
||||
"@standardnotes/features": "^1.32.6",
|
||||
"@standardnotes/components": "^1.7.5",
|
||||
"@standardnotes/features": "^1.32.11",
|
||||
"@types/detox": "^16.4.1",
|
||||
"@types/faker": "^5.1.3",
|
||||
"@types/jest": "^26.0.14",
|
||||
|
||||
@@ -4,6 +4,8 @@ import ArchiveIcon from '@standardnotes/stylekit/dist/icons/ic-archive.svg';
|
||||
import AuthenticatorIcon from '@standardnotes/stylekit/dist/icons/ic-authenticator.svg';
|
||||
import CodeIcon from '@standardnotes/stylekit/dist/icons/ic-code.svg';
|
||||
import MarkdownIcon from '@standardnotes/stylekit/dist/icons/ic-markdown.svg';
|
||||
import NotesIcon from '@standardnotes/stylekit/dist/icons/ic-notes.svg';
|
||||
import OpenInIcon from '@standardnotes/stylekit/dist/icons/ic-open-in.svg';
|
||||
import PencilOffIcon from '@standardnotes/stylekit/dist/icons/ic-pencil-off.svg';
|
||||
import PinFilledIcon from '@standardnotes/stylekit/dist/icons/ic-pin-filled.svg';
|
||||
import SpreadsheetsIcon from '@standardnotes/stylekit/dist/icons/ic-spreadsheets.svg';
|
||||
@@ -11,6 +13,7 @@ import TasksIcon from '@standardnotes/stylekit/dist/icons/ic-tasks.svg';
|
||||
import PlainTextIcon from '@standardnotes/stylekit/dist/icons/ic-text-paragraph.svg';
|
||||
import RichTextIcon from '@standardnotes/stylekit/dist/icons/ic-text-rich.svg';
|
||||
import TrashFilledIcon from '@standardnotes/stylekit/dist/icons/ic-trash-filled.svg';
|
||||
import UserAddIcon from '@standardnotes/stylekit/dist/icons/ic-user-add.svg';
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeContext } from 'styled-components';
|
||||
|
||||
@@ -26,6 +29,9 @@ const ICONS = {
|
||||
'trash-filled': TrashFilledIcon,
|
||||
'pin-filled': PinFilledIcon,
|
||||
archive: ArchiveIcon,
|
||||
'user-add': UserAddIcon,
|
||||
'open-in': OpenInIcon,
|
||||
notes: NotesIcon,
|
||||
};
|
||||
|
||||
export type TEditorIcon = Extract<
|
||||
@@ -42,9 +48,10 @@ export type TEditorIcon = Extract<
|
||||
| 'pin-filled'
|
||||
| 'archive'
|
||||
>;
|
||||
export type TGeneralIcon = Extract<IconType, 'user-add' | 'open-in' | 'notes'>;
|
||||
|
||||
type Props = {
|
||||
type: TEditorIcon;
|
||||
type: TEditorIcon | TGeneralIcon;
|
||||
fill?: string;
|
||||
styles?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import { ApplicationIdentifier, DeviceInterface } from '@standardnotes/snjs';
|
||||
import { ApplicationIdentifier, AbstractDevice } from '@standardnotes/snjs';
|
||||
import { Alert, Linking, Platform } from 'react-native';
|
||||
import DefaultPreference from 'react-native-default-preference';
|
||||
import FingerprintScanner from 'react-native-fingerprint-scanner';
|
||||
@@ -45,7 +45,7 @@ const showLoadFailForItemIds = (failedItemIds: string[]) => {
|
||||
Alert.alert('Unable to load item(s)', text);
|
||||
};
|
||||
|
||||
export class MobileDeviceInterface extends DeviceInterface {
|
||||
export class MobileDeviceInterface extends AbstractDevice {
|
||||
constructor() {
|
||||
super(setTimeout, setInterval);
|
||||
}
|
||||
|
||||
35
src/screens/SideMenu/Listed.styled.ts
Normal file
35
src/screens/SideMenu/Listed.styled.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import styled from 'styled-components/native';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
blogItemIcon: {
|
||||
marginTop: -6,
|
||||
},
|
||||
loadingIndicator: {
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
blogActionInProgressIndicator: {
|
||||
marginTop: -5,
|
||||
marginLeft: 6,
|
||||
transform: [
|
||||
{
|
||||
scale: 0.8,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
export const CreateBlogContainer = styled.View`
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
export const CantLoadActionsText = styled.Text`
|
||||
font-size: 12px;
|
||||
margin-top: -12px;
|
||||
margin-bottom: 10px;
|
||||
opacity: 0.7;
|
||||
color: ${({ theme }) => theme.stylekitContrastForegroundColor};
|
||||
`;
|
||||
export const ListedItemRow = styled.View`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
`;
|
||||
242
src/screens/SideMenu/Listed.tsx
Normal file
242
src/screens/SideMenu/Listed.tsx
Normal file
@@ -0,0 +1,242 @@
|
||||
import { SnIcon } from '@Components/SnIcon';
|
||||
import { ApplicationContext } from '@Root/ApplicationContext';
|
||||
import {
|
||||
CantLoadActionsText,
|
||||
CreateBlogContainer,
|
||||
ListedItemRow,
|
||||
styles,
|
||||
} from '@Screens/SideMenu/Listed.styled';
|
||||
import { SideMenuCell } from '@Screens/SideMenu/SideMenuCell';
|
||||
import { SideMenuOptionIconDescriptionType } from '@Screens/SideMenu/SideMenuSection';
|
||||
import { ButtonType, ListedAccount, SNNote } from '@standardnotes/snjs';
|
||||
import { ListedAccountInfo } from '@standardnotes/snjs/dist/@types/services/api/responses';
|
||||
import { useCustomActionSheet } from '@Style/custom_action_sheet';
|
||||
import React, { FC, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { ActivityIndicator, FlatList, View } from 'react-native';
|
||||
|
||||
type TProps = {
|
||||
note: SNNote;
|
||||
};
|
||||
|
||||
type TListedAccountItem =
|
||||
| ListedAccountInfo
|
||||
| Pick<ListedAccountInfo, 'display_name'>;
|
||||
|
||||
export const Listed: FC<TProps> = ({ note }) => {
|
||||
const application = useContext(ApplicationContext);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isActionInProgress, setIsActionInProgress] = useState(false);
|
||||
const [isRequestingAccount, setIsRequestingAccount] = useState(false);
|
||||
|
||||
const [listedAccounts, setListedAccounts] = useState<ListedAccount[]>([]);
|
||||
const [listedAccountDetails, setListedAccountDetails] = useState<
|
||||
TListedAccountItem[]
|
||||
>([]);
|
||||
const [
|
||||
authorUrlWithInProgressAction,
|
||||
setAuthorUrlWithInProgressAction,
|
||||
] = useState<string | null>(null);
|
||||
|
||||
const { showActionSheet } = useCustomActionSheet();
|
||||
|
||||
const getListedAccountsDetails = useCallback(
|
||||
async (accounts: ListedAccount[]) => {
|
||||
if (!application) {
|
||||
return;
|
||||
}
|
||||
const listedAccountsArray: TListedAccountItem[] = [];
|
||||
|
||||
for (const listedAccountItem of accounts) {
|
||||
const listedItemInfo = await application.getListedAccountInfo(
|
||||
listedAccountItem,
|
||||
note?.uuid
|
||||
);
|
||||
|
||||
listedAccountsArray.push(
|
||||
listedItemInfo
|
||||
? listedItemInfo
|
||||
: { display_name: listedAccountItem.authorId }
|
||||
);
|
||||
}
|
||||
return listedAccountsArray;
|
||||
},
|
||||
[application, note?.uuid]
|
||||
);
|
||||
|
||||
const reloadListedAccounts = useCallback(async () => {
|
||||
if (!application) {
|
||||
return [];
|
||||
}
|
||||
setIsLoading(true);
|
||||
const accounts = await application.getListedAccounts();
|
||||
setListedAccounts(accounts);
|
||||
|
||||
setListedAccountDetails((await getListedAccountsDetails(accounts)) || []);
|
||||
setIsLoading(false);
|
||||
}, [application, getListedAccountsDetails]);
|
||||
|
||||
const registerNewAccount = useCallback(() => {
|
||||
if (!application || isRequestingAccount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestAccount = async () => {
|
||||
setIsRequestingAccount(true);
|
||||
const account = await application.requestNewListedAccount();
|
||||
if (account) {
|
||||
const openSettings = await application.alertService.confirm(
|
||||
'Your new Listed blog has been successfully created!' +
|
||||
' You can publish a new post to your blog from Standard Notes via the' +
|
||||
' Actions menu in the editor pane. Open your blog settings to begin setting it up.',
|
||||
undefined,
|
||||
'Open Settings',
|
||||
ButtonType.Info,
|
||||
'Later'
|
||||
);
|
||||
reloadListedAccounts();
|
||||
|
||||
if (openSettings) {
|
||||
const info = await application.getListedAccountInfo(account);
|
||||
if (info) {
|
||||
application.deviceInterface.openUrl(info?.settings_url);
|
||||
}
|
||||
}
|
||||
}
|
||||
setIsRequestingAccount(false);
|
||||
};
|
||||
|
||||
requestAccount();
|
||||
}, [application, isRequestingAccount, reloadListedAccounts]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadListedData = async () => {
|
||||
await reloadListedAccounts();
|
||||
};
|
||||
loadListedData();
|
||||
}, [reloadListedAccounts]);
|
||||
|
||||
const doesListedItemHaveActions = (
|
||||
item: TListedAccountItem
|
||||
): item is ListedAccountInfo => {
|
||||
return (item as ListedAccountInfo).author_url !== undefined;
|
||||
};
|
||||
|
||||
const showActionsMenu = (item: TListedAccountItem, index: number) => {
|
||||
if (!application) {
|
||||
return;
|
||||
}
|
||||
if (!doesListedItemHaveActions(item)) {
|
||||
application.alertService.alert('Unable to load actions.');
|
||||
return;
|
||||
}
|
||||
showActionSheet(
|
||||
item.display_name,
|
||||
item.actions.map(action => ({
|
||||
text: action.label,
|
||||
callback: async () => {
|
||||
setIsActionInProgress(true);
|
||||
setAuthorUrlWithInProgressAction(item.author_url);
|
||||
|
||||
const response = await application.actionsManager.runAction(
|
||||
action,
|
||||
note
|
||||
);
|
||||
|
||||
if (!response || response.error) {
|
||||
setIsActionInProgress(false);
|
||||
setAuthorUrlWithInProgressAction(null);
|
||||
return;
|
||||
}
|
||||
const listedDetails = (await getListedAccountsDetails(
|
||||
listedAccounts
|
||||
)) as TListedAccountItem[];
|
||||
setListedAccountDetails(listedDetails);
|
||||
|
||||
showActionsMenu(listedDetails[index], index);
|
||||
setIsActionInProgress(false);
|
||||
setAuthorUrlWithInProgressAction(null);
|
||||
},
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
if (!application) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<View>
|
||||
{isLoading && <ActivityIndicator style={styles.loadingIndicator} />}
|
||||
{listedAccountDetails.length > 0 && (
|
||||
<FlatList
|
||||
data={listedAccountDetails}
|
||||
renderItem={({ item, index }) => {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<View>
|
||||
<ListedItemRow>
|
||||
<SideMenuCell
|
||||
text={item.display_name}
|
||||
onSelect={() => showActionsMenu(item, index)}
|
||||
iconDesc={{
|
||||
side: 'left',
|
||||
type: SideMenuOptionIconDescriptionType.CustomComponent,
|
||||
value: (
|
||||
<SnIcon type={'notes'} styles={styles.blogItemIcon} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{isActionInProgress &&
|
||||
(item as ListedAccountInfo).author_url ===
|
||||
authorUrlWithInProgressAction && (
|
||||
<ActivityIndicator
|
||||
style={styles.blogActionInProgressIndicator}
|
||||
/>
|
||||
)}
|
||||
</ListedItemRow>
|
||||
{!isLoading && !doesListedItemHaveActions(item) && (
|
||||
<CantLoadActionsText>
|
||||
Unable to load actions
|
||||
</CantLoadActionsText>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<CreateBlogContainer>
|
||||
<ListedItemRow>
|
||||
<SideMenuCell
|
||||
text={
|
||||
isRequestingAccount ? 'Creating account...' : 'Create New Author'
|
||||
}
|
||||
onSelect={registerNewAccount}
|
||||
iconDesc={{
|
||||
side: 'left',
|
||||
type: SideMenuOptionIconDescriptionType.CustomComponent,
|
||||
value: <SnIcon type={'user-add'} styles={styles.blogItemIcon} />,
|
||||
}}
|
||||
/>
|
||||
{isRequestingAccount && (
|
||||
<ActivityIndicator style={styles.blogActionInProgressIndicator} />
|
||||
)}
|
||||
</ListedItemRow>
|
||||
<ListedItemRow>
|
||||
<SideMenuCell
|
||||
text={'Learn more'}
|
||||
onSelect={() =>
|
||||
application.deviceInterface.openUrl('https://listed.to')
|
||||
}
|
||||
iconDesc={{
|
||||
side: 'left',
|
||||
type: SideMenuOptionIconDescriptionType.CustomComponent,
|
||||
value: <SnIcon type={'open-in'} styles={styles.blogItemIcon} />,
|
||||
}}
|
||||
/>
|
||||
</ListedItemRow>
|
||||
</CreateBlogContainer>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
useStyles,
|
||||
} from './MainSideMenu.styled';
|
||||
import { SideMenuHero } from './SideMenuHero';
|
||||
import { SideMenuOption, SideMenuSection } from './SideMenuSection';
|
||||
import { SideMenuOptionIconDescriptionType, SideMenuOption, SideMenuSection } from './SideMenuSection';
|
||||
import { TagSelectionList } from './TagSelectionList';
|
||||
|
||||
type Props = {
|
||||
@@ -163,7 +163,7 @@ export const MainSideMenu = React.memo(({ drawerRef }: Props) => {
|
||||
|
||||
const iconDescriptorForTheme = (currentTheme: SNTheme | MobileTheme) => {
|
||||
const desc = {
|
||||
type: 'circle',
|
||||
type: SideMenuOptionIconDescriptionType.Circle,
|
||||
side: 'right' as 'right',
|
||||
};
|
||||
|
||||
@@ -218,7 +218,7 @@ export const MainSideMenu = React.memo(({ drawerRef }: Props) => {
|
||||
text: 'Get More Themes',
|
||||
key: 'get-theme',
|
||||
iconDesc: {
|
||||
type: 'icon',
|
||||
type: SideMenuOptionIconDescriptionType.Icon,
|
||||
name: ThemeService.nameForIcon(ICON_BRUSH),
|
||||
side: 'right',
|
||||
size: 17,
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
SCREEN_INPUT_MODAL_TAG,
|
||||
SCREEN_NOTE_HISTORY,
|
||||
} from '@Screens/screens';
|
||||
import { Listed } from '@Screens/SideMenu/Listed';
|
||||
import {
|
||||
ButtonType,
|
||||
ComponentArea,
|
||||
@@ -52,7 +53,7 @@ import DrawerLayout from 'react-native-gesture-handler/DrawerLayout';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import { ThemeContext } from 'styled-components/native';
|
||||
import { SafeAreaContainer, useStyles } from './NoteSideMenu.styled';
|
||||
import { SideMenuOption, SideMenuSection } from './SideMenuSection';
|
||||
import { SideMenuOption, SideMenuOptionIconDescriptionType, SideMenuSection } from './SideMenuSection';
|
||||
import { TagSelectionList } from './TagSelectionList';
|
||||
|
||||
function sortAlphabetically(array: SNComponent[]): SNComponent[] {
|
||||
@@ -382,7 +383,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
text: 'Get More Editors',
|
||||
key: 'get-editors',
|
||||
iconDesc: {
|
||||
type: 'icon',
|
||||
type: SideMenuOptionIconDescriptionType.Icon,
|
||||
name: ThemeService.nameForIcon(ICON_MEDICAL),
|
||||
side: 'right',
|
||||
size: 17,
|
||||
@@ -496,7 +497,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
text: rawOption.text,
|
||||
key: rawOption.icon,
|
||||
iconDesc: {
|
||||
type: 'icon',
|
||||
type: SideMenuOptionIconDescriptionType.Icon,
|
||||
side: 'right' as 'right',
|
||||
name: ThemeService.nameForIcon(rawOption.icon),
|
||||
},
|
||||
@@ -585,42 +586,70 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
enum MenuSections {
|
||||
OptionsSection = 'options-section',
|
||||
EditorsSection = 'editors-section',
|
||||
ListedSection = 'listed-section',
|
||||
TagsSection = 'tags-section',
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaContainer edges={['top', 'bottom', 'right']}>
|
||||
<FlatList
|
||||
style={styles.sections}
|
||||
data={['options-section', 'editors-section', 'tags-section'].map(
|
||||
key => ({
|
||||
key,
|
||||
noteOptions,
|
||||
editorComponents: editors,
|
||||
onTagSelect,
|
||||
selectedTags,
|
||||
})
|
||||
)}
|
||||
renderItem={({ item, index }) =>
|
||||
index === 0 ? (
|
||||
<SideMenuSection title="Options" options={item.noteOptions} />
|
||||
) : index === 1 ? (
|
||||
<SideMenuSection
|
||||
title="Editors"
|
||||
options={item.editorComponents}
|
||||
collapsed={true}
|
||||
/>
|
||||
) : index === 2 ? (
|
||||
<SideMenuSection title="Tags">
|
||||
<TagSelectionList
|
||||
hasBottomPadding={Platform.OS === 'android'}
|
||||
contentType={ContentType.Tag}
|
||||
onTagSelect={item.onTagSelect}
|
||||
selectedTags={item.selectedTags}
|
||||
emptyPlaceholder={
|
||||
'Create a new tag using the tag button in the bottom right corner.'
|
||||
}
|
||||
data={Object.values(MenuSections).map(key => ({
|
||||
key,
|
||||
noteOptions,
|
||||
editorComponents: editors,
|
||||
onTagSelect,
|
||||
selectedTags,
|
||||
}))}
|
||||
renderItem={({ item }) => {
|
||||
const {
|
||||
OptionsSection,
|
||||
EditorsSection,
|
||||
ListedSection,
|
||||
TagsSection,
|
||||
} = MenuSections;
|
||||
|
||||
if (item.key === OptionsSection) {
|
||||
return (
|
||||
<SideMenuSection title="Options" options={item.noteOptions} />
|
||||
);
|
||||
}
|
||||
if (item.key === EditorsSection) {
|
||||
return (
|
||||
<SideMenuSection
|
||||
title="Editors"
|
||||
options={item.editorComponents}
|
||||
collapsed={true}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
) : null
|
||||
}
|
||||
);
|
||||
}
|
||||
if (item.key === ListedSection) {
|
||||
return (
|
||||
<SideMenuSection title="Listed" collapsed={true}>
|
||||
<Listed note={note} />
|
||||
</SideMenuSection>
|
||||
);
|
||||
}
|
||||
if (item.key === TagsSection) {
|
||||
return (
|
||||
<SideMenuSection title="Tags">
|
||||
<TagSelectionList
|
||||
hasBottomPadding={Platform.OS === 'android'}
|
||||
contentType={ContentType.Tag}
|
||||
onTagSelect={item.onTagSelect}
|
||||
selectedTags={item.selectedTags}
|
||||
emptyPlaceholder={
|
||||
'Create a new tag using the tag button in the bottom right corner.'
|
||||
}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
/>
|
||||
|
||||
<FAB
|
||||
|
||||
@@ -16,22 +16,24 @@ import {
|
||||
TextContainer,
|
||||
Touchable,
|
||||
} from './SideMenuCell.styled';
|
||||
import { SideMenuOption } from './SideMenuSection';
|
||||
import { SideMenuOptionIconDescriptionType, SideMenuOption } from './SideMenuSection';
|
||||
|
||||
const renderIcon = (desc: SideMenuOption['iconDesc'], color: string) => {
|
||||
if (!desc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (desc.type === 'icon' && desc.name) {
|
||||
if (desc.type === SideMenuOptionIconDescriptionType.Icon && desc.name) {
|
||||
return (
|
||||
<IconGraphicContainer>
|
||||
<Icon name={desc.name} size={desc.size || 20} color={color} />
|
||||
</IconGraphicContainer>
|
||||
);
|
||||
} else if (desc.type === 'ascii') {
|
||||
}
|
||||
if (desc.type === SideMenuOptionIconDescriptionType.Ascii) {
|
||||
return <IconAscii>{desc.value}</IconAscii>;
|
||||
} else if (desc.type === 'circle') {
|
||||
}
|
||||
if (desc.type === SideMenuOptionIconDescriptionType.Circle) {
|
||||
return (
|
||||
<IconCircleContainer>
|
||||
<Circle
|
||||
@@ -40,9 +42,11 @@ const renderIcon = (desc: SideMenuOption['iconDesc'], color: string) => {
|
||||
/>
|
||||
</IconCircleContainer>
|
||||
);
|
||||
} else {
|
||||
return <RegularText>*</RegularText>;
|
||||
}
|
||||
if (desc.type === SideMenuOptionIconDescriptionType.CustomComponent) {
|
||||
return desc.value;
|
||||
}
|
||||
return <RegularText>*</RegularText>;
|
||||
};
|
||||
|
||||
export const SideMenuCell: React.FC<SideMenuOption> = props => {
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { ReactElement, useMemo, useState } from 'react';
|
||||
import { SideMenuCell } from './SideMenuCell';
|
||||
import { CollapsedLabel, Header, Root, Title } from './SideMenuSection.styled';
|
||||
|
||||
export enum SideMenuOptionIconDescriptionType {
|
||||
Icon = 'icon',
|
||||
Ascii = 'ascii',
|
||||
Circle = 'circle',
|
||||
CustomComponent = 'custom-component',
|
||||
}
|
||||
|
||||
export type SideMenuOption = {
|
||||
text: string;
|
||||
subtext?: string;
|
||||
textClass?: 'info' | 'danger' | 'warning';
|
||||
key?: string;
|
||||
iconDesc?: {
|
||||
type: string;
|
||||
type: SideMenuOptionIconDescriptionType;
|
||||
side?: 'left' | 'right';
|
||||
name?: string;
|
||||
value?: string;
|
||||
value?: string | ReactElement;
|
||||
backgroundColor?: string;
|
||||
borderColor?: string;
|
||||
size?: number;
|
||||
|
||||
@@ -20,6 +20,7 @@ import React, {
|
||||
import { FlatList, ListRenderItem } from 'react-native';
|
||||
import { SideMenuCell } from './SideMenuCell';
|
||||
import { EmptyPlaceholder } from './TagSelectionList.styled';
|
||||
import {SideMenuOptionIconDescriptionType} from "@Screens/SideMenu/SideMenuSection";
|
||||
|
||||
type Props = {
|
||||
contentType: ContentType.Tag | ContentType.SmartTag;
|
||||
@@ -158,7 +159,7 @@ export const TagSelectionList = React.memo(
|
||||
text={title}
|
||||
iconDesc={{
|
||||
side: 'left',
|
||||
type: 'ascii',
|
||||
type: SideMenuOptionIconDescriptionType.Ascii,
|
||||
value: '#',
|
||||
}}
|
||||
key={item.uuid}
|
||||
|
||||
98
yarn.lock
98
yarn.lock
@@ -2526,39 +2526,47 @@
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@standardnotes/auth@^3.16.1":
|
||||
version "3.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.16.1.tgz#c73b3c70dfde1905998a6a27d91241ff5c7926dc"
|
||||
integrity sha512-MSYfb80AVeERrZPiN15XG9e/ECv6UrVJ0R5h9jVIEJzxPKfmpVJVr2wH2ts3B6etjD3VYtUS7UNBXC/fYI4kuw==
|
||||
"@standardnotes/auth@^3.16.4":
|
||||
version "3.16.4"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.16.4.tgz#6293bd67cdc4055229f1d520b6f44b39c6053a7a"
|
||||
integrity sha512-2tHsDnwQgGD3pOzKuSjo4yj8hLjATb70jzFnEWoEpyCdHTuGys9qSBElfi672hU4vg+/nXaHpdVUuD5DPzLaXg==
|
||||
dependencies:
|
||||
"@standardnotes/common" "^1.11.0"
|
||||
"@standardnotes/common" "^1.14.0"
|
||||
jsonwebtoken "^8.5.1"
|
||||
|
||||
"@standardnotes/common@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.11.0.tgz#5ed4be817a1f448e6eeb700d141dbfd40193aabe"
|
||||
integrity sha512-8TKx7bCwIazhGD3wkWTV4rmwbERsyisPbVDn6UIm1lNktWjKDF5OL1D8omalpR5wdM5qmXX5njI1zll2cxlW7A==
|
||||
"@standardnotes/common@^1.14.0":
|
||||
version "1.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.14.0.tgz#c3b8e06fb8120524da8d135f2dc594998e5a5c3a"
|
||||
integrity sha512-QA8JhWty7y/N62jdLyWuXzsVvDDeFwcQAH/DB/g5Mmaqlz9VlKcsbRmVl4wHLG3ur6n5Qj68aOhzCQd0p7f/WA==
|
||||
|
||||
"@standardnotes/components@^1.7.3":
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/components/-/components-1.7.3.tgz#544521a5f0c5955c384fa7878003f4fdfcda860c"
|
||||
integrity sha512-2AcxDdSFMK0G7h6R+EdMR2muzmFyqm0Qtl9znjQ3Bec4wGo9gpLpxlvotbihh230H+PTq4DJybX586bEs6HUKg==
|
||||
"@standardnotes/components@^1.7.5":
|
||||
version "1.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/components/-/components-1.7.5.tgz#2f199350779a8f99e9e536223d318b822bdf369f"
|
||||
integrity sha512-7nyrrcgPAkCf6pjbAIO8qOM+22KQU0jMCIDX3b4GAS1jXM7DJDy5Frnk3oMHd9NeFhnbf0TQH4Nz4uEeid6/HA==
|
||||
|
||||
"@standardnotes/domain-events@^2.23.7":
|
||||
version "2.23.7"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.23.7.tgz#ea8f4d38929249234125475c0a59deb6eb384b72"
|
||||
integrity sha512-sRht/c0KXv0NwoIalWs+2ugQz1C0JfCRIs/5FAeRCWCbn/OgHytCqPuD73/hGgUU/J323tYwB5+/7gYan4EWJg==
|
||||
"@standardnotes/domain-events@^2.23.14":
|
||||
version "2.23.14"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.23.14.tgz#733e340c6d42935c90858380d7721150d1804574"
|
||||
integrity sha512-DRPD0lGdVn/tbVyl97QGyyAcdUZd4qsETICCO882mG33HBN8Yc7st176U+izu3T5W3dlnTqE+MJUASj3UxVCvw==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.16.1"
|
||||
"@standardnotes/features" "^1.32.6"
|
||||
"@standardnotes/auth" "^3.16.4"
|
||||
"@standardnotes/features" "^1.32.11"
|
||||
|
||||
"@standardnotes/features@^1.32.6":
|
||||
version "1.32.6"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.32.6.tgz#63856e4f4af1a56aa6533a5e214a43c4b2d8b510"
|
||||
integrity sha512-GQJfDzYAAlRSccPWgAAT95hmUqmo9Z/m52uhOvgOwdQgwxOkHnYh60cdM/lnYrTBk0wv9MYPTFd+3vbMXy9/YQ==
|
||||
"@standardnotes/features@^1.32.11":
|
||||
version "1.32.11"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.32.11.tgz#43417e541bdf0ce8a10dfd68e8073fc1338f1888"
|
||||
integrity sha512-ZheQibMz4t2A6LKWcTDDGc5760AnPLFnHFwsSp0O8YydI3yz+qjm3pFF8XNeAEwgSvOX1W1nlX3E/X5tCp5LgQ==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.16.1"
|
||||
"@standardnotes/common" "^1.11.0"
|
||||
"@standardnotes/auth" "^3.16.4"
|
||||
"@standardnotes/common" "^1.14.0"
|
||||
|
||||
"@standardnotes/services@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.1.0.tgz#42f743f58fb4ab85627368ae6bcdf513079ef708"
|
||||
integrity sha512-r4lqUO30iHmjWodUTv+2D8xeCgpYFvJrNzR/pBIlZsAKMSjskxPyIUvBdQvHWs0o4vjf7ZedhpEwi68XwUqO3w==
|
||||
dependencies:
|
||||
"@standardnotes/common" "^1.14.0"
|
||||
"@standardnotes/utils" "^1.1.1"
|
||||
|
||||
"@standardnotes/settings@^1.11.3":
|
||||
version "1.11.3"
|
||||
@@ -2570,28 +2578,39 @@
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/sncrypto-common/-/sncrypto-common-1.7.0.tgz#6ad96afeaa031c26e45cbaf527bb511b803f998d"
|
||||
integrity sha512-Dke13reJMLQFXa7y9EqZYEeZG5Ouy+32qWEsQISLjLRPrTuNwyNXee2mdPh6c9uNZxOQwrdHxVGfqzJ2iy3RpQ==
|
||||
|
||||
"@standardnotes/snjs@2.61.0":
|
||||
version "2.61.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.61.0.tgz#0628fdb17c97f810fda0a989557184e394e4e555"
|
||||
integrity sha512-2j/WfHPMRxwD6K857HHCWZbmK2zL4nZc7a+jY0rgfZd4yzU/wFwy3iUxV//a/IDQ9XUKAFQnyVjGnMS/oaWzsA==
|
||||
"@standardnotes/snjs@2.63.1":
|
||||
version "2.63.1"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.63.1.tgz#0759be39e77304fcca11ea902aa9be7bb737e756"
|
||||
integrity sha512-d32CE7/yS+qEGlOfHTDc0NPzCFXPaK2zxlCi/j68R9lT/3LuV/uc1o9eNK9a5Fgcbtbk55vbW0rUYjQQVwUF8A==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.16.1"
|
||||
"@standardnotes/common" "^1.11.0"
|
||||
"@standardnotes/domain-events" "^2.23.7"
|
||||
"@standardnotes/features" "^1.32.6"
|
||||
"@standardnotes/auth" "^3.16.4"
|
||||
"@standardnotes/common" "^1.14.0"
|
||||
"@standardnotes/domain-events" "^2.23.14"
|
||||
"@standardnotes/features" "^1.32.11"
|
||||
"@standardnotes/services" "^1.1.0"
|
||||
"@standardnotes/settings" "^1.11.3"
|
||||
"@standardnotes/sncrypto-common" "^1.7.0"
|
||||
"@standardnotes/utils" "^1.1.1"
|
||||
|
||||
"@standardnotes/stylekit@5.5.0":
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.5.0.tgz#24d2aef1529bbb09406f95acdd55ba138cf14958"
|
||||
integrity sha512-feAdjOu0tBfpRJh5pH+urji40Xu72tJ2VRWxzkxXX1SYHMbgXnjBNs5R4IWko2kMIQwNGuBOvEg87S5w2/fdhw==
|
||||
"@standardnotes/stylekit@5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.9.0.tgz#16d62623335091062238c850a930dedaa5ad6f1d"
|
||||
integrity sha512-lIlEKwxKkQT+AttSmN40zvzWzfqpfI4VPTscZvRnWRqMQjYDEnSbcA1h0UDcT5tvnrna4RtsOiUL9o0KDx56sg==
|
||||
dependencies:
|
||||
"@reach/listbox" "^0.15.0"
|
||||
"@reach/menu-button" "^0.15.1"
|
||||
"@svgr/webpack" "^6.2.1"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@standardnotes/utils@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/utils/-/utils-1.1.1.tgz#a936edd328b4e10b43b11ffc8b1626a499fa6659"
|
||||
integrity sha512-LaB1Y4arvwuABT0fybJ9At6pPEAwsDooaldYPuvqyfQAWdeqRCBMHxRDCX6yunrrwBwk7UoTie9MRw6DF1igwg==
|
||||
dependencies:
|
||||
"@standardnotes/common" "^1.14.0"
|
||||
dompurify "^2.3.4"
|
||||
lodash "^4.17.19"
|
||||
|
||||
"@svgr/babel-plugin-add-jsx-attribute@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz#bd6d1ff32a31b82b601e73672a789cc41e84fe18"
|
||||
@@ -4459,6 +4478,11 @@ domhandler@^4.2.0, domhandler@^4.3.0:
|
||||
dependencies:
|
||||
domelementtype "^2.2.0"
|
||||
|
||||
dompurify@^2.3.4:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.6.tgz#2e019d7d7617aacac07cbbe3d88ae3ad354cf875"
|
||||
integrity sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==
|
||||
|
||||
domutils@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
|
||||
|
||||
Reference in New Issue
Block a user