mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-18 20:49:36 +01:00
feat: new announcements and dialog
This commit is contained in:
70
apps/mobile/src/components/Announcements/announcement.js
Normal file
70
apps/mobile/src/components/Announcements/announcement.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {FlatList, View} from 'react-native';
|
||||||
|
import {useTracked} from '../../provider';
|
||||||
|
import {useMessageStore, useSelectionStore} from '../../provider/stores';
|
||||||
|
import {Button} from '../Button';
|
||||||
|
import {allowedOnPlatform, renderItem} from './functions';
|
||||||
|
|
||||||
|
export const Announcement = ({color}) => {
|
||||||
|
const [state] = useTracked();
|
||||||
|
const colors = state.colors;
|
||||||
|
const announcements = useMessageStore(state => state.announcements);
|
||||||
|
const remove = useMessageStore(state => state.remove);
|
||||||
|
let announcement = announcements.length > 0 ? announcements[0] : null;
|
||||||
|
const selectionMode = useSelectionStore(state => state.selectionMode);
|
||||||
|
|
||||||
|
return !announcement || selectionMode ? null : (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg,
|
||||||
|
width: '100%',
|
||||||
|
paddingHorizontal: 12
|
||||||
|
}}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingVertical: 12,
|
||||||
|
width: '100%',
|
||||||
|
borderRadius: 10,
|
||||||
|
overflow: 'hidden'
|
||||||
|
}}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
}}>
|
||||||
|
<View />
|
||||||
|
<Button
|
||||||
|
fontSize={12}
|
||||||
|
type="errorShade"
|
||||||
|
icon="close"
|
||||||
|
height={null}
|
||||||
|
onPress={() => {
|
||||||
|
remove(announcement.id);
|
||||||
|
}}
|
||||||
|
iconSize={20}
|
||||||
|
style={{
|
||||||
|
borderRadius: 100,
|
||||||
|
paddingHorizontal: 0,
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View>
|
||||||
|
<FlatList
|
||||||
|
style={{
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
data={announcement?.body.filter(item =>
|
||||||
|
allowedOnPlatform(item.platform)
|
||||||
|
)}
|
||||||
|
renderItem={({item, index}) =>
|
||||||
|
renderItem({item: item, index: index, color: colors[color]})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,28 +1,86 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { useTracked } from '../../provider';
|
import { useTracked } from '../../provider';
|
||||||
|
import { eSendEvent, presentSheet } from '../../services/EventManager';
|
||||||
|
import { eCloseAnnouncementDialog, eOpenPremiumDialog } from '../../utils/Events';
|
||||||
|
import { openLinkInBrowser } from '../../utils/functions';
|
||||||
|
import { SIZE } from '../../utils/SizeUtils';
|
||||||
|
import { sleep } from '../../utils/TimeUtils';
|
||||||
|
import { SettingsBackupAndRestore } from '../../views/Settings';
|
||||||
import { Button } from '../Button';
|
import { Button } from '../Button';
|
||||||
import { getStyle } from './functions';
|
import { allowedOnPlatform, getStyle } from './functions';
|
||||||
|
|
||||||
export const Cta = ({actions, style = {}}) => {
|
export const Cta = ({actions, style = {}, color}) => {
|
||||||
const [state] = useTracked();
|
const [state] = useTracked();
|
||||||
const colors = state.colors;
|
const colors = state.colors;
|
||||||
|
let buttons =
|
||||||
|
actions.filter(item =>
|
||||||
|
allowedOnPlatform(item.platforms)
|
||||||
|
) || [];
|
||||||
|
|
||||||
|
const onPress = async item => {
|
||||||
|
eSendEvent(eCloseAnnouncementDialog);
|
||||||
|
await sleep(500);
|
||||||
|
if (item.type === 'link') {
|
||||||
|
try {
|
||||||
|
await openLinkInBrowser(item.data, colors);
|
||||||
|
} catch (e) {}
|
||||||
|
} else if (item.type === 'promo') {
|
||||||
|
eSendEvent(eOpenPremiumDialog, {
|
||||||
|
promoCode: item.data,
|
||||||
|
text: item.title
|
||||||
|
});
|
||||||
|
} else if (item.type === 'backup') {
|
||||||
|
presentSheet({
|
||||||
|
title: 'Backup & restore',
|
||||||
|
paragraph: 'Please enable automatic backups to keep your data safe',
|
||||||
|
noProgress: true,
|
||||||
|
noIcon: true,
|
||||||
|
component: <SettingsBackupAndRestore isSheet={true} />
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
paddingTop: 12,
|
|
||||||
backgroundColor: colors.bg,
|
|
||||||
...getStyle(style)
|
...getStyle(style)
|
||||||
}}>
|
}}>
|
||||||
{actions.map((item, index) => (
|
{buttons.length > 0 &&
|
||||||
|
buttons.slice(0, 1).map(item => (
|
||||||
<Button
|
<Button
|
||||||
type={index === 0 ? 'accent' : 'grayBg'}
|
key={item.title}
|
||||||
title={item.text}
|
title={item.title}
|
||||||
width="100%"
|
fontSize={SIZE.md}
|
||||||
onPress={() => {}}
|
buttonType={{
|
||||||
|
color: color ? color : colors.accent,
|
||||||
|
text: colors.light,
|
||||||
|
selected: color ? color : colors.accent,
|
||||||
|
opacity: 1
|
||||||
|
}}
|
||||||
|
onPress={() => onPress(item)}
|
||||||
|
width={'100%'}
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 10
|
marginBottom: 5
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{buttons.length > 1 &&
|
||||||
|
buttons.slice(1, 2).map((item, index) => (
|
||||||
|
<Button
|
||||||
|
key={item.title}
|
||||||
|
title={item.title}
|
||||||
|
fontSize={SIZE.xs + 1}
|
||||||
|
type="gray"
|
||||||
|
onPress={() => onPress(item)}
|
||||||
|
width={null}
|
||||||
|
height={20}
|
||||||
|
style={{
|
||||||
|
minWidth: '50%'
|
||||||
|
}}
|
||||||
|
textStyle={{
|
||||||
|
textDecorationLine: 'underline'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {View} from 'react-native';
|
||||||
|
import {allowedPlatforms} from '../../provider/stores';
|
||||||
|
import {ProFeatures} from '../ResultDialog/pro-features';
|
||||||
|
import {Body} from './body';
|
||||||
|
import {Cta} from './cta';
|
||||||
|
import {Description} from './description';
|
||||||
|
import {List} from './list';
|
||||||
|
import {Photo} from './photo';
|
||||||
|
import {SubHeading} from './subheading';
|
||||||
|
import {Title} from './title';
|
||||||
|
|
||||||
|
export function allowedOnPlatform(platforms) {
|
||||||
|
return platforms.some(platform => allowedPlatforms.indexOf(platform) > -1);
|
||||||
|
}
|
||||||
|
|
||||||
export const margins = {
|
export const margins = {
|
||||||
0: 0,
|
0: 0,
|
||||||
1: 12,
|
1: 12,
|
||||||
@@ -12,3 +28,34 @@ export const getStyle = style => {
|
|||||||
textAlign: style.textAlign || 'left'
|
textAlign: style.textAlign || 'left'
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Features = () => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%'
|
||||||
|
}}>
|
||||||
|
<ProFeatures />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderItems = {
|
||||||
|
title: Title,
|
||||||
|
description: Description,
|
||||||
|
body: Body,
|
||||||
|
cta: Cta,
|
||||||
|
image: Photo,
|
||||||
|
list: List,
|
||||||
|
subheading: SubHeading,
|
||||||
|
features: Features,
|
||||||
|
callToActions: Cta
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderItem = ({item, index, color}) => {
|
||||||
|
const Item = renderItems[item.type];
|
||||||
|
|
||||||
|
return <Item {...item} index={index} color={color} />;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,112 +1,21 @@
|
|||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import {FlatList, View} from 'react-native';
|
import {FlatList, View} from 'react-native';
|
||||||
import {useTracked} from '../../provider';
|
import {useTracked} from '../../provider';
|
||||||
|
import {useMessageStore} from '../../provider/stores';
|
||||||
import {eSubscribeEvent, eUnSubscribeEvent} from '../../services/EventManager';
|
import {eSubscribeEvent, eUnSubscribeEvent} from '../../services/EventManager';
|
||||||
import {
|
import {
|
||||||
eCloseAnnouncementDialog,
|
eCloseAnnouncementDialog,
|
||||||
eOpenAnnouncementDialog
|
eOpenAnnouncementDialog
|
||||||
} from '../../utils/Events';
|
} from '../../utils/Events';
|
||||||
import BaseDialog from '../Dialog/base-dialog';
|
import BaseDialog from '../Dialog/base-dialog';
|
||||||
import {Body} from './body';
|
import {renderItem} from "./functions";
|
||||||
import {Description} from './description';
|
|
||||||
import {Photo} from './photo';
|
|
||||||
import {SubHeading} from './subheading';
|
|
||||||
import {List} from './list';
|
|
||||||
import {Title} from './title';
|
|
||||||
import {Cta} from './cta';
|
|
||||||
import {allowedPlatforms} from '../../provider/stores';
|
|
||||||
import {ProFeatures} from '../ResultDialog/pro-features';
|
|
||||||
|
|
||||||
const announcement_dialog_info = {
|
export const AnnouncementDialog = () => {
|
||||||
body: [
|
|
||||||
{
|
|
||||||
type: 'image',
|
|
||||||
src: 'https://media.istockphoto.com/vectors/flash-sale-promotional-labels-templates-set-special-offer-text-design-vector-id1195558677?s=170667a',
|
|
||||||
caption: 'an image of a bear',
|
|
||||||
style: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'title',
|
|
||||||
text: "Don't miss out on this one!",
|
|
||||||
style: {
|
|
||||||
textAlign: 'center',
|
|
||||||
marginTop: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'description',
|
|
||||||
text: "It's 50% off on Notesnook Pro today. Grab the offer now before it's too late.",
|
|
||||||
style: {
|
|
||||||
textAlign: 'center',
|
|
||||||
marginBottom: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'features'
|
|
||||||
}
|
|
||||||
// {
|
|
||||||
// type: 'subheading',
|
|
||||||
// text: 'New image tool'
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// type: 'body',
|
|
||||||
// text: 'But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer.'
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// type: 'list',
|
|
||||||
// items: [
|
|
||||||
// {
|
|
||||||
// text: 'Lorem ipsum dolor sit amet'
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// text: 'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium'
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
],
|
|
||||||
callToActions: [
|
|
||||||
{
|
|
||||||
text: 'Get 50% Off for One Year',
|
|
||||||
action: 'none',
|
|
||||||
platforms: ['mobile']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const Features = () => {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
alignItems:'center',
|
|
||||||
width:'100%'
|
|
||||||
}}>
|
|
||||||
<ProFeatures />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderItems = {
|
|
||||||
title: Title,
|
|
||||||
description: Description,
|
|
||||||
body: Body,
|
|
||||||
cta: Cta,
|
|
||||||
image: Photo,
|
|
||||||
list: List,
|
|
||||||
subheading: SubHeading,
|
|
||||||
features: Features
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderItem = ({item, index}) => {
|
|
||||||
const Item = renderItems[item.type];
|
|
||||||
|
|
||||||
return <Item {...item} index={index} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Announcement = () => {
|
|
||||||
const [state] = useTracked();
|
const [state] = useTracked();
|
||||||
const colors = state.colors;
|
const colors = state.colors;
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [info, setInfo] = useState(null);
|
||||||
|
const remove = useMessageStore(state => state.remove);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eSubscribeEvent(eOpenAnnouncementDialog, open);
|
eSubscribeEvent(eOpenAnnouncementDialog, open);
|
||||||
@@ -117,11 +26,15 @@ export const Announcement = () => {
|
|||||||
};
|
};
|
||||||
}, [visible]);
|
}, [visible]);
|
||||||
|
|
||||||
const open = () => {
|
const open = data => {
|
||||||
|
setInfo(data);
|
||||||
|
console.log(info);
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
|
//remove(info.id);
|
||||||
|
setInfo(null);
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -130,6 +43,7 @@ export const Announcement = () => {
|
|||||||
animated={false}
|
animated={false}
|
||||||
centered={false}
|
centered={false}
|
||||||
bottom={true}
|
bottom={true}
|
||||||
|
onRequestClose={close}
|
||||||
visible={visible}>
|
visible={visible}>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
@@ -141,17 +55,10 @@ export const Announcement = () => {
|
|||||||
style={{
|
style={{
|
||||||
width: '100%'
|
width: '100%'
|
||||||
}}
|
}}
|
||||||
data={announcement_dialog_info.body}
|
data={info?.body}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Cta
|
|
||||||
actions={announcement_dialog_info.callToActions.filter(item =>
|
|
||||||
item.platforms.some(
|
|
||||||
platform => allowedPlatforms.indexOf(platform) > -1
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
height: 15
|
height: 15
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { ActionSheetComponent } from '../ActionSheetComponent';
|
|||||||
import ActionSheetWrapper from '../ActionSheetComponent/ActionSheetWrapper';
|
import ActionSheetWrapper from '../ActionSheetComponent/ActionSheetWrapper';
|
||||||
import { AddNotebookDialog } from '../AddNotebookDialog';
|
import { AddNotebookDialog } from '../AddNotebookDialog';
|
||||||
import { AddTopicDialog } from '../AddTopicDialog';
|
import { AddTopicDialog } from '../AddTopicDialog';
|
||||||
import { Announcement } from '../Announcements';
|
import { AnnouncementDialog } from '../Announcements';
|
||||||
import { AttachmentDialog } from '../AttachmentDialog';
|
import { AttachmentDialog } from '../AttachmentDialog';
|
||||||
import { Dialog } from '../Dialog';
|
import { Dialog } from '../Dialog';
|
||||||
import ExportDialog from '../ExportDialog';
|
import ExportDialog from '../ExportDialog';
|
||||||
@@ -302,7 +302,7 @@ export class DialogManager extends Component {
|
|||||||
<TagsDialog />
|
<TagsDialog />
|
||||||
<AttachmentDialog />
|
<AttachmentDialog />
|
||||||
<Expiring/>
|
<Expiring/>
|
||||||
<Announcement/>
|
<AnnouncementDialog/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
287
apps/mobile/src/components/Shapes/index.js
Normal file
287
apps/mobile/src/components/Shapes/index.js
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Dimensions, StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
|
const Circle = ({size, color, position}) => {
|
||||||
|
let style = {
|
||||||
|
wrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
...position
|
||||||
|
},
|
||||||
|
circle: {
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
borderRadius: size / 2,
|
||||||
|
backgroundColor: color
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<View style={style.wrapper}>
|
||||||
|
<View style={style.circle} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Donut = ({size, color, position}) => {
|
||||||
|
let style = {
|
||||||
|
wrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
...position
|
||||||
|
},
|
||||||
|
donut: {
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
borderRadius: size / 2,
|
||||||
|
borderWidth: size / 4,
|
||||||
|
borderColor: color
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<View style={style.wrapper}>
|
||||||
|
<View style={style.donut} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Triangle = ({size, color, position}) => {
|
||||||
|
let style = {
|
||||||
|
wrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
...position
|
||||||
|
},
|
||||||
|
triangle: {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderStyle: 'solid',
|
||||||
|
borderLeftWidth: size / 2,
|
||||||
|
borderRightWidth: size / 2,
|
||||||
|
borderBottomWidth: size,
|
||||||
|
borderLeftColor: 'transparent',
|
||||||
|
borderRightColor: 'transparent',
|
||||||
|
borderBottomColor: color,
|
||||||
|
transform: [{rotate: '180deg'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<View style={style.wrapper}>
|
||||||
|
<View style={style.triangle} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DiamondNarrow = ({size, color, position}) => {
|
||||||
|
let style = {
|
||||||
|
wrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
...position
|
||||||
|
},
|
||||||
|
diamondNarrow: {},
|
||||||
|
diamondNarrowTop: {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
borderTopWidth: 0,
|
||||||
|
borderTopColor: 'transparent',
|
||||||
|
borderLeftColor: 'transparent',
|
||||||
|
borderLeftWidth: size / 2,
|
||||||
|
borderRightColor: 'transparent',
|
||||||
|
borderRightWidth: size / 2,
|
||||||
|
borderBottomColor: color,
|
||||||
|
borderBottomWidth: size / 1.42
|
||||||
|
},
|
||||||
|
diamondNarrowBottom: {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
borderTopWidth: size / 1.42,
|
||||||
|
borderTopColor: color,
|
||||||
|
borderLeftColor: 'transparent',
|
||||||
|
borderLeftWidth: size / 2,
|
||||||
|
borderRightColor: 'transparent',
|
||||||
|
borderRightWidth: size / 2,
|
||||||
|
borderBottomColor: 'transparent',
|
||||||
|
borderBottomWidth: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<View style={style.wrapper}>
|
||||||
|
<View style={style.diamondNarrow}>
|
||||||
|
<View style={style.diamondNarrowTop} />
|
||||||
|
<View style={style.diamondNarrowBottom} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CutDiamond = ({size, color, position}) => {
|
||||||
|
let style = {
|
||||||
|
wrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
...position
|
||||||
|
},
|
||||||
|
cutDiamond: {},
|
||||||
|
cutDiamondTop: {
|
||||||
|
width: size,
|
||||||
|
height: 0,
|
||||||
|
borderTopWidth: 0,
|
||||||
|
borderTopColor: 'transparent',
|
||||||
|
borderLeftColor: 'transparent',
|
||||||
|
borderLeftWidth: size / 4,
|
||||||
|
borderRightColor: 'transparent',
|
||||||
|
borderRightWidth: size / 4,
|
||||||
|
borderBottomColor: color,
|
||||||
|
borderBottomWidth: size / 4
|
||||||
|
},
|
||||||
|
cutDiamondBottom: {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
borderTopWidth: size / 1.42,
|
||||||
|
borderTopColor: color,
|
||||||
|
borderLeftColor: 'transparent',
|
||||||
|
borderLeftWidth: size / 2,
|
||||||
|
borderRightColor: 'transparent',
|
||||||
|
borderRightWidth: size / 2,
|
||||||
|
borderBottomColor: 'transparent',
|
||||||
|
borderBottomWidth: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<View style={style.wrapper}>
|
||||||
|
<View style={style.cutDiamond}>
|
||||||
|
<View style={style.cutDiamondTop} />
|
||||||
|
<View style={style.cutDiamondBottom} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const Shapes = ({
|
||||||
|
primaryColor,
|
||||||
|
secondaryColor,
|
||||||
|
height,
|
||||||
|
figures,
|
||||||
|
borderRadius,
|
||||||
|
style
|
||||||
|
}) => {
|
||||||
|
const config = {
|
||||||
|
primaryColor: primaryColor || '#416DF8',
|
||||||
|
secondaryColor: secondaryColor || '#2F53D5',
|
||||||
|
height: Dimensions.get('window').height / (height || 3.5),
|
||||||
|
sizefigure: 100,
|
||||||
|
figures: figures || [
|
||||||
|
{name: 'circle', position: 'center', size: 60},
|
||||||
|
{name: 'donut', position: 'flex-start', axis: 'top', size: 80},
|
||||||
|
{name: 'circle', position: 'center', axis: 'right', size: 100}
|
||||||
|
],
|
||||||
|
borderRadius: borderRadius !== undefined ? borderRadius : 30
|
||||||
|
};
|
||||||
|
|
||||||
|
const arrFigures = [];
|
||||||
|
const buildFigures = () => {
|
||||||
|
config.figures.forEach((e, i) => {
|
||||||
|
let position = {
|
||||||
|
alignItems: e.position
|
||||||
|
};
|
||||||
|
|
||||||
|
const sizefigure = e.size || config.sizefigure;
|
||||||
|
|
||||||
|
switch (e.axis) {
|
||||||
|
case 'left':
|
||||||
|
position.left = -sizefigure / 2;
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
position.right = -sizefigure / 2;
|
||||||
|
break;
|
||||||
|
case 'top':
|
||||||
|
position.top = -sizefigure / 2;
|
||||||
|
break;
|
||||||
|
case 'bottom':
|
||||||
|
position.bottom = -sizefigure / 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.name === 'circle') {
|
||||||
|
arrFigures.push(
|
||||||
|
<Circle
|
||||||
|
key={i}
|
||||||
|
size={sizefigure}
|
||||||
|
color={config.secondaryColor}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (e.name === 'donut') {
|
||||||
|
arrFigures.push(
|
||||||
|
<Donut
|
||||||
|
key={i}
|
||||||
|
size={sizefigure}
|
||||||
|
color={config.secondaryColor}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (e.name === 'triangle') {
|
||||||
|
arrFigures.push(
|
||||||
|
<Triangle
|
||||||
|
key={i}
|
||||||
|
size={sizefigure}
|
||||||
|
color={config.secondaryColor}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (e.name === 'diamondNarrow') {
|
||||||
|
arrFigures.push(
|
||||||
|
<DiamondNarrow
|
||||||
|
key={i}
|
||||||
|
size={sizefigure}
|
||||||
|
color={config.secondaryColor}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (e.name === 'cutDiamond') {
|
||||||
|
arrFigures.push(
|
||||||
|
<CutDiamond
|
||||||
|
key={i}
|
||||||
|
size={sizefigure}
|
||||||
|
color={config.secondaryColor}
|
||||||
|
position={position}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return arrFigures;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
...styles.wrapper,
|
||||||
|
backgroundColor: config.primaryColor,
|
||||||
|
height: config.height,
|
||||||
|
borderBottomLeftRadius: config.borderRadius,
|
||||||
|
borderBottomRightRadius: config.borderRadius,
|
||||||
|
...style
|
||||||
|
}}>
|
||||||
|
<>{buildFigures()}</>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
wrapper: {
|
||||||
|
position: 'absolute',
|
||||||
|
height: '100%',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export { Shapes };
|
||||||
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { View } from 'react-native';
|
|
||||||
import { useTracked } from '../../provider';
|
|
||||||
import {
|
|
||||||
allowedPlatforms,
|
|
||||||
useMessageStore,
|
|
||||||
useSelectionStore
|
|
||||||
} from '../../provider/stores';
|
|
||||||
import { eSendEvent, presentSheet } from '../../services/EventManager';
|
|
||||||
import { hexToRGBA, RGB_Linear_Shade } from '../../utils/ColorUtils';
|
|
||||||
import { eOpenPremiumDialog } from '../../utils/Events';
|
|
||||||
import { openLinkInBrowser } from '../../utils/functions';
|
|
||||||
import { SIZE } from '../../utils/SizeUtils';
|
|
||||||
import { SettingsBackupAndRestore } from '../../views/Settings';
|
|
||||||
import { Button } from '../Button';
|
|
||||||
import Seperator from '../Seperator';
|
|
||||||
import Heading from '../Typography/Heading';
|
|
||||||
import Paragraph from '../Typography/Paragraph';
|
|
||||||
|
|
||||||
export const Announcement = ({color}) => {
|
|
||||||
const [state] = useTracked();
|
|
||||||
const colors = state.colors;
|
|
||||||
const announcements = useMessageStore(state => state.announcements);
|
|
||||||
const remove = useMessageStore(state => state.remove);
|
|
||||||
let announcement = announcements.length > 0 ? announcements[0] : null;
|
|
||||||
const selectionMode = useSelectionStore(state => state.selectionMode);
|
|
||||||
|
|
||||||
return !announcement || selectionMode ? null : (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
backgroundColor: colors.bg,
|
|
||||||
width: '100%'
|
|
||||||
}}>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingVertical: 12,
|
|
||||||
width: '100%'
|
|
||||||
}}>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between'
|
|
||||||
}}>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center'
|
|
||||||
}}>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
backgroundColor: colors.accent,
|
|
||||||
borderRadius: 100,
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginRight: 2.5
|
|
||||||
}}>
|
|
||||||
<Paragraph color={colors.light} size={SIZE.xs}>
|
|
||||||
{announcements.length}
|
|
||||||
</Paragraph>
|
|
||||||
</View>
|
|
||||||
<Button
|
|
||||||
title={'Announcement'}
|
|
||||||
fontSize={12}
|
|
||||||
height={null}
|
|
||||||
icon="bullhorn"
|
|
||||||
style={{
|
|
||||||
backgroundColor: color
|
|
||||||
? RGB_Linear_Shade(
|
|
||||||
colors.night ? 0.04 : -0.04,
|
|
||||||
hexToRGBA(color, 0.12)
|
|
||||||
)
|
|
||||||
: colors.shade
|
|
||||||
}}
|
|
||||||
buttonType={{text: color || colors.accent}}
|
|
||||||
style={{
|
|
||||||
paddingVertical: 4
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
title="Dismiss"
|
|
||||||
fontSize={12}
|
|
||||||
type="error"
|
|
||||||
height={null}
|
|
||||||
onPress={() => {
|
|
||||||
remove(announcement.id);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
paddingVertical: 4
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between'
|
|
||||||
}}>
|
|
||||||
{announcement?.title && (
|
|
||||||
<Heading
|
|
||||||
style={{
|
|
||||||
width: '100%'
|
|
||||||
}}
|
|
||||||
size={SIZE.lg}
|
|
||||||
color={colors.heading}>
|
|
||||||
{announcement.title}
|
|
||||||
</Heading>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{announcement?.description && (
|
|
||||||
<Paragraph color={colors.pri}>{announcement.description}</Paragraph>
|
|
||||||
)}
|
|
||||||
<Seperator />
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
flexWrap: 'wrap'
|
|
||||||
}}>
|
|
||||||
{announcement?.callToActions &&
|
|
||||||
announcement.callToActions.map((item, index) =>
|
|
||||||
item.platforms.some(
|
|
||||||
platform => allowedPlatforms.indexOf(platform) > -1
|
|
||||||
) ? (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
key={item.title}
|
|
||||||
title={item.title}
|
|
||||||
fontSize={SIZE.md}
|
|
||||||
buttonType={{
|
|
||||||
color:
|
|
||||||
index === 0
|
|
||||||
? color
|
|
||||||
? color
|
|
||||||
: colors.accent
|
|
||||||
: color
|
|
||||||
? RGB_Linear_Shade(
|
|
||||||
colors.night ? 0.04 : -0.04,
|
|
||||||
hexToRGBA(color, 0.12)
|
|
||||||
)
|
|
||||||
: colors.shade,
|
|
||||||
text:
|
|
||||||
index !== 0
|
|
||||||
? color
|
|
||||||
? color
|
|
||||||
: colors.accent
|
|
||||||
: colors.light,
|
|
||||||
selected: color
|
|
||||||
? color
|
|
||||||
: index === 0
|
|
||||||
? colors.accent
|
|
||||||
: colors.shade,
|
|
||||||
opacity: 1
|
|
||||||
}}
|
|
||||||
onPress={async () => {
|
|
||||||
if (item.type === 'link') {
|
|
||||||
try {
|
|
||||||
await openLinkInBrowser(item.data, state.colors);
|
|
||||||
} catch (e) {}
|
|
||||||
} else if (item.type === 'promo') {
|
|
||||||
eSendEvent(eOpenPremiumDialog, {
|
|
||||||
promoCode: item.data,
|
|
||||||
text: item.title
|
|
||||||
});
|
|
||||||
} else if (item.type === 'backup') {
|
|
||||||
presentSheet({
|
|
||||||
title: 'Backup & restore',
|
|
||||||
paragraph:
|
|
||||||
'Please enable automatic backups to keep your data safe',
|
|
||||||
noProgress: true,
|
|
||||||
noIcon: true,
|
|
||||||
component: <SettingsBackupAndRestore isSheet={true} />
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
width={'100%'}
|
|
||||||
style={{
|
|
||||||
marginBottom: 10
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : null
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import {View} from 'react-native';
|
import {View} from 'react-native';
|
||||||
import {useTracked} from '../../provider';
|
import {useTracked} from '../../provider';
|
||||||
import {useMessageStore} from '../../provider/stores';
|
import {useMessageStore} from '../../provider/stores';
|
||||||
@@ -8,8 +8,10 @@ import {normalize, SIZE} from '../../utils/SizeUtils';
|
|||||||
import {Button} from '../Button';
|
import {Button} from '../Button';
|
||||||
import {Placeholder} from '../ListPlaceholders';
|
import {Placeholder} from '../ListPlaceholders';
|
||||||
import Heading from '../Typography/Heading';
|
import Heading from '../Typography/Heading';
|
||||||
import {Announcement} from './announcement';
|
import {Announcement} from '../Announcements/announcement';
|
||||||
import {Card} from './card';
|
import {Card} from './card';
|
||||||
|
import { eSendEvent } from '../../services/EventManager';
|
||||||
|
import { eOpenAnnouncementDialog } from '../../utils/Events';
|
||||||
|
|
||||||
export const Header = React.memo(
|
export const Header = React.memo(
|
||||||
({
|
({
|
||||||
@@ -28,8 +30,14 @@ export const Header = React.memo(
|
|||||||
const [state] = useTracked();
|
const [state] = useTracked();
|
||||||
const {colors} = state;
|
const {colors} = state;
|
||||||
const announcements = useMessageStore(state => state.announcements);
|
const announcements = useMessageStore(state => state.announcements);
|
||||||
|
const dialogs = useMessageStore(state => state.dialogs);
|
||||||
|
|
||||||
return announcements.length > 0 && !noAnnouncement ? (
|
useEffect(() => {
|
||||||
|
if (dialogs.length > 0) {
|
||||||
|
eSendEvent(eOpenAnnouncementDialog,dialogs[0]);
|
||||||
|
}
|
||||||
|
},[dialogs])
|
||||||
|
return announcements.length !== 0 && !noAnnouncement ? (
|
||||||
<Announcement color={color || colors.accent} />
|
<Announcement color={color || colors.accent} />
|
||||||
) : type === 'search' ? null : !shouldShow ? (
|
) : type === 'search' ? null : !shouldShow ? (
|
||||||
<View
|
<View
|
||||||
@@ -43,7 +51,6 @@ export const Header = React.memo(
|
|||||||
{messageCard && (
|
{messageCard && (
|
||||||
<Card color={COLORS_NOTE[color?.toLowerCase()] || colors.accent} />
|
<Card color={COLORS_NOTE[color?.toLowerCase()] || colors.accent} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -149,10 +149,25 @@ export type Action = {
|
|||||||
title: string
|
title: string
|
||||||
data: string
|
data: string
|
||||||
}
|
}
|
||||||
|
export type Style = {
|
||||||
|
marginTop?: number,
|
||||||
|
marginBottom?: number,
|
||||||
|
textAlign?: "center" | "left" | "right"
|
||||||
|
}
|
||||||
|
export type BodyItem = {
|
||||||
|
type: "image" | "title" | "description" | "body" | "list" | "features" | "poll" | "subheading" | "shapes"
|
||||||
|
src?: string
|
||||||
|
caption?: string
|
||||||
|
text?: string
|
||||||
|
style?: Style,
|
||||||
|
items?: Array<{
|
||||||
|
text?: string
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
export type Announcement = {
|
export type Announcement = {
|
||||||
title: string
|
type: "dialog" | "inline"
|
||||||
description: string
|
body: BodyItem[]
|
||||||
id: string
|
id: string
|
||||||
callToActions: Action[]
|
callToActions: Action[]
|
||||||
timestamp: number
|
timestamp: number
|
||||||
@@ -166,6 +181,7 @@ export interface MessageStore extends State {
|
|||||||
setMessage: (message: Message) => void
|
setMessage: (message: Message) => void
|
||||||
announcements: Announcement[],
|
announcements: Announcement[],
|
||||||
setAnnouncement: () => Promise<void>
|
setAnnouncement: () => Promise<void>
|
||||||
|
dialogs: Announcement[]
|
||||||
remove: (id: string) => void
|
remove: (id: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -332,6 +332,7 @@ export const useMessageStore = create<MessageStore>((set, get) => ({
|
|||||||
}
|
}
|
||||||
set({ announcements: copy });
|
set({ announcements: copy });
|
||||||
},
|
},
|
||||||
|
dialogs:[],
|
||||||
setAnnouncement: async function () {
|
setAnnouncement: async function () {
|
||||||
let announcements: Announcement[] = [];
|
let announcements: Announcement[] = [];
|
||||||
try {
|
try {
|
||||||
@@ -342,14 +343,15 @@ export const useMessageStore = create<MessageStore>((set, get) => ({
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
set({ announcements: [] })
|
set({ announcements: [] })
|
||||||
} finally {
|
} finally {
|
||||||
set({ announcements: await getFiltered(announcements) })
|
let all = await getFiltered(announcements);
|
||||||
|
set({ announcements: all.filter(a => a.type === "inline"),dialogs:all.filter(a => a.type === "dialog") })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const getFiltered = async (announcements) => {
|
const getFiltered = async (announcements:Announcement[]) => {
|
||||||
if (!announcements) return [];
|
if (!announcements) return [];
|
||||||
let filtered = [];
|
let filtered:Announcement[] = [];
|
||||||
for (var announcement of announcements) {
|
for (var announcement of announcements) {
|
||||||
if (await shouldShowAnnouncement(announcement)) {
|
if (await shouldShowAnnouncement(announcement)) {
|
||||||
filtered.push(announcement);
|
filtered.push(announcement);
|
||||||
|
|||||||
Reference in New Issue
Block a user