mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-21 22:19:41 +01:00
add walkthroughs for pro/trial users
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -22,7 +22,7 @@ export const ChangePassword = () => {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const changePassword = async () => {
|
const changePassword = async () => {
|
||||||
if (error || !oldPassword || !password) {
|
if (error || !oldPassword.current || !password.current) {
|
||||||
ToastEvent.show({
|
ToastEvent.show({
|
||||||
heading: 'All fields required',
|
heading: 'All fields required',
|
||||||
message: 'Fill all the fields and try again.',
|
message: 'Fill all the fields and try again.',
|
||||||
@@ -34,7 +34,7 @@ export const ChangePassword = () => {
|
|||||||
eSendEvent(eCloseProgressDialog);
|
eSendEvent(eCloseProgressDialog);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await db.user.changePassword(oldPassword, password);
|
await db.user.changePassword(oldPassword.current, password.current);
|
||||||
ToastEvent.show({
|
ToastEvent.show({
|
||||||
heading: `Account password updated`,
|
heading: `Account password updated`,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const ForgotPassword = () => {
|
|||||||
const [sent, setSent] = useState(false);
|
const [sent, setSent] = useState(false);
|
||||||
|
|
||||||
const sendRecoveryEmail = async () => {
|
const sendRecoveryEmail = async () => {
|
||||||
if (!email || error) {
|
if (!email.current || error) {
|
||||||
ToastEvent.show({
|
ToastEvent.show({
|
||||||
heading: 'Account email is required.',
|
heading: 'Account email is required.',
|
||||||
type: 'error',
|
type: 'error',
|
||||||
@@ -38,7 +38,7 @@ export const ForgotPassword = () => {
|
|||||||
if (lastRecoveryEmailTime && Date.now() - JSON.parse(lastRecoveryEmailTime) < 60000 * 3) {
|
if (lastRecoveryEmailTime && Date.now() - JSON.parse(lastRecoveryEmailTime) < 60000 * 3) {
|
||||||
throw new Error('Please wait before requesting another email');
|
throw new Error('Please wait before requesting another email');
|
||||||
}
|
}
|
||||||
await db.user.recoverAccount(email.toLowerCase());
|
await db.user.recoverAccount(email.current.toLowerCase());
|
||||||
await MMKV.setItem('lastRecoveryEmailTime', JSON.stringify(Date.now()));
|
await MMKV.setItem('lastRecoveryEmailTime', JSON.stringify(Date.now()));
|
||||||
ToastEvent.show({
|
ToastEvent.show({
|
||||||
heading: `Check your email to reset password`,
|
heading: `Check your email to reset password`,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { LayoutAnimation, Platform, View } from 'react-native';
|
import { Platform, View } from 'react-native';
|
||||||
import { SheetManager } from 'react-native-actions-sheet';
|
import { SheetManager } from 'react-native-actions-sheet';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { useTracked } from '../../provider';
|
import { useTracked } from '../../provider';
|
||||||
@@ -31,9 +31,7 @@ export const Login = ({ changeMode }) => {
|
|||||||
const emailInputRef = useRef();
|
const emailInputRef = useRef();
|
||||||
const passwordInputRef = useRef();
|
const passwordInputRef = useRef();
|
||||||
const password = useRef();
|
const password = useRef();
|
||||||
|
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
|
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import { eSendEvent, ToastEvent } from '../../services/EventManager';
|
|||||||
import { clearMessage, setEmailVerifyMessage } from '../../services/Message';
|
import { clearMessage, setEmailVerifyMessage } from '../../services/Message';
|
||||||
import PremiumService from '../../services/PremiumService';
|
import PremiumService from '../../services/PremiumService';
|
||||||
import { db } from '../../utils/database';
|
import { db } from '../../utils/database';
|
||||||
import { eCloseLoginDialog, eOpenResultDialog } from '../../utils/Events';
|
import { eCloseLoginDialog } from '../../utils/Events';
|
||||||
|
import { openLinkInBrowser } from '../../utils/functions';
|
||||||
import { SIZE } from '../../utils/SizeUtils';
|
import { SIZE } from '../../utils/SizeUtils';
|
||||||
import { sleep } from '../../utils/TimeUtils';
|
import { sleep } from '../../utils/TimeUtils';
|
||||||
import umami from '../../utils/umami';
|
import umami from '../../utils/umami';
|
||||||
@@ -58,7 +59,7 @@ export const Signup = ({ changeMode, welcome, trial }) => {
|
|||||||
if (!validateInfo() || error) return;
|
if (!validateInfo() || error) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await db.user.signup(email.toLowerCase(), password);
|
await db.user.signup(email.current.toLowerCase(), password.current);
|
||||||
let user = await db.user.getUser();
|
let user = await db.user.getUser();
|
||||||
setUser(user);
|
setUser(user);
|
||||||
setLastSynced(await db.lastSynced());
|
setLastSynced(await db.lastSynced());
|
||||||
@@ -214,7 +215,36 @@ export const Signup = ({ changeMode, welcome, trial }) => {
|
|||||||
marginBottom={5}
|
marginBottom={5}
|
||||||
/>
|
/>
|
||||||
<Paragraph size={SIZE.xs} color={colors.icon}>
|
<Paragraph size={SIZE.xs} color={colors.icon}>
|
||||||
By signing up, you agree to our terms of service and privacy policy.
|
By signing up, you agree to our{' '}
|
||||||
|
<Paragraph
|
||||||
|
size={SIZE.xs}
|
||||||
|
onPress={() => {
|
||||||
|
openLinkInBrowser('https://notesnook.com/tos', colors)
|
||||||
|
.catch(e => {})
|
||||||
|
.then(r => {});
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
textDecorationLine: 'underline'
|
||||||
|
}}
|
||||||
|
color={colors.accent}
|
||||||
|
>
|
||||||
|
terms of service{' '}
|
||||||
|
</Paragraph>
|
||||||
|
and{' '}
|
||||||
|
<Paragraph
|
||||||
|
size={SIZE.xs}
|
||||||
|
onPress={() => {
|
||||||
|
openLinkInBrowser('https://notesnook.com/privacy', colors)
|
||||||
|
.catch(e => {})
|
||||||
|
.then(r => {});
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
textDecorationLine: 'underline'
|
||||||
|
}}
|
||||||
|
color={colors.accent}
|
||||||
|
>
|
||||||
|
privacy policy.
|
||||||
|
</Paragraph>
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export const Placeholder = ({ type, w, h, color }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SvgToPngView = ({ width, height, src, color, img }) => {
|
export const SvgToPngView = ({ width = 250, height = 250, src }) => {
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { InteractionManager, View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
import ToggleSwitch from 'toggle-switch-react-native';
|
||||||
import { useTracked } from '../../provider';
|
import { useTracked } from '../../provider';
|
||||||
import { eSubscribeEvent, eUnSubscribeEvent } from '../../services/EventManager';
|
import { eSubscribeEvent, eUnSubscribeEvent } from '../../services/EventManager';
|
||||||
import Navigation from '../../services/Navigation';
|
import Navigation from '../../services/Navigation';
|
||||||
import { getElevation } from '../../utils';
|
|
||||||
import { normalize, SIZE } from '../../utils/SizeUtils';
|
import { normalize, SIZE } from '../../utils/SizeUtils';
|
||||||
import { Button } from '../Button';
|
import { Button } from '../Button';
|
||||||
import { PressableButton } from '../PressableButton';
|
import { PressableButton } from '../PressableButton';
|
||||||
import Heading from '../Typography/Heading';
|
import Heading from '../Typography/Heading';
|
||||||
import Paragraph from '../Typography/Paragraph';
|
import Paragraph from '../Typography/Paragraph';
|
||||||
import ToggleSwitch from 'toggle-switch-react-native';
|
|
||||||
|
|
||||||
export const MenuListItem = React.memo(
|
export const MenuListItem = React.memo(
|
||||||
({ item, index, noTextMode, testID, rightBtn }) => {
|
({ item, index, noTextMode, testID, rightBtn }) => {
|
||||||
|
|||||||
@@ -7,8 +7,15 @@ import { DDS } from '../../services/DeviceDetection';
|
|||||||
import { eSendEvent, presentSheet } from '../../services/EventManager';
|
import { eSendEvent, presentSheet } from '../../services/EventManager';
|
||||||
import PremiumService from '../../services/PremiumService';
|
import PremiumService from '../../services/PremiumService';
|
||||||
import { getElevation } from '../../utils';
|
import { getElevation } from '../../utils';
|
||||||
import { eOpenLoginDialog, eOpenResultDialog } from '../../utils/Events';
|
import { db } from '../../utils/database';
|
||||||
|
import {
|
||||||
|
eClosePremiumDialog,
|
||||||
|
eCloseProgressDialog,
|
||||||
|
eOpenLoginDialog,
|
||||||
|
eOpenResultDialog
|
||||||
|
} from '../../utils/Events';
|
||||||
import { SIZE } from '../../utils/SizeUtils';
|
import { SIZE } from '../../utils/SizeUtils';
|
||||||
|
import { sleep } from '../../utils/TimeUtils';
|
||||||
import umami from '../../utils/umami';
|
import umami from '../../utils/umami';
|
||||||
import { ActionIcon } from '../ActionIcon';
|
import { ActionIcon } from '../ActionIcon';
|
||||||
import { AuthMode } from '../Auth';
|
import { AuthMode } from '../Auth';
|
||||||
@@ -19,6 +26,7 @@ import Seperator from '../Seperator';
|
|||||||
import { Toast } from '../Toast';
|
import { Toast } from '../Toast';
|
||||||
import Heading from '../Typography/Heading';
|
import Heading from '../Typography/Heading';
|
||||||
import Paragraph from '../Typography/Paragraph';
|
import Paragraph from '../Typography/Paragraph';
|
||||||
|
import { Walkthrough } from '../Walkthrough';
|
||||||
import { features } from './features';
|
import { features } from './features';
|
||||||
import { Group } from './group';
|
import { Group } from './group';
|
||||||
import { PricingPlans } from './pricing-plans';
|
import { PricingPlans } from './pricing-plans';
|
||||||
@@ -146,8 +154,14 @@ export const Component = ({ close, promo, getRef }) => {
|
|||||||
{userCanRequestTrial ? (
|
{userCanRequestTrial ? (
|
||||||
<Button
|
<Button
|
||||||
key="calltoaction"
|
key="calltoaction"
|
||||||
onPress={() => {
|
onPress={async () => {
|
||||||
eSendEvent(eOpenResultDialog);
|
try {
|
||||||
|
await db.user.activateTrial();
|
||||||
|
eSendEvent(eClosePremiumDialog);
|
||||||
|
eSendEvent(eCloseProgressDialog);
|
||||||
|
await sleep(300);
|
||||||
|
Walkthrough.present('trialstarted', false, true);
|
||||||
|
} catch (e) {}
|
||||||
}}
|
}}
|
||||||
title="Try free for 14 days"
|
title="Try free for 14 days"
|
||||||
type="accent"
|
type="accent"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import BaseDialog from '../Dialog/base-dialog';
|
|||||||
import { presentDialog } from '../Dialog/functions';
|
import { presentDialog } from '../Dialog/functions';
|
||||||
import Heading from '../Typography/Heading';
|
import Heading from '../Typography/Heading';
|
||||||
import Paragraph from '../Typography/Paragraph';
|
import Paragraph from '../Typography/Paragraph';
|
||||||
|
import { Walkthrough } from '../Walkthrough';
|
||||||
import { PricingItem } from './pricing-item';
|
import { PricingItem } from './pricing-item';
|
||||||
|
|
||||||
const promoCyclesMonthly = {
|
const promoCyclesMonthly = {
|
||||||
@@ -195,12 +196,14 @@ export const PricingPlans = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onPress={() => {
|
onPress={async () => {
|
||||||
eSendEvent(eClosePremiumDialog);
|
try {
|
||||||
eSendEvent(eCloseProgressDialog);
|
await db.user.activateTrial();
|
||||||
setTimeout(() => {
|
eSendEvent(eClosePremiumDialog);
|
||||||
eSendEvent(eOpenLoginDialog, 1);
|
eSendEvent(eCloseProgressDialog);
|
||||||
}, 400);
|
await sleep(300);
|
||||||
|
Walkthrough.present('trialstarted', false, true);
|
||||||
|
} catch (e) {}
|
||||||
}}
|
}}
|
||||||
title={`Try free for 14 days`}
|
title={`Try free for 14 days`}
|
||||||
type="grayAccent"
|
type="grayAccent"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import Heading from '../Typography/Heading';
|
|||||||
import Paragraph from '../Typography/Paragraph';
|
import Paragraph from '../Typography/Paragraph';
|
||||||
import walkthroughs, { TStep } from './walkthroughs';
|
import walkthroughs, { TStep } from './walkthroughs';
|
||||||
|
|
||||||
export const Walkthrough = ({ steps }: { steps: TStep[] }) => {
|
export const Walkthrough = ({ steps, canSkip = true }: { steps: TStep[]; canSkip: boolean }) => {
|
||||||
const [state] = useTracked();
|
const [state] = useTracked();
|
||||||
const colors = state.colors;
|
const colors = state.colors;
|
||||||
const [step, setStep] = useState<TStep>(steps && steps[0]);
|
const [step, setStep] = useState<TStep>(steps && steps[0]);
|
||||||
@@ -33,17 +33,36 @@ export const Walkthrough = ({ steps }: { steps: TStep[] }) => {
|
|||||||
>
|
>
|
||||||
{step.walkthroughItem(colors)}
|
{step.walkthroughItem(colors)}
|
||||||
|
|
||||||
<Heading>{step.title}</Heading>
|
{step.title ? <Heading>{step.title}</Heading> : null}
|
||||||
<Paragraph
|
{step.text ? (
|
||||||
style={{
|
<Paragraph
|
||||||
textAlign: 'center',
|
style={{
|
||||||
alignSelf: 'center',
|
textAlign: 'center',
|
||||||
maxWidth: '80%'
|
alignSelf: 'center',
|
||||||
}}
|
maxWidth: '80%'
|
||||||
size={SIZE.md}
|
}}
|
||||||
>
|
size={SIZE.md}
|
||||||
{step.text}
|
>
|
||||||
</Paragraph>
|
{step.text}
|
||||||
|
</Paragraph>
|
||||||
|
) : null}
|
||||||
|
{step.actionButton && (
|
||||||
|
<Button
|
||||||
|
//@ts-ignore
|
||||||
|
style={{
|
||||||
|
height: 30,
|
||||||
|
marginTop: 10
|
||||||
|
}}
|
||||||
|
textStyle={{
|
||||||
|
textDecorationLine: 'underline'
|
||||||
|
}}
|
||||||
|
onPress={async () => {
|
||||||
|
step.actionButton?.action();
|
||||||
|
}}
|
||||||
|
type="transparent"
|
||||||
|
title={step.actionButton.text}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
@@ -69,43 +88,52 @@ export const Walkthrough = ({ steps }: { steps: TStep[] }) => {
|
|||||||
type="accent"
|
type="accent"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
{canSkip ? (
|
||||||
//@ts-ignore
|
<Button
|
||||||
style={{
|
//@ts-ignore
|
||||||
height: 30,
|
style={{
|
||||||
marginTop: 10
|
height: 30,
|
||||||
}}
|
marginTop: 10
|
||||||
textStyle={{
|
}}
|
||||||
textDecorationLine: 'underline'
|
textStyle={{
|
||||||
}}
|
textDecorationLine: 'underline'
|
||||||
onPress={async () => {
|
}}
|
||||||
eSendEvent(eCloseProgressDialog);
|
onPress={async () => {
|
||||||
}}
|
eSendEvent(eCloseProgressDialog);
|
||||||
type="gray"
|
}}
|
||||||
title="Skip introduction"
|
type="gray"
|
||||||
/>
|
title="Skip introduction"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Walkthrough.present = async (id: 'notebooks') => {
|
Walkthrough.present = async (
|
||||||
let walkthroughState = await MMKV.getItem('walkthroughState');
|
id: 'notebooks' | 'trialstarted' | 'emailconfirmed' | 'prouser',
|
||||||
if (walkthroughState) {
|
canSkip = true,
|
||||||
walkthroughState = JSON.parse(walkthroughState);
|
nopersist?: boolean
|
||||||
} else {
|
) => {
|
||||||
|
if (!nopersist) {
|
||||||
|
let walkthroughState = await MMKV.getItem('walkthroughState');
|
||||||
|
if (walkthroughState) {
|
||||||
|
walkthroughState = JSON.parse(walkthroughState);
|
||||||
|
} else {
|
||||||
|
//@ts-ignore
|
||||||
|
walkthroughState = {};
|
||||||
|
}
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
walkthroughState = {};
|
if (walkthroughState[id]) return;
|
||||||
|
//@ts-ignore
|
||||||
|
walkthroughState[id] = true;
|
||||||
|
MMKV.setItem('walkthroughState', JSON.stringify(walkthroughState));
|
||||||
}
|
}
|
||||||
//@ts-ignore
|
|
||||||
if (walkthroughState[id]) return;
|
|
||||||
//@ts-ignore
|
|
||||||
walkthroughState[id] = true;
|
|
||||||
MMKV.setItem('walkthroughState', JSON.stringify(walkthroughState));
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
let walkthrough = walkthroughs[id];
|
let walkthrough = walkthroughs[id];
|
||||||
if (!walkthrough) return;
|
if (!walkthrough) return;
|
||||||
presentSheet({
|
presentSheet({
|
||||||
component: <Walkthrough steps={walkthrough.steps} />,
|
component: <Walkthrough canSkip={canSkip} steps={walkthrough.steps} />,
|
||||||
disableClosing: true
|
disableClosing: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,25 +1,34 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { Linking, View } from 'react-native';
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
import { COMMUNITY_SVG, LAUNCH_ROCKET, SUPPORT_SVG, WELCOME_SVG } from '../../assets/images/assets';
|
||||||
import { TrackedState, useTracked } from '../../provider';
|
import { TrackedState, useTracked } from '../../provider';
|
||||||
import { eSendEvent } from '../../services/EventManager';
|
import { eSendEvent } from '../../services/EventManager';
|
||||||
import { getElevation } from '../../utils';
|
import { getElevation } from '../../utils';
|
||||||
import { eOpenAddNotebookDialog } from '../../utils/Events';
|
import { eOpenAddNotebookDialog } from '../../utils/Events';
|
||||||
import { SIZE } from '../../utils/SizeUtils';
|
import { SIZE } from '../../utils/SizeUtils';
|
||||||
import useRotator from '../../utils/use-rotator';
|
import useRotator from '../../utils/use-rotator';
|
||||||
|
import { AccentColorPicker } from '../../views/Settings/appearance';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
import { SvgToPngView } from '../ListPlaceholders';
|
||||||
import { PinItem } from '../Menu/TagsSection';
|
import { PinItem } from '../Menu/TagsSection';
|
||||||
|
import Seperator from '../Seperator';
|
||||||
import Heading from '../Typography/Heading';
|
import Heading from '../Typography/Heading';
|
||||||
import Paragraph from '../Typography/Paragraph';
|
import Paragraph from '../Typography/Paragraph';
|
||||||
|
|
||||||
export type TStep = {
|
export type TStep = {
|
||||||
text: string;
|
text?: string;
|
||||||
walkthroughItem: (colors: TrackedState['colors']) => React.ReactNode;
|
walkthroughItem: (colors: TrackedState['colors']) => React.ReactNode;
|
||||||
title: string;
|
title?: string;
|
||||||
button?: {
|
button?: {
|
||||||
type: 'next' | 'done';
|
type: 'next' | 'done';
|
||||||
title: string;
|
title: string;
|
||||||
action?: () => void;
|
action?: () => void;
|
||||||
};
|
};
|
||||||
|
actionButton?: {
|
||||||
|
text: string;
|
||||||
|
action: () => void;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotebookWelcome = () => {
|
const NotebookWelcome = () => {
|
||||||
@@ -237,4 +246,170 @@ const notebooks: { id: string; steps: TStep[] } = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
export default { notebooks };
|
const ChooseTheme = () => {
|
||||||
|
const [state] = useTracked();
|
||||||
|
const { colors } = state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
height: 100
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AccentColorPicker settings={false} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const trialstarted: { id: string; steps: TStep[] } = {
|
||||||
|
id: 'trialstarted',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
title: 'Your trial is activated',
|
||||||
|
text: 'You can use all permium features for free for the next 14 days',
|
||||||
|
walkthroughItem: colors => <SvgToPngView src={LAUNCH_ROCKET(colors.pri)} />,
|
||||||
|
button: {
|
||||||
|
type: 'next',
|
||||||
|
title: 'Next'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: 'Make yourself at home',
|
||||||
|
text: 'Pick a theme of your choice',
|
||||||
|
walkthroughItem: () => <ChooseTheme />,
|
||||||
|
button: {
|
||||||
|
type: 'next',
|
||||||
|
title: 'Next'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Join the cause',
|
||||||
|
text: 'Meet other privacy-minded people and talk to us directly about your concerns, issues and suggestions.',
|
||||||
|
walkthroughItem: colors => <SvgToPngView src={COMMUNITY_SVG(colors.pri)} />,
|
||||||
|
button: {
|
||||||
|
type: 'done',
|
||||||
|
title: 'Continue'
|
||||||
|
},
|
||||||
|
actionButton: {
|
||||||
|
text: 'Join Discord Community',
|
||||||
|
action: () => {
|
||||||
|
Linking.openURL('https://discord.gg/zQBK97EE22').catch(console.log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const emailconfirmed: { id: string; steps: TStep[] } = {
|
||||||
|
id: 'emailconfirmed',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
title: 'Email confirmed',
|
||||||
|
text: 'Your email was confirmed successfully. Thank you for choosing end-to-end encrypted note taking.',
|
||||||
|
walkthroughItem: colors => <SvgToPngView src={WELCOME_SVG(colors.pri)} />,
|
||||||
|
button: {
|
||||||
|
type: 'done',
|
||||||
|
title: 'Continue'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const Support = () => {
|
||||||
|
const [state] = useTracked();
|
||||||
|
const { colors } = state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SvgToPngView src={SUPPORT_SVG()} />
|
||||||
|
<Heading>Get Priority Support</Heading>
|
||||||
|
<Paragraph
|
||||||
|
style={{
|
||||||
|
textAlign: 'center'
|
||||||
|
}}
|
||||||
|
size={SIZE.md}
|
||||||
|
>
|
||||||
|
You can reach out to us via multiple channels if you face an issue or want to just talk.
|
||||||
|
</Paragraph>
|
||||||
|
<Seperator />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
marginBottom: 10,
|
||||||
|
width: '90%'
|
||||||
|
}}
|
||||||
|
onPress={() => {
|
||||||
|
Linking.openURL('https://discord.gg/zQBK97EE22').catch(console.log);
|
||||||
|
}}
|
||||||
|
icon="discord"
|
||||||
|
type="grayBg"
|
||||||
|
title="Join our community on Discord"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
marginBottom: 10,
|
||||||
|
width: '90%'
|
||||||
|
}}
|
||||||
|
onPress={() => {
|
||||||
|
Linking.openURL('https://t.me/notesnook').catch(console.log);
|
||||||
|
}}
|
||||||
|
icon="telegram"
|
||||||
|
type="grayBg"
|
||||||
|
title="Join our Telegram group"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
marginBottom: 10,
|
||||||
|
width: '90%'
|
||||||
|
}}
|
||||||
|
icon="bug"
|
||||||
|
type="grayBg"
|
||||||
|
title="Submit an issue from Settings"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
marginBottom: 10,
|
||||||
|
width: '90%'
|
||||||
|
}}
|
||||||
|
icon="mail"
|
||||||
|
type="grayBg"
|
||||||
|
title="Email us at support@streetwriters.co"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const prouser: { id: string; steps: TStep[] } = {
|
||||||
|
id: 'prouser',
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
title: 'Welcome to Notesnook Pro',
|
||||||
|
text: 'Thank you for reaffirming our idea that privacy comes first',
|
||||||
|
walkthroughItem: colors => <SvgToPngView src={LAUNCH_ROCKET(colors.pri)} />,
|
||||||
|
button: {
|
||||||
|
type: 'next',
|
||||||
|
title: 'Next'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
walkthroughItem: () => <Support />,
|
||||||
|
button: {
|
||||||
|
type: 'done',
|
||||||
|
title: 'Continue'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { notebooks, trialstarted, emailconfirmed, prouser };
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ import {
|
|||||||
} from '../utils/Events';
|
} from '../utils/Events';
|
||||||
import { editorRef, tabBarRef } from '../utils/Refs';
|
import { editorRef, tabBarRef } from '../utils/Refs';
|
||||||
import { sleep } from '../utils/TimeUtils';
|
import { sleep } from '../utils/TimeUtils';
|
||||||
|
import useTooltip, { hideAllTooltips } from '../utils/use-tooltip';
|
||||||
import { EditorWrapper } from '../views/Editor/EditorWrapper';
|
import { EditorWrapper } from '../views/Editor/EditorWrapper';
|
||||||
import { checkStatus, EditorWebView, getNote } from '../views/Editor/Functions';
|
import { checkStatus, EditorWebView, getNote } from '../views/Editor/Functions';
|
||||||
import tiny from '../views/Editor/tiny/tiny';
|
import tiny from '../views/Editor/tiny/tiny';
|
||||||
import { NavigatorStack } from './NavigatorStack';
|
import { NavigatorStack } from './NavigatorStack';
|
||||||
import useTooltip, { hideAllTooltips } from '../utils/use-tooltip';
|
|
||||||
|
|
||||||
let layoutTimer = null;
|
let layoutTimer = null;
|
||||||
|
|
||||||
|
|||||||
@@ -70,13 +70,13 @@ export function setLoginMessage() {
|
|||||||
const emailMessage = {
|
const emailMessage = {
|
||||||
visible: true,
|
visible: true,
|
||||||
message: 'Email not confirmed',
|
message: 'Email not confirmed',
|
||||||
actionText: 'Confirm now to get 7 more days of free trial',
|
actionText: 'Please confrim your email to sync notes.',
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
PremiumService.showVerifyEmailDialog();
|
PremiumService.showVerifyEmailDialog();
|
||||||
},
|
},
|
||||||
data: {},
|
data: {},
|
||||||
icon: 'email',
|
icon: 'email',
|
||||||
type: 'normal'
|
type: 'error'
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setEmailVerifyMessage() {
|
export function setEmailVerifyMessage() {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import * as RNIap from 'react-native-iap';
|
|||||||
import { enabled } from 'react-native-privacy-snapshot';
|
import { enabled } from 'react-native-privacy-snapshot';
|
||||||
import { doInBackground, editing } from '.';
|
import { doInBackground, editing } from '.';
|
||||||
import { ProFeatures } from '../components/ResultDialog/pro-features';
|
import { ProFeatures } from '../components/ResultDialog/pro-features';
|
||||||
|
import { Walkthrough } from '../components/Walkthrough';
|
||||||
import {
|
import {
|
||||||
clearAllStores,
|
clearAllStores,
|
||||||
initialize,
|
initialize,
|
||||||
@@ -202,23 +203,7 @@ export const useAppEvents = () => {
|
|||||||
if (!user) return;
|
if (!user) return;
|
||||||
MMKV.setItem('isUserEmailConfirmed', 'yes');
|
MMKV.setItem('isUserEmailConfirmed', 'yes');
|
||||||
await PremiumService.setPremiumStatus();
|
await PremiumService.setPremiumStatus();
|
||||||
let message = 'You have been rewarded 7 more days of free trial. Enjoy using Notesnook!';
|
Walkthrough.present('emailconfirmed', false, true);
|
||||||
presentSheet({
|
|
||||||
title: 'Email confirmed!',
|
|
||||||
paragraph: message,
|
|
||||||
component: (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingBottom: 15,
|
|
||||||
alignItems: 'center'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ProFeatures />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (user?.isEmailConfirmed) {
|
if (user?.isEmailConfirmed) {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
}
|
}
|
||||||
@@ -240,15 +225,7 @@ export const useAppEvents = () => {
|
|||||||
const onAccountStatusChange = async userStatus => {
|
const onAccountStatusChange = async userStatus => {
|
||||||
if (!PremiumService.get() && userStatus.type === 5) {
|
if (!PremiumService.get() && userStatus.type === 5) {
|
||||||
PremiumService.subscriptions.clear();
|
PremiumService.subscriptions.clear();
|
||||||
presentSheet({
|
Walkthrough.present('prouser', false, true);
|
||||||
title: 'Notesnook Pro',
|
|
||||||
paragraph: `Your Notesnook Pro subscription has been successfully activated.`,
|
|
||||||
action: async () => {
|
|
||||||
eSendEvent(eCloseProgressDialog);
|
|
||||||
},
|
|
||||||
icon: 'check',
|
|
||||||
actionText: 'Continue'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
await PremiumService.setPremiumStatus();
|
await PremiumService.setPremiumStatus();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { Appearance, ScrollView, TouchableOpacity, View } from 'react-native';
|
import { Appearance, TouchableOpacity, View } from 'react-native';
|
||||||
|
import { ScrollView } from 'react-native-gesture-handler';
|
||||||
import Menu, { MenuItem } from 'react-native-reanimated-material-menu';
|
import Menu, { MenuItem } from 'react-native-reanimated-material-menu';
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import ToggleSwitch from 'toggle-switch-react-native';
|
import ToggleSwitch from 'toggle-switch-react-native';
|
||||||
@@ -111,82 +112,8 @@ const SettingsAppearanceSection = () => {
|
|||||||
Change the accent color of the app.
|
Change the accent color of the app.
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
</View>
|
</View>
|
||||||
<ScrollView
|
|
||||||
horizontal={true}
|
<AccentColorPicker />
|
||||||
showsHorizontalScrollIndicator={false}
|
|
||||||
onMoveShouldSetResponderCapture={() => {
|
|
||||||
tabBarRef.current?.setScrollEnabled(false);
|
|
||||||
}}
|
|
||||||
onMomentumScrollEnd={() => {
|
|
||||||
tabBarRef.current?.setScrollEnabled(true);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
borderRadius: 5,
|
|
||||||
padding: 5,
|
|
||||||
marginTop: 10,
|
|
||||||
marginBottom: pv + 5,
|
|
||||||
width: '100%',
|
|
||||||
paddingHorizontal: 12
|
|
||||||
}}
|
|
||||||
nestedScrollEnabled
|
|
||||||
contentContainerStyle={{
|
|
||||||
alignSelf: 'center',
|
|
||||||
flexDirection: 'row',
|
|
||||||
flexWrap: 'wrap'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{[
|
|
||||||
'#FF5722',
|
|
||||||
'#FFA000',
|
|
||||||
'#1B5E20',
|
|
||||||
'#008837',
|
|
||||||
'#757575',
|
|
||||||
'#0560ff',
|
|
||||||
'#009688',
|
|
||||||
'#2196F3',
|
|
||||||
'#880E4F',
|
|
||||||
'#9C27B0',
|
|
||||||
'#FF1744',
|
|
||||||
'#B71C1C'
|
|
||||||
].map(item => (
|
|
||||||
<PressableButton
|
|
||||||
key={item}
|
|
||||||
customColor={
|
|
||||||
colors.accent === item
|
|
||||||
? RGB_Linear_Shade(!colors.night ? -0.2 : 0.2, hexToRGBA(item, 1))
|
|
||||||
: item
|
|
||||||
}
|
|
||||||
customSelectedColor={item}
|
|
||||||
alpha={!colors.night ? -0.1 : 0.1}
|
|
||||||
opacity={1}
|
|
||||||
onPress={async () => {
|
|
||||||
await PremiumService.verify(async () => {
|
|
||||||
changeAccentColor(item);
|
|
||||||
await MMKV.setStringAsync('accentColor', item);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
customStyle={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginRight: 10,
|
|
||||||
marginVertical: 5,
|
|
||||||
width: DDS.isLargeTablet() ? 40 : 50,
|
|
||||||
height: DDS.isLargeTablet() ? 40 : 50,
|
|
||||||
borderRadius: 100
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{colors.accent === item ? (
|
|
||||||
<Icon
|
|
||||||
size={DDS.isLargeTablet() ? SIZE.lg : SIZE.xxl}
|
|
||||||
color="white"
|
|
||||||
name="check"
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</PressableButton>
|
|
||||||
))}
|
|
||||||
<View style={{ width: 50 }} />
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<CustomButton
|
<CustomButton
|
||||||
title="Use system theme"
|
title="Use system theme"
|
||||||
@@ -337,4 +264,99 @@ const SettingsAppearanceSection = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const AccentColorPicker = ({ settings = true }) => {
|
||||||
|
const [state, dispatch] = useTracked();
|
||||||
|
const { colors } = state;
|
||||||
|
|
||||||
|
function changeColorScheme(colors = COLOR_SCHEME, accent = ACCENT) {
|
||||||
|
let newColors = setColorScheme(colors, accent);
|
||||||
|
dispatch({ type: Actions.THEME, colors: newColors });
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeAccentColor(accentColor) {
|
||||||
|
ACCENT.color = accentColor;
|
||||||
|
ACCENT.shade = accentColor + '12';
|
||||||
|
changeColorScheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView
|
||||||
|
horizontal={true}
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
|
onMoveShouldSetResponderCapture={() => {
|
||||||
|
if (!settings) return;
|
||||||
|
tabBarRef.current?.setScrollEnabled(false);
|
||||||
|
}}
|
||||||
|
onMomentumScrollEnd={() => {
|
||||||
|
if (!settings) return;
|
||||||
|
tabBarRef.current?.setScrollEnabled(true);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
borderRadius: 5,
|
||||||
|
padding: 5,
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: pv + 5,
|
||||||
|
width: '100%',
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
maxWidth: settings ? null : '100%'
|
||||||
|
}}
|
||||||
|
scrollEnabled={true}
|
||||||
|
nestedScrollEnabled={true}
|
||||||
|
contentContainerStyle={{
|
||||||
|
alignSelf: 'center',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{[
|
||||||
|
'#FF5722',
|
||||||
|
'#FFA000',
|
||||||
|
'#1B5E20',
|
||||||
|
'#008837',
|
||||||
|
'#757575',
|
||||||
|
'#0560ff',
|
||||||
|
'#009688',
|
||||||
|
'#2196F3',
|
||||||
|
'#880E4F',
|
||||||
|
'#9C27B0',
|
||||||
|
'#FF1744',
|
||||||
|
'#B71C1C'
|
||||||
|
].map(item => (
|
||||||
|
<PressableButton
|
||||||
|
key={item}
|
||||||
|
customColor={
|
||||||
|
colors.accent === item
|
||||||
|
? RGB_Linear_Shade(!colors.night ? -0.2 : 0.2, hexToRGBA(item, 1))
|
||||||
|
: item
|
||||||
|
}
|
||||||
|
customSelectedColor={item}
|
||||||
|
alpha={!colors.night ? -0.1 : 0.1}
|
||||||
|
opacity={1}
|
||||||
|
onPress={async () => {
|
||||||
|
await PremiumService.verify(async () => {
|
||||||
|
changeAccentColor(item);
|
||||||
|
await MMKV.setStringAsync('accentColor', item);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
customStyle={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginRight: 10,
|
||||||
|
marginVertical: 5,
|
||||||
|
width: DDS.isLargeTablet() ? 40 : 50,
|
||||||
|
height: DDS.isLargeTablet() ? 40 : 50,
|
||||||
|
borderRadius: 100
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{colors.accent === item ? (
|
||||||
|
<Icon size={DDS.isLargeTablet() ? SIZE.lg : SIZE.xxl} color="white" name="check" />
|
||||||
|
) : null}
|
||||||
|
</PressableButton>
|
||||||
|
))}
|
||||||
|
<View style={{ width: 50 }} />
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default SettingsAppearanceSection;
|
export default SettingsAppearanceSection;
|
||||||
|
|||||||
@@ -124,11 +124,17 @@ export const Settings = ({ navigation }) => {
|
|||||||
component: <Issue />
|
component: <Issue />
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
desc: `Facing an issue? Click here to create a bug report`
|
desc: `Faced an issue or have a suggestion? Click here to create a bug report`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Join our Telegram group',
|
||||||
|
desc: "We are on telegram, let's talk",
|
||||||
|
func: () => {
|
||||||
|
Linking.openURL('https://t.me/notesnook').catch(console.log);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Join our Discord community',
|
name: 'Join our Discord community',
|
||||||
|
|
||||||
func: async () => {
|
func: async () => {
|
||||||
presentSheet({
|
presentSheet({
|
||||||
title: 'Join our Discord Community',
|
title: 'Join our Discord Community',
|
||||||
@@ -144,7 +150,7 @@ export const Settings = ({ navigation }) => {
|
|||||||
icon: 'discord',
|
icon: 'discord',
|
||||||
action: async () => {
|
action: async () => {
|
||||||
try {
|
try {
|
||||||
await openLinkInBrowser('https://discord.gg/zQBK97EE22', colors);
|
Linking.openURL('https://discord.gg/zQBK97EE22').catch(console.log);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
},
|
},
|
||||||
actionText: 'Join Now'
|
actionText: 'Join Now'
|
||||||
|
|||||||
Reference in New Issue
Block a user