mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 19:49:54 +02:00
mobile: ui updates
This commit is contained in:
@@ -382,6 +382,13 @@ const ExportNotesSheet = ({
|
||||
|
||||
ExportNotesSheet.present = async (ids?: string[], allNotes?: boolean) => {
|
||||
const exportNoteIds = allNotes ? await db.notes.all?.ids() : ids || [];
|
||||
if (!exportNoteIds.length) {
|
||||
ToastManager.show({
|
||||
message: strings.noNotesToExport(),
|
||||
type: "info"
|
||||
});
|
||||
return;
|
||||
}
|
||||
presentSheet({
|
||||
component: (ref, close, update) => (
|
||||
<ExportNotesSheet ids={exportNoteIds} update={update} close={close} />
|
||||
|
||||
@@ -320,7 +320,7 @@ export function FormInput({
|
||||
onPress && loading ? colors.primary.accent : colors.primary.paragraph,
|
||||
paddingTop: Spacing.LEVEL_3,
|
||||
paddingBottom: Spacing.LEVEL_3,
|
||||
lineHeight: fontSize + fontSize * 0.3,
|
||||
lineHeight: fontSize + fontSize * 0.2,
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
fontFamily: "Inter-Regular",
|
||||
|
||||
@@ -18,26 +18,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { TextInput, View } from "react-native";
|
||||
import { db } from "../../../common/database";
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
import { Button } from "../../../components/ui/button";
|
||||
import FormInput, {
|
||||
createFormRef,
|
||||
validators
|
||||
} from "../../../components/ui/input/form-input";
|
||||
import Heading from "../../../components/ui/typography/heading";
|
||||
import Paragraph from "../../../components/ui/typography/paragraph";
|
||||
import { eSendEvent, ToastManager } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { eUserLoggedIn } from "../../../utils/events";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
import { PASSWORD_PLACEHOLDER } from "../../../utils/constants";
|
||||
|
||||
enum EmailChangeSteps {
|
||||
verify,
|
||||
changeEmail
|
||||
}
|
||||
|
||||
const RESEND_TIMEOUT = 30;
|
||||
|
||||
export const ChangeEmail = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const [step, setStep] = useState(EmailChangeSteps.verify);
|
||||
@@ -49,10 +51,30 @@ export const ChangeEmail = () => {
|
||||
})
|
||||
);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [resendSeconds, setResendSeconds] = useState(RESEND_TIMEOUT);
|
||||
const emailInputRef = useRef<TextInput>(null);
|
||||
const passInputRef = useRef<TextInput>(null);
|
||||
const codeInputRef = useRef<TextInput>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (step !== EmailChangeSteps.changeEmail || resendSeconds <= 0) return;
|
||||
const timer = setTimeout(() => {
|
||||
setResendSeconds((seconds) => seconds - 1);
|
||||
}, 1000);
|
||||
return () => clearTimeout(timer);
|
||||
}, [step, resendSeconds]);
|
||||
|
||||
const onResend = useCallback(async () => {
|
||||
if (resendSeconds > 0) return;
|
||||
try {
|
||||
const { email } = formRef.current.getValues();
|
||||
await db.user?.sendVerificationEmail(email);
|
||||
setResendSeconds(RESEND_TIMEOUT);
|
||||
} catch (e) {
|
||||
formRef.current.setError("code", (e as Error).message);
|
||||
}
|
||||
}, [resendSeconds]);
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
if (step === EmailChangeSteps.verify) {
|
||||
@@ -67,6 +89,7 @@ export const ChangeEmail = () => {
|
||||
if (!verified) throw new Error(strings.passwordIncorrect());
|
||||
await db.user?.sendVerificationEmail(email);
|
||||
setStep(EmailChangeSteps.changeEmail);
|
||||
setResendSeconds(RESEND_TIMEOUT);
|
||||
formRef.current.clearErrors();
|
||||
formRef.current.setValue("code", "");
|
||||
setLoading(false);
|
||||
@@ -113,67 +136,98 @@ export const ChangeEmail = () => {
|
||||
gap: Spacing.LEVEL_4
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
{step === EmailChangeSteps.verify ? (
|
||||
<>
|
||||
<FormInput
|
||||
name="email"
|
||||
formRef={formRef}
|
||||
label={strings.enterNewEmail()}
|
||||
fwdRef={emailInputRef}
|
||||
placeholder={"you@example.com"}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
validators={[
|
||||
validators.required(strings.emailRequired()),
|
||||
validators.email(strings.enterValidEmail())
|
||||
]}
|
||||
onSubmitEditing={() => {
|
||||
passInputRef.current?.focus();
|
||||
}}
|
||||
/>
|
||||
<FormInput
|
||||
name="password"
|
||||
label={strings.password()}
|
||||
formRef={formRef}
|
||||
fwdRef={passInputRef}
|
||||
placeholder={PASSWORD_PLACEHOLDER}
|
||||
secureTextEntry
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
autoComplete="password"
|
||||
validators={[validators.required(strings.passwordRequired())]}
|
||||
onSubmitEditing={onSubmit}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FormInput
|
||||
name="code"
|
||||
formRef={formRef}
|
||||
fwdRef={codeInputRef}
|
||||
placeholder={strings.code()}
|
||||
keyboardType="number-pad"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
maxLength={6}
|
||||
validators={[
|
||||
validators.required(strings.enterSixDigitCode()),
|
||||
(value: string) =>
|
||||
/^\d{6}$/.test(value.trim())
|
||||
? undefined
|
||||
: strings.enterSixDigitCode()
|
||||
]}
|
||||
onSubmitEditing={onSubmit}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
{step === EmailChangeSteps.verify ? (
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
<FormInput
|
||||
name="email"
|
||||
formRef={formRef}
|
||||
label={strings.enterNewEmail()}
|
||||
fwdRef={emailInputRef}
|
||||
placeholder={strings.enterEmail()}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
validators={[
|
||||
validators.required(strings.emailRequired()),
|
||||
validators.email(strings.enterValidEmail())
|
||||
]}
|
||||
onSubmitEditing={() => {
|
||||
passInputRef.current?.focus();
|
||||
}}
|
||||
/>
|
||||
<FormInput
|
||||
name="password"
|
||||
label={strings.enterAccountPassword()}
|
||||
formRef={formRef}
|
||||
fwdRef={passInputRef}
|
||||
placeholder={strings.password()}
|
||||
secureTextEntry
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
autoComplete="password"
|
||||
validators={[validators.required(strings.passwordRequired())]}
|
||||
onSubmitEditing={onSubmit}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_4
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<Heading fontSize="MD" lineHeight="100%">
|
||||
{strings.verifyCurrentEmail()}
|
||||
</Heading>
|
||||
<Paragraph fontSize="SM" color={colors.secondary.paragraph}>
|
||||
{strings.verifyCurrentEmailDesc()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
<FormInput
|
||||
name="code"
|
||||
formRef={formRef}
|
||||
label={strings.enterCode()}
|
||||
fwdRef={codeInputRef}
|
||||
placeholder={strings.code()}
|
||||
keyboardType="number-pad"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
maxLength={6}
|
||||
validators={[
|
||||
validators.required(strings.enterSixDigitCode()),
|
||||
(value: string) =>
|
||||
/^\d{6}$/.test(value.trim())
|
||||
? undefined
|
||||
: strings.enterSixDigitCode()
|
||||
]}
|
||||
onSubmitEditing={onSubmit}
|
||||
/>
|
||||
<Paragraph
|
||||
fontSize="SM"
|
||||
onPress={onResend}
|
||||
style={{
|
||||
marginTop: -Spacing.LEVEL_1
|
||||
}}
|
||||
color={
|
||||
resendSeconds > 0
|
||||
? colors.secondary.paragraph
|
||||
: colors.primary.accent
|
||||
}
|
||||
>
|
||||
{resendSeconds > 0
|
||||
? strings.resend2faCode(`${resendSeconds}`)
|
||||
: strings.resendCode()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Button
|
||||
title={
|
||||
@@ -181,7 +235,7 @@ export const ChangeEmail = () => {
|
||||
? undefined
|
||||
: step === EmailChangeSteps.verify
|
||||
? strings.continue()
|
||||
: strings.changeEmail()
|
||||
: strings.verify()
|
||||
}
|
||||
type="accent"
|
||||
style={{
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
} from "../manage-inbox-keys";
|
||||
import { FailedInboxItems } from "../failed-inbox-items";
|
||||
import AccountCard from "./account-card";
|
||||
import { EditProfile } from "./edit-profile";
|
||||
|
||||
export const components: { [name: string]: ReactElement } = {
|
||||
homeselector: <HomePicker />,
|
||||
@@ -81,5 +82,6 @@ export const components: { [name: string]: ReactElement } = {
|
||||
"inbox-keys": <InboxKeysList />,
|
||||
"failed-inbox-items": <FailedInboxItems />,
|
||||
"setup-inbox-keys": <SetupInboxKeys />,
|
||||
"account-card": <AccountCard />
|
||||
"account-card": <AccountCard />,
|
||||
"edit-profile": <EditProfile />
|
||||
};
|
||||
|
||||
204
apps/mobile/app/screens/settings/components/edit-profile.tsx
Normal file
204
apps/mobile/app/screens/settings/components/edit-profile.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { Image, TextInput, View } from "react-native";
|
||||
import { openPicker } from "react-native-image-crop-picker";
|
||||
import { db } from "../../../common/database";
|
||||
import { Radius, Spacing } from "../../../common/design/spacing";
|
||||
import AppIcon from "../../../components/ui/AppIcon";
|
||||
import { Button } from "../../../components/ui/button";
|
||||
import FormInput, {
|
||||
createFormRef
|
||||
} from "../../../components/ui/input/form-input";
|
||||
import { Pressable } from "../../../components/ui/pressable";
|
||||
import { ToastManager } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
|
||||
const AVATAR_SIZE = 105;
|
||||
const AVATAR_BADGE_SIZE = 32;
|
||||
|
||||
export const EditProfile = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const profile = useUserStore((state) => state.profile);
|
||||
const formRef = useRef(
|
||||
createFormRef({
|
||||
fullName: profile?.fullName || ""
|
||||
})
|
||||
);
|
||||
const nameInputRef = useRef<TextInput>(null);
|
||||
const [name, setName] = useState(profile?.fullName || "");
|
||||
const [profilePicture, setProfilePicture] = useState(profile?.profilePicture);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const isPicking = useRef(false);
|
||||
|
||||
const isDirty =
|
||||
name.trim() !== (profile?.fullName || "").trim() ||
|
||||
profilePicture !== profile?.profilePicture;
|
||||
|
||||
const onPickImage = async () => {
|
||||
if (isPicking.current) return;
|
||||
try {
|
||||
const image = await openPicker({
|
||||
width: 512,
|
||||
height: 512,
|
||||
cropping: true,
|
||||
cropperCircleOverlay: true,
|
||||
mediaType: "photo",
|
||||
includeBase64: true
|
||||
});
|
||||
setProfilePicture(
|
||||
image.data ? `data:${image.mime};base64,${image.data}` : image.path
|
||||
);
|
||||
isPicking.current = false;
|
||||
} catch (e) {
|
||||
isPicking.current = false;
|
||||
/* user cancelled */
|
||||
}
|
||||
};
|
||||
|
||||
const onSave = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const fullName = formRef.current.getValue("fullName").trim();
|
||||
await db.settings.setProfile({
|
||||
fullName: fullName || undefined,
|
||||
profilePicture: profilePicture || undefined
|
||||
});
|
||||
useUserStore.setState({
|
||||
profile: db.settings.getProfile()
|
||||
});
|
||||
ToastManager.show({
|
||||
heading: strings.fullNameUpdated(),
|
||||
type: "success",
|
||||
context: "global"
|
||||
});
|
||||
Navigation.goBack();
|
||||
} catch (e) {
|
||||
ToastManager.error(e as Error, undefined, "global");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingTop: Spacing.LEVEL_4,
|
||||
gap: Spacing.LEVEL_4
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_3,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
type="transparent"
|
||||
onPress={onPickImage}
|
||||
style={{
|
||||
width: AVATAR_SIZE,
|
||||
height: AVATAR_SIZE,
|
||||
borderRadius: AVATAR_SIZE
|
||||
}}
|
||||
>
|
||||
{profilePicture ? (
|
||||
<Image
|
||||
source={{ uri: profilePicture }}
|
||||
style={{
|
||||
width: AVATAR_SIZE,
|
||||
height: AVATAR_SIZE,
|
||||
borderRadius: AVATAR_SIZE
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
width: AVATAR_SIZE,
|
||||
height: AVATAR_SIZE,
|
||||
borderRadius: AVATAR_SIZE,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: colors.secondary.background
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name="user"
|
||||
iconFamily="notesnook"
|
||||
size={AVATAR_SIZE / 2.5}
|
||||
color={colors.secondary.icon}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
width: AVATAR_BADGE_SIZE,
|
||||
height: AVATAR_BADGE_SIZE,
|
||||
borderRadius: AVATAR_BADGE_SIZE,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderWidth: 2,
|
||||
borderColor: colors.primary.background,
|
||||
backgroundColor: colors.secondary.background
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name="mode-edit"
|
||||
iconFamily="notesnook"
|
||||
size={16}
|
||||
color={colors.primary.icon}
|
||||
/>
|
||||
</View>
|
||||
</Pressable>
|
||||
|
||||
<FormInput
|
||||
name="fullName"
|
||||
formRef={formRef}
|
||||
fwdRef={nameInputRef}
|
||||
label={strings.name()}
|
||||
placeholder={strings.name()}
|
||||
wrapperStyle={{ width: "100%" }}
|
||||
containerStyle={{ borderRadius: Radius.XS }}
|
||||
onChangeText={setName}
|
||||
onSubmitEditing={onSave}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Button
|
||||
title={strings.saveChanges()}
|
||||
type="accent"
|
||||
loading={loading}
|
||||
disabled={!isDirty}
|
||||
onPress={onSave}
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditProfile;
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { FlatList, Platform, View } from "react-native";
|
||||
import { ActivityIndicator, FlatList, Platform, View } from "react-native";
|
||||
import NotificationSounds, {
|
||||
playSampleSound,
|
||||
Sound,
|
||||
@@ -34,6 +34,10 @@ import { useThemeColors } from "@notesnook/theme";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { Header } from "../../../components/header";
|
||||
import LineSeparator from "../../../components/ui/seperator/line-separator";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
|
||||
const SoundItem = ({
|
||||
playingSoundId,
|
||||
@@ -127,43 +131,80 @@ const SoundItem = ({
|
||||
};
|
||||
|
||||
export default function SoundPicker() {
|
||||
const { colors } = useThemeColors();
|
||||
const [sounds, setSounds] = useState<Sound[]>([]);
|
||||
const [ringtones, setRingtones] = useState<Sound[]>([]);
|
||||
const [playing, setPlaying] = useState<Sound | undefined>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const notificationSound = useSettingStore(
|
||||
(state) => state.settings.notificationSound
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
NotificationSounds.getNotifications("ringtone").then((results) =>
|
||||
setRingtones([
|
||||
{
|
||||
soundID: "defaultSound",
|
||||
title: strings.defaultSound(),
|
||||
url: ""
|
||||
},
|
||||
...results
|
||||
])
|
||||
);
|
||||
NotificationSounds.getNotifications("notification").then((results) =>
|
||||
setSounds([...results])
|
||||
);
|
||||
async function getSounds() {
|
||||
try {
|
||||
const ringtones = await NotificationSounds.getNotifications("ringtone");
|
||||
const sounds =
|
||||
await NotificationSounds.getNotifications("notification");
|
||||
setRingtones([
|
||||
{
|
||||
soundID: "defaultSound",
|
||||
title: strings.defaultSound(),
|
||||
url: ""
|
||||
},
|
||||
...ringtones
|
||||
]);
|
||||
setSounds([...sounds]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
getSounds();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View>
|
||||
<FlatList
|
||||
data={[...sounds, ...ringtones]}
|
||||
renderItem={({ item, index }) => (
|
||||
<SoundItem
|
||||
playingSoundId={playing?.soundID}
|
||||
selectedSoundId={notificationSound?.soundID}
|
||||
item={item}
|
||||
index={index}
|
||||
setPlaying={setPlaying}
|
||||
/>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<Header
|
||||
title={strings.changeNotificationSound()}
|
||||
canGoBack
|
||||
style={{
|
||||
backgroundColor: "transparent"
|
||||
}}
|
||||
/>
|
||||
<LineSeparator />
|
||||
|
||||
{loading ? (
|
||||
<View
|
||||
style={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: Spacing.LEVEL_2,
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<ActivityIndicator size="small" color={colors.primary.accent} />
|
||||
<Paragraph color={colors.secondary.paragraph}>
|
||||
{strings.loading()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : (
|
||||
<FlatList
|
||||
data={[...sounds, ...ringtones]}
|
||||
renderItem={({ item, index }) => (
|
||||
<SoundItem
|
||||
playingSoundId={playing?.soundID}
|
||||
selectedSoundId={notificationSound?.soundID}
|
||||
item={item}
|
||||
index={index}
|
||||
setPlaying={setPlaying}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -62,99 +62,6 @@ export const accountGroup: SettingSection = {
|
||||
useHook: () => useUserStore((state) => state.user),
|
||||
hidden: (current) => !current,
|
||||
sections: [
|
||||
// {
|
||||
// id: "subscription-status",
|
||||
// useHook: () => useUserStore((state) => state.user),
|
||||
// hidden: (current) => {
|
||||
// const user = current as User;
|
||||
// return (
|
||||
// !user ||
|
||||
// !user.subscription ||
|
||||
// user.subscription.provider === undefined ||
|
||||
// !strings.subscriptionProviderInfo[user?.subscription?.provider] ||
|
||||
// user.subscription?.plan === SubscriptionPlan.FREE
|
||||
// );
|
||||
// },
|
||||
// name: (current) => {
|
||||
// const user = (current as User) || useUserStore.getState().user;
|
||||
// return (
|
||||
// strings.subscriptionProviderInfo[
|
||||
// user?.subscription?.provider
|
||||
// ]?.title() || `Unknown provider id: ${user?.subscription?.provider}`
|
||||
// );
|
||||
// },
|
||||
// icon: "credit-card",
|
||||
// modifer: () => {
|
||||
// const user = useUserStore.getState().user;
|
||||
// if (!user) return;
|
||||
// const subscriptionProviderInfo =
|
||||
// strings.subscriptionProviderInfo[user?.subscription?.provider];
|
||||
|
||||
// if (!subscriptionProviderInfo) return;
|
||||
|
||||
// const isCurrentPlatform =
|
||||
// (user.subscription?.provider === SubscriptionProvider.APPLE &&
|
||||
// Platform.OS === "ios") ||
|
||||
// (user.subscription?.provider === SubscriptionProvider.GOOGLE &&
|
||||
// Platform.OS === "android");
|
||||
|
||||
// if (
|
||||
// (user.subscription?.provider === SubscriptionProvider.GOOGLE ||
|
||||
// user.subscription?.provider === SubscriptionProvider.APPLE) &&
|
||||
// isCurrentPlatform &&
|
||||
// user?.subscription?.productId
|
||||
// ) {
|
||||
// RNIap.deepLinkToSubscriptions({
|
||||
// sku: user?.subscription.productId
|
||||
// });
|
||||
// } else {
|
||||
// presentSheet({
|
||||
// title: subscriptionProviderInfo.title(),
|
||||
// paragraph: subscriptionProviderInfo.desc()
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// description: (current) => {
|
||||
// const user = current as User;
|
||||
// if (!user) return strings.neverHesitate();
|
||||
// const subscriptionDaysLeft =
|
||||
// user && getTimeLeft(user.subscription?.expiry);
|
||||
// const expiryDate = dayjs(user?.subscription?.expiry).format(
|
||||
// "dddd, MMMM D, YYYY h:mm A"
|
||||
// );
|
||||
// const startDate = dayjs(user?.subscription?.start).format(
|
||||
// "dddd, MMMM D, YYYY h:mm A"
|
||||
// );
|
||||
|
||||
// const trialEndDate = dayjs(user?.subscription?.start)
|
||||
// .add(
|
||||
// user?.subscription?.productId?.includes("monthly") ? 7 : 14,
|
||||
// "day"
|
||||
// )
|
||||
// .format("dddd, MMMM D, YYYY h:mm A");
|
||||
|
||||
// if (
|
||||
// user.subscription?.plan !== SubscriptionPlan.FREE &&
|
||||
// user.subscription?.productId
|
||||
// ) {
|
||||
// const status = user.subscription?.status;
|
||||
// return status === SubscriptionStatus.TRIAL
|
||||
// ? strings.trialOnGoing(trialEndDate)
|
||||
// : status === SubscriptionStatus.ACTIVE
|
||||
// ? strings.subRenewOn(expiryDate)
|
||||
// : status === SubscriptionStatus.CANCELED ||
|
||||
// status === SubscriptionStatus.PAUSED
|
||||
// ? strings.subEndsOn(expiryDate)
|
||||
// : status === SubscriptionStatus.EXPIRED
|
||||
// ? subscriptionDaysLeft.time < -3
|
||||
// ? strings.subEnded()
|
||||
// : strings.accountDowngradedIn(3)
|
||||
// : strings.neverHesitate();
|
||||
// }
|
||||
|
||||
// return strings.neverHesitate();
|
||||
// }
|
||||
// },
|
||||
{
|
||||
id: "redeem-gift-code",
|
||||
name: strings.redeemGiftCode(),
|
||||
@@ -222,76 +129,17 @@ export const accountGroup: SettingSection = {
|
||||
description: strings.editProfileDesc(),
|
||||
icon: "user",
|
||||
iconFamily: "notesnook",
|
||||
sections: [
|
||||
{
|
||||
id: "remove-profile-picture",
|
||||
icon: "trash",
|
||||
iconFamily: "notesnook",
|
||||
name: strings.removeProfilePicture(),
|
||||
description: strings.removeProfilePictureDesc(),
|
||||
useHook: () =>
|
||||
useUserStore((state) => state.profile?.profilePicture),
|
||||
hidden: () =>
|
||||
!useUserStore.getState().profile?.profilePicture,
|
||||
isModal: true,
|
||||
modifer: () => {
|
||||
presentDialog({
|
||||
title: strings.removeProfilePicture(),
|
||||
paragraph: strings.removeProfilePictureConfirmation(),
|
||||
positiveText: strings.remove(),
|
||||
positivePress: async () => {
|
||||
db.settings
|
||||
.setProfile({
|
||||
profilePicture: undefined
|
||||
})
|
||||
.then(async () => {
|
||||
useUserStore.setState({
|
||||
profile: db.settings.getProfile()
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "remove-name",
|
||||
icon: "trash",
|
||||
iconFamily: "notesnook",
|
||||
name: strings.removeFullName(),
|
||||
description: strings.removeFullNameDesc(),
|
||||
useHook: () =>
|
||||
useUserStore((state) => state.profile?.fullName),
|
||||
hidden: () => !useUserStore.getState().profile?.fullName,
|
||||
isModal: true,
|
||||
modifer: () => {
|
||||
presentDialog({
|
||||
title: strings.removeFullName(),
|
||||
paragraph: strings.removeFullNameConfirmation(),
|
||||
positiveText: strings.remove(),
|
||||
positivePress: async () => {
|
||||
db.settings
|
||||
.setProfile({
|
||||
fullName: undefined
|
||||
})
|
||||
.then(async () => {
|
||||
useUserStore.setState({
|
||||
profile: db.settings.getProfile()
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "change-email",
|
||||
name: strings.changeEmail(),
|
||||
type: "screen",
|
||||
component: "change-email",
|
||||
description: strings.changeEmailDesc(),
|
||||
icon: "at",
|
||||
headerBottomBorder: true
|
||||
}
|
||||
]
|
||||
component: "edit-profile"
|
||||
},
|
||||
|
||||
{
|
||||
id: "change-email",
|
||||
name: strings.changeEmail(),
|
||||
type: "screen",
|
||||
component: "change-email",
|
||||
description: strings.changeEmailDesc(),
|
||||
icon: "at",
|
||||
headerBottomBorder: true
|
||||
},
|
||||
{
|
||||
id: "subscription-not-active",
|
||||
|
||||
@@ -280,7 +280,7 @@ export const customizeGroup: SettingSection = {
|
||||
maxInputValue: 400,
|
||||
step: 1,
|
||||
inputBadgeValue: "px",
|
||||
icon: "text-aa",
|
||||
icon: "format-font-size",
|
||||
iconFamily: "notesnook",
|
||||
property: "defaultFontSize"
|
||||
},
|
||||
@@ -290,8 +290,7 @@ export const customizeGroup: SettingSection = {
|
||||
description: strings.lineHeightDesc(),
|
||||
type: "input-selector",
|
||||
property: "defaultLineHeight",
|
||||
icon: "list",
|
||||
iconSize: 8,
|
||||
icon: "format-arrows-vertical",
|
||||
iconFamily: "notesnook",
|
||||
minInputValue: EDITOR_LINE_HEIGHT.MIN,
|
||||
maxInputValue: EDITOR_LINE_HEIGHT.MAX,
|
||||
|
||||
@@ -90,7 +90,7 @@ export const productivityGroup: SettingSection = {
|
||||
validators: [
|
||||
validators.number(),
|
||||
validators.custom((value) => {
|
||||
return value < 4
|
||||
return value < 5
|
||||
? strings.valueMustBeGreaterThan("4")
|
||||
: undefined;
|
||||
})
|
||||
@@ -110,6 +110,7 @@ export const productivityGroup: SettingSection = {
|
||||
name: strings.changeNotificationSound(),
|
||||
description: strings.changeNotificationSoundDesc(),
|
||||
component: "sound-picker",
|
||||
hideHeader: true,
|
||||
icon: "speaker-high",
|
||||
iconFamily: "notesnook",
|
||||
hidden: () => Platform.OS === "android"
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
3
packages/icons/svgs/format-font-size.svg
Normal file
3
packages/icons/svgs/format-font-size.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.375 3.0625V4.8125C11.375 4.92853 11.3289 5.03981 11.2469 5.12186C11.1648 5.20391 11.0535 5.25 10.9375 5.25C10.8215 5.25 10.7102 5.20391 10.6281 5.12186C10.5461 5.03981 10.5 4.92853 10.5 4.8125V3.5H7.4375V10.5H8.75C8.86603 10.5 8.97731 10.5461 9.05936 10.6281C9.14141 10.7102 9.1875 10.8215 9.1875 10.9375C9.1875 11.0535 9.14141 11.1648 9.05936 11.2469C8.97731 11.3289 8.86603 11.375 8.75 11.375H5.25C5.13397 11.375 5.02269 11.3289 4.94064 11.2469C4.85859 11.1648 4.8125 11.0535 4.8125 10.9375C4.8125 10.8215 4.85859 10.7102 4.94064 10.6281C5.02269 10.5461 5.13397 10.5 5.25 10.5H6.5625V3.5H3.5V4.8125C3.5 4.92853 3.45391 5.03981 3.37186 5.12186C3.28981 5.20391 3.17853 5.25 3.0625 5.25C2.94647 5.25 2.83519 5.20391 2.75314 5.12186C2.67109 5.03981 2.625 4.92853 2.625 4.8125V3.0625C2.625 2.94647 2.67109 2.83519 2.75314 2.75314C2.83519 2.67109 2.94647 2.625 3.0625 2.625H10.9375C11.0535 2.625 11.1648 2.67109 11.2469 2.75314C11.3289 2.83519 11.375 2.94647 11.375 3.0625Z" fill="#181818"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1547,7 +1547,12 @@ $day$: Current day (eg. Monday)`,
|
||||
enterNotebookDescription: () => t`Enter notebook description`,
|
||||
searchNotebooks: () => t`Search notebooks`,
|
||||
enterNewEmail: () => t`Enter your new email`,
|
||||
enterEmail: () => t`Enter email`,
|
||||
verifyNewEmail: () => t`Enter verification code sent to your new email`,
|
||||
verifyCurrentEmail: () => t`Verify your current email`,
|
||||
verifyCurrentEmailDesc: () =>
|
||||
t`We've sent a verification code to your new email. Enter it below to continue.`,
|
||||
enterCode: () => t`Enter code`,
|
||||
issueTitlePlaceholder: () => t`Tell us what happened`,
|
||||
issuePlaceholder: () => t`Tell us more about the issue you are facing.
|
||||
|
||||
@@ -3054,5 +3059,6 @@ Continue without attachments?`,
|
||||
t`Changes from other devices won't be updated in the editor in real-time.`,
|
||||
disableSync: () => t`Disable sync`,
|
||||
disableSyncDesc: () =>
|
||||
t`Turns off syncing completely on this device. Any changes made will remain local only and new changes from your other devices won't sync to this device.`
|
||||
t`Turns off syncing completely on this device. Any changes made will remain local only and new changes from your other devices won't sync to this device.`,
|
||||
noNotesToExport: () => t`No notes to export`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user