Compare commits

...

29 Commits

Author SHA1 Message Date
Abdullah Atta
f9116b22db web: bump version to 2.4.9 2023-04-27 13:16:01 +05:00
Abdullah Atta
5b957f5f0d web: fix Can't resolve './network.worker' error 2023-04-27 13:08:13 +05:00
Abdullah Atta
a578aae3dc web: wait for internet before running sync on device wake 2023-04-27 13:08:13 +05:00
Abdullah Atta
43e5d2fa2d editor: fix table properties tooltip text 2023-04-27 13:08:03 +05:00
Abdullah Atta
b58d034716 theme: use outline instead of boxShadow for input border 2023-04-27 12:59:26 +05:00
Abdullah Atta
3b77d79997 web: prevent pro users from resubscribing 2023-04-27 12:59:26 +05:00
Abdullah Atta
ef4c9af7dc web: rename setters from onXYZ to xyz 2023-04-27 12:59:26 +05:00
ammarahm-ed
8ba3cf172e mobile: release v2.4.14 2023-04-27 12:49:02 +05:00
ammarahm-ed
3e446ce9dd mobile: fix crash on get search items 2023-04-27 11:27:48 +05:00
ammarahm-ed
394aa98713 mobile: remove usage of lazy imports 2023-04-27 10:59:04 +05:00
Muhammad Ali
a2ac882a05 desktop: remove custom protocol interception for https requests (#2426) 2023-04-27 10:13:37 +05:00
ammarahm-ed
cae36487f9 mobile: fix notebook not updated on note 2023-04-27 09:38:44 +05:00
ammarahm-ed
fade5eddbf mobile: replace newline with paragraph in generated html 2023-04-27 08:32:09 +05:00
ammarahm-ed
66b2ffce31 mobile: fix webpack config 2023-04-27 08:32:09 +05:00
ammarahm-ed
13cd2d4daa mobile: refactor 2023-04-27 08:18:25 +05:00
ammarahm-ed
8442faeeb1 mobile: close editor on navigate to topic 2023-04-26 20:06:40 +05:00
Ammar Ahmed
6d18383b22 mobile: sync notes created from quick note/notification notes (#2428)
* mobile: sync quick notes

* Update apps/mobile/app/services/notifications.ts

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>

* mobile: ensure full db init when sync is required

---------

Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-04-26 15:15:08 +05:00
ammarahm-ed
f9bcfe2f7a mobile: obfuscate short emails 2023-04-26 15:02:56 +05:00
ammarahm-ed
3621f95dbc web: obfuscate short emails 2023-04-26 15:02:56 +05:00
ammarahm-ed
536dead44d mobile: always load notes on init 2023-04-26 14:59:05 +05:00
ammarahm-ed
39d74e80a9 mobile: ensure that overlay gets hidden when entering foreground 2023-04-26 14:59:05 +05:00
ammarahm-ed
46e03dd248 mobile: only show app lock for main activity 2023-04-26 14:59:05 +05:00
ammarahm-ed
152fee2c29 mobile: reload editor if rendered view lost 2023-04-26 14:59:05 +05:00
ammarahm-ed
492a4d16b2 mobile: fix note options ui 2023-04-26 14:58:37 +05:00
ammarahm-ed
3d7c1fa1e7 mobile: fix configure toolbar sheet close with swipe 2023-04-26 14:58:21 +05:00
ammarahm-ed
69bb0e3656 editor: fix toolbar dropdown buttons size on mobile 2023-04-26 14:54:30 +05:00
ammarahm-ed
4875cfa108 core: duplicate note without content 2023-04-26 14:47:52 +05:00
ammarahm-ed
a28beb62c6 core: deep clone note when duplicating 2023-04-26 14:46:24 +05:00
Abdullah Atta
21c8960146 web: fix "Unauthorized" error on subscription cancel 2023-04-26 11:39:48 +05:00
61 changed files with 316 additions and 317 deletions

View File

@@ -148,7 +148,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
<Button
key={item.title}
title={item.title}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
type="gray"
onPress={() => onPress(item)}
width={null}

View File

@@ -67,7 +67,7 @@ export const Title = ({ text, style = {}, inline }) => {
right: 0
}}
iconSize={24}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
borderRadius: 100,
paddingVertical: 0,

View File

@@ -209,7 +209,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
}}
>
<Paragraph
size={SIZE.xs + 1}
size={SIZE.xs}
style={{
marginRight: 10
}}
@@ -221,7 +221,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
style={{
marginRight: 10
}}
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
>
{formatBytes(attachment.length)}
@@ -232,7 +232,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
style={{
marginRight: 10
}}
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
>
{attachment.noteIds.length} note
@@ -248,7 +248,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
context: "local"
});
}}
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
>
{attachment.metadata.hash}
@@ -304,7 +304,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
}}
key={item.id}
>
<Paragraph size={SIZE.xs + 1}>{item.title}</Paragraph>
<Paragraph size={SIZE.xs}>{item.title}</Paragraph>
</PressableButton>
))}
</>

View File

@@ -44,8 +44,10 @@ import Paragraph from "../ui/typography/paragraph";
import { LoginSteps, useLogin } from "./use-login";
function getObfuscatedEmail(email) {
if (!email) return null;
return email.replace(/(.{2})(.*)(?=@)/, function (gp1, gp2, gp3) {
if (!email) return "";
const [username, provider] = email.split("@");
if (username.length === 1) return `****@${provider}`;
return email.replace(/(.{1})(.*)(?=@)/, function (gp1, gp2, gp3) {
for (let i = 0; i < gp3.length; i++) {
gp2 += "*";
}

View File

@@ -86,7 +86,7 @@ export const ProFeatures = ({ count = 6 }) => {
await sleep(300);
eSendEvent(eOpenPremiumDialog);
}}
size={SIZE.xs + 1}
size={SIZE.xs}
style={{
textDecorationLine: "underline",
color: colors.icon

View File

@@ -37,6 +37,7 @@ import { useNoteStore } from "../../stores/use-notes-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
import { AndroidModule } from "../../utils";
import { eOpenAnnouncementDialog } from "../../utils/events";
import { getGithubVersion } from "../../utils/github-version";
import { SIZE } from "../../utils/size";
@@ -192,6 +193,11 @@ const Launcher = React.memo(
const onUnlockBiometrics = useCallback(async () => {
if (!(await BiometricService.isBiometryAvailable())) return;
if (Platform.OS === "android") {
const activityName = await AndroidModule.getActivityName();
if (activityName !== "MainActivity") return;
}
let verified = await BiometricService.validateUser(
"Unlock to access your notes",
""

View File

@@ -108,7 +108,7 @@ const ReminderItem = React.memo(
color={colors.errorText}
/>
<Paragraph
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
style={{ marginLeft: 5 }}
>
@@ -133,7 +133,7 @@ const ReminderItem = React.memo(
>
<Icon name="reload" size={SIZE.md} color={colors.accent} />
<Paragraph
size={SIZE.xs + 1}
size={SIZE.xs}
color={colors.icon}
style={{ marginLeft: 5 }}
>
@@ -147,7 +147,7 @@ const ReminderItem = React.memo(
<ReminderTime
reminder={item}
checkIsActive={false}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
justifyContent: "flex-start",
borderWidth: 0,

View File

@@ -143,7 +143,7 @@ export const Expiring = () => {
await sleep(300);
eSendEvent(eOpenPremiumDialog, promo);
}}
size={SIZE.xs + 1}
size={SIZE.xs}
style={{
textDecorationLine: "underline",
color: colors.icon,

View File

@@ -62,10 +62,10 @@ export const DateMeta = ({ item }) => {
paddingVertical: 3
}}
>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
<Paragraph size={SIZE.xs} color={colors.icon}>
{getNameFromKey(key)}
</Paragraph>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
<Paragraph size={SIZE.xs} color={colors.icon}>
{timeConverter(item[key])}
</Paragraph>
</View>

View File

@@ -108,7 +108,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
backgroundColor: "transparent",
paddingHorizontal: 0
}}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
/>
) : null}
</View>

View File

@@ -115,12 +115,12 @@ export const Items = ({ item, buttons, close }) => {
onPress={item.func}
key={item.id}
testID={"icon-" + item.id}
activeOpacity={1}
customStyle={{
alignItems: "center",
width: topBarItemWidth,
marginBottom: 10,
alignSelf: "flex-start",
marginRight: isLast ? 0 : 10,
backgroundColor: "transparent"
paddingHorizontal: 0,
width: topBarItemWidth
}}
>
<PressableButton
@@ -150,11 +150,7 @@ export const Items = ({ item, buttons, close }) => {
/>
</PressableButton>
<Paragraph
size={SIZE.xxs + 1}
style={{ textAlign: "center" }}
textBreakStrategy="simple"
>
<Paragraph size={SIZE.xxs + 1} style={{ textAlign: "center" }}>
{item.title}
</Paragraph>
</PressableButton>
@@ -187,7 +183,7 @@ export const Items = ({ item, buttons, close }) => {
horizontal
style={{
paddingHorizontal: 12,
paddingTop: 12
paddingVertical: 12
}}
>
{topBarItems.map(renderTopBarItem)}

View File

@@ -23,13 +23,18 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import Notebook from "../../screens/notebook";
import { TopicNotes } from "../../screens/notes/topic-notes";
import { presentSheet, ToastEvent } from "../../services/event-manager";
import {
eSendEvent,
presentSheet,
ToastEvent
} from "../../services/event-manager";
import Navigation from "../../services/navigation";
import { useNotebookStore } from "../../stores/use-notebook-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import { eClearEditor } from "../../utils/events";
export default function Notebooks({ note, close, full }) {
const colors = useThemeStore((state) => state.colors);
@@ -112,6 +117,7 @@ export default function Notebooks({ note, close, full }) {
size={SIZE.sm}
onPress={() => {
navigateNotebook(item.id);
eSendEvent(eClearEditor);
close();
}}
>
@@ -134,6 +140,7 @@ export default function Notebooks({ note, close, full }) {
key={topic.id}
onPress={() => {
navigateTopic(topic.id, item.id);
eSendEvent(eClearEditor);
close();
}}
onLongPress={async () => {
@@ -155,7 +162,7 @@ export default function Notebooks({ note, close, full }) {
title={topic.title}
type="gray"
height={30}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
icon="bookmark-outline"
style={{
marginRight: 5,
@@ -184,7 +191,7 @@ export default function Notebooks({ note, close, full }) {
{noteNotebooks.length > 1 && !full ? (
<Button
title={`See all linked notebooks`}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
alignSelf: "flex-end",
marginRight: 12,

View File

@@ -91,7 +91,7 @@ export const Synced = ({ item, close }) => {
console.error(e);
}
}}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
title="Learn more"
height={30}
type="grayAccent"

View File

@@ -57,7 +57,7 @@ export const Tags = ({ item, close }) => {
icon="plus"
iconPosition="right"
height={30}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
marginRight: 5,
borderRadius: 100,
@@ -107,7 +107,7 @@ const TagItem = ({ tag, close }) => {
title={"#" + tag}
type="grayBg"
height={20}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={style}
textStyle={{
textDecorationLine: "underline"

View File

@@ -39,7 +39,7 @@ export const Topics = ({ item, close }) => {
height={30}
onPress={() => open(topic)}
icon="bookmark-outline"
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{
marginRight: 5,
paddingHorizontal: 8,

View File

@@ -42,6 +42,7 @@ import { SelectionProvider } from "./context";
import { FilteredList } from "./filtered-list";
import { ListItem } from "./list-item";
import { useItemSelectionStore } from "./store";
import { useRelationStore } from "../../../stores/use-relation-store";
const MoveNoteSheet = ({ note, actionSheetRef }) => {
const colors = useThemeStore((state) => state.colors);
@@ -263,7 +264,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
if (itemState[id] === "selected") {
if (item.type === "notebook") {
for (let noteId of noteIds) {
db.relations.add(item, { id: noteId, type: "note" });
await db.relations.add(item, { id: noteId, type: "note" });
}
} else {
await db.notes.addToNotebook(
@@ -278,7 +279,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
} else if (itemState[id] === "deselected") {
if (item.type === "notebook") {
for (let noteId of noteIds) {
db.relations.unlink(item, { id: noteId, type: "note" });
await db.relations.unlink(item, { id: noteId, type: "note" });
}
} else {
await db.notes.removeFromNotebook(
@@ -296,6 +297,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
setNotebooks();
eSendEvent(eOnTopicSheetUpdate);
SearchService.updateAndSearch();
useRelationStore.getState().update();
actionSheetRef.current?.hide();
};

View File

@@ -158,11 +158,7 @@ export const MoveNotes = ({
{item.title}
</Paragraph>
{item.type == "note" && item.headline ? (
<Paragraph
numberOfLines={1}
color={colors.icon}
size={SIZE.xs + 1}
>
<Paragraph numberOfLines={1} color={colors.icon} size={SIZE.xs}>
{item.headline}
</Paragraph>
) : null}

View File

@@ -43,8 +43,7 @@ import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet";
import { QRCode } from "../../ui/svg/lazy";
import Paragraph from "../../ui/typography/paragraph";
let RNFetchBlob;
import RNFetchBlob from "rn-fetch-blob";
class RecoveryKeySheet extends React.Component {
constructor(props) {
@@ -115,7 +114,6 @@ class RecoveryKeySheet extends React.Component {
this.svg.current?.toDataURL(async (data) => {
try {
let path;
RNFetchBlob = (await import("rn-fetch-blob")).default;
let fileName = "nn_" + this.user.email + "_recovery_key_qrcode";
fileName = sanitizeFilename(fileName, { replacement: "_" });
fileName = fileName + ".png";
@@ -151,7 +149,6 @@ class RecoveryKeySheet extends React.Component {
fileName = sanitizeFilename(fileName, { replacement: "_" });
fileName = fileName + ".txt";
RNFetchBlob = (await import("rn-fetch-blob")).default;
if (Platform.OS === "android") {
let file = await ScopedStorage.createDocument(
fileName,

View File

@@ -119,7 +119,7 @@ export default function ReminderNotify({
marginTop: 10
}}
>
<Paragraph size={SIZE.xs + 1}>Remind me in:</Paragraph>
<Paragraph size={SIZE.xs}>Remind me in:</Paragraph>
{QuickActions.map((item) => {
return (
<Button
@@ -127,7 +127,7 @@ export default function ReminderNotify({
key={item.title}
title={item.title}
height={30}
fontSize={SIZE.xs + 1}
fontSize={SIZE.xs}
style={{ marginLeft: 10, borderRadius: 100 }}
onPress={() => onSnooze(item.time)}
/>
@@ -150,7 +150,7 @@ export default function ReminderNotify({
<Paragraph
style={{
color: colors.icon,
fontSize: SIZE.xs + 1,
fontSize: SIZE.xs,
marginBottom: 10
}}
>

View File

@@ -550,7 +550,7 @@ export default function ReminderSheet({
}}
>
<>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
<Paragraph size={SIZE.xs} color={colors.icon}>
{recurringMode === RecurringModes.Daily
? "Repeats daily " + `at ${dayjs(date).format("hh:mm A")}.`
: selectedDays.length === 7 &&

View File

@@ -44,7 +44,8 @@ import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet";
import Paragraph from "../../ui/typography/paragraph";
let RNFetchBlob;
import RNFetchBlob from "rn-fetch-blob";
const RestoreDataSheet = () => {
const [visible, setVisible] = useState(false);
const [restoring, setRestoring] = useState(false);
@@ -213,7 +214,6 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
return;
}
} else {
RNFetchBlob = (await import("rn-fetch-blob")).default;
let path = await storage.checkAndCreateDir("/backups/");
files = await RNFetchBlob.fs.lstat(path);
}

View File

@@ -25,7 +25,6 @@ import Navigation from "../../../services/navigation";
import { useThemeStore } from "../../../stores/use-theme-store";
import { GROUP, SORT } from "../../../utils/constants";
import { refreshNotesPage } from "../../../utils/events";
import layoutmanager from "../../../utils/layout-manager";
import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
@@ -39,7 +38,6 @@ const Sort = ({ type, screen }) => {
const updateGroupOptions = async (_groupOptions) => {
await db.settings.setGroupOptions(type, _groupOptions);
layoutmanager.withSpringAnimation(600);
setGroupOptions(_groupOptions);
setTimeout(() => {
if (screen !== "TopicSheet") Navigation.queueRoutesForUpdate(screen);

View File

@@ -40,6 +40,8 @@ import { getElevation } from "../../../utils";
import { SIZE } from "../../../utils/size";
import { IconButton } from "../icon-button";
import Paragraph from "../typography/paragraph";
import phone from "phone";
interface InputProps extends TextInputProps {
fwdRef?: RefObject<TextInput>;
validationType?:
@@ -149,7 +151,6 @@ const Input = ({
isError = customValidator && value === customValidator();
break;
case "phonenumber": {
const { default: phone } = await import("phone");
const result = phone(value, {
strictDetection: true,
validateMobilePrefix: true

View File

@@ -65,7 +65,7 @@ export const Notice = ({
flexShrink: 1
}}
selectable={selectable}
size={isSmall ? SIZE.xs + 1 : SIZE.sm}
size={isSmall ? SIZE.xs : SIZE.sm}
>
{text}
</Paragraph>

View File

@@ -19,9 +19,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { FeatureType } from "./components/sheets/new-feature";
export const features: FeatureType[] = [
{
title: "Default font size & font family",
body: "Now you can set default font size and font family in editor that will be used across all your new and old notes."
}
];
export const features: FeatureType[] = [];

View File

@@ -623,7 +623,7 @@ export const useActions = ({ close = () => null, item }) => {
},
{
id: "favorite",
title: !item.favorite ? "Favorite" : "Unfavorite",
title: item.favorite ? "Unfavorite" : "Favorite",
icon: item.favorite ? "star-off" : "star-outline",
func: addToFavorites,
close: false,
@@ -701,7 +701,7 @@ export const useActions = ({ close = () => null, item }) => {
},
{
id: "read-only",
title: "Read only",
title: "Readonly",
icon: "pencil-lock",
func: toggleReadyOnlyMode,
on: item.readonly

View File

@@ -571,8 +571,8 @@ export const useAppEvents = () => {
if (notesAddedFromIntent || shareExtensionOpened) {
let id = useEditorStore.getState().currentEditingNote;
let note = id && db.notes.note(id).data;
eSendEvent("loadingNote", note);
eSendEvent("webview_reset");
setTimeout(() => eSendEvent("loadingNote", note), 1);
MMKV.removeItem("shareExtensionOpened");
}
} catch (e) {

View File

@@ -580,22 +580,21 @@ export const useEditor = (
const onReady = useCallback(async () => {
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
overlay(true);
setLoading(true);
eSendEvent("webview_reset");
} else {
isDefaultEditor && restoreEditorState();
}
}, [overlay, isDefaultEditor, restoreEditorState]);
}, [isDefaultEditor, restoreEditorState]);
useEffect(() => {
state.current.saveCount = 0;
async () => {
(async () => {
await commands.setSessionId(sessionIdRef.current);
if (sessionIdRef.current) {
if (!state.current?.ready) return;
await onReady();
}
};
})();
}, [sessionId, loading, commands, onReady]);
const onLoad = useCallback(async () => {
@@ -636,6 +635,7 @@ export const useEditor = (
saveContent,
onContentChanged,
editorId: editorId,
markImageLoaded
markImageLoaded,
overlay
};
};

View File

@@ -51,6 +51,7 @@ export const EditorWrapper = ({ width }) => {
if (editorState().movedAway) return;
if (state === "active") {
editorController.current.onReady();
editorController.current.overlay(false);
}
};

View File

@@ -118,6 +118,7 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
get: () => {
const notebook = db.notebooks?.notebook(params?.current?.item?.id)
?.data as NotebookType;
if (!notebook) return [];
return db.relations?.from(notebook, "note");
}
});

View File

@@ -20,8 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { ToolId } from "@notesnook/editor/dist/toolbar";
import React, { RefObject } from "react";
import { View } from "react-native";
import ActionSheet from "react-native-actions-sheet";
import { ScrollView } from "react-native-gesture-handler";
import { PressableButton } from "../../../components/ui/pressable";
import { SvgView } from "../../../components/ui/svg";
import Paragraph from "../../../components/ui/typography/paragraph";
@@ -34,13 +32,14 @@ import {
getToolIcon,
getUngroupedTools
} from "./toolbar-definition";
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
export default function ToolSheet({
group,
fwdRef
}: {
group: DraggableItem;
fwdRef: RefObject<ActionSheet>;
fwdRef: RefObject<ActionSheetRef>;
}) {
const colors = useThemeStore((state) => state.colors);
const data = useDragState((state) => state.data);
@@ -50,6 +49,7 @@ export default function ToolSheet({
(item: ToolId) => {
const tool = findToolById(item);
const iconSvgString = tool ? getToolIcon(tool.icon as ToolId) : null;
if (item === "none") return;
return (
<PressableButton
key={item}
@@ -109,12 +109,7 @@ export default function ToolSheet({
padding: 12
}}
>
<ScrollView
onMomentumScrollEnd={() => {
fwdRef.current?.handleChildScrollEnd();
}}
nestedScrollEnabled={true}
>
<ScrollView nestedScrollEnabled={true}>
{!ungrouped || ungrouped.length === 0 ? (
<Paragraph
style={{

View File

@@ -109,7 +109,7 @@ const SettingsUserSection = ({ item }) => {
flexGrow: 1
}}
>
<Heading color={colors.accent} size={SIZE.xs + 1}>
<Heading color={colors.accent} size={SIZE.xs}>
{SUBSCRIPTION_STATUS_STRINGS[
user.subscription?.type
]?.toUpperCase() || "Basic"}

View File

@@ -86,8 +86,9 @@ async function getNextMonthlyReminderDate(
}
async function initDatabase(notes = true) {
if (db.isInitialized) return;
await db.initCollections();
if (!db.isInitialized) {
await db.initCollections();
}
if (notes) {
await db.notes?.init();
}
@@ -202,14 +203,15 @@ const onEvent = async ({ type, detail }: Event) => {
reply_button_text: "Take note",
reply_placeholder_text: "Write something..."
});
await initDatabase(false);
if (!db.isInitialized) await db.init();
await db.notes?.init();
await db.notes?.add({
content: {
type: "tiptap",
data: `<p>${input} </p>`
}
});
await db.notes?.init();
await db.sync(false, false);
useNoteStore.getState().setNotes();
break;
}

View File

@@ -164,7 +164,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 2044
versionCode 2045
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -4,6 +4,7 @@ package com.streetwriters.notesnook;
import android.graphics.Color;
import android.view.WindowManager;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -36,6 +37,16 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule {
}
}
@ReactMethod
public void getActivityName(Promise promise) {
try {
promise.resolve(getCurrentActivity().getClass().getSimpleName());
} catch (Exception e) {
promise.resolve(null);
}
}
@ReactMethod
public void setSecureMode(final boolean mode) {

View File

@@ -1,10 +1,6 @@
- Added default font size & font family settings
- Redesign reminder/notebook sheets to improve UX
- Fixed an issue causing sync to fail
- Fix tag suggestions in share extension
- Fix app font sizes too small in some places
- Improved mobile editor performance on low-end devices
- Fix pin to notifications not working
- Bug fixes and performance improvements
- Sync notes created from share extension & notification quick notes without requiring to launch the app
- Fix formatting is lost when sharing notes to other apps
- Fix editor would be blank after entering the app from background
- Small bug fixes and ux improvements
Thank you for using Notesnook!

View File

@@ -993,7 +993,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2037;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1067,7 +1067,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.14;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1097,7 +1097,7 @@
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2037;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1170,7 +1170,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.14;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1328,7 +1328,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2037;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1340,7 +1340,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.14;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1370,7 +1370,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2037;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1382,7 +1382,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.14;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1411,7 +1411,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2037;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1485,7 +1485,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.14;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1515,7 +1515,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2036;
CURRENT_PROJECT_VERSION = 2037;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1589,7 +1589,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.13;
MARKETING_VERSION = 2.4.14;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1,10 +1,6 @@
- Added default font size & font family settings
- Redesign reminder/notebook sheets to improve UX
- Fixed an issue causing sync to fail
- Fix tag suggestions in share extension
- Fix app font sizes too small in some places
- Improved mobile editor performance on low-end devices
- Fix pin to notifications not working
- Bug fixes and performance improvements
- Sync notes created from share extension & notification quick notes in background
- Fix formatting is lost when sharing notes to other apps
- Fix editor would be blank after entering the app from background sometimes
- Small bug fixes and UX improvements
Thank you for using Notesnook!

View File

@@ -168,6 +168,7 @@ module.exports = (env) => {
/node_modules(.*[/\\])+@microsoft/,
/node_modules(.*[/\\])+@msgpack/,
/node_modules(.*[/\\])+liqe/,
/node_modules(.*[/\\])+leac/,
/node_modules(.*[/\\])+selderee/,
/node_modules(.*[/\\])+html-to-text/,
/node_modules(.*[/\\])+buffer/,

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "2.4.13",
"version": "2.4.14",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [

View File

@@ -1,89 +0,0 @@
diff --git a/node_modules/leac/lib/leac.cjs b/node_modules/leac/lib/leac.cjs
index 13123da..9d490e2 100644
--- a/node_modules/leac/lib/leac.cjs
+++ b/node_modules/leac/lib/leac.cjs
@@ -1 +1,83 @@
-"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=/\n/g;function t(t){const o=[...t.matchAll(e)].map((e=>e.index||0));o.unshift(-1);const s=n(o,0,o.length);return e=>r(s,e)}function n(e,t,r){if(r-t==1)return{offset:e[t],index:t+1};const o=Math.ceil((t+r)/2),s=n(e,t,o),l=n(e,o,r);return{offset:s.offset,low:s,high:l}}function r(e,t){return function(e){return Object.prototype.hasOwnProperty.call(e,"index")}(e)?{line:e.index,column:t-e.offset}:r(e.high.offset<t?e.high:e.low,t)}function o(e,t){return{...e,regex:s(e,t)}}function s(e,t){if(0===e.name.length)throw new Error(`Rule #${t} has empty name, which is not allowed.`);if(function(e){return Object.prototype.hasOwnProperty.call(e,"regex")}(e))return function(e){if(e.global)throw new Error(`Regular expression /${e.source}/${e.flags} contains the global flag, which is not allowed.`);return e.sticky?e:new RegExp(e.source,e.flags+"y")}(e.regex);if(function(e){return Object.prototype.hasOwnProperty.call(e,"str")}(e)){if(0===e.str.length)throw new Error(`Rule #${t} ("${e.name}") has empty "str" property, which is not allowed.`);return new RegExp(l(e.str),"y")}return new RegExp(l(e.name),"y")}function l(e){return e.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g,"\\$&")}exports.createLexer=function(e,n="",r={}){const s="string"!=typeof n?n:r,l="string"==typeof n?n:"",c=e.map(o),i=!!s.lineNumbers;return function(e,n=0){const r=i?t(e):()=>({line:0,column:0});let o=n;const s=[];e:for(;o<e.length;){let t=!1;for(const n of c){n.regex.lastIndex=o;const c=n.regex.exec(e);if(c&&c[0].length>0){if(!n.discard){const e=r(o),t="string"==typeof n.replace?c[0].replace(new RegExp(n.regex.source,n.regex.flags),n.replace):c[0];s.push({state:l,name:n.name,text:t,offset:o,len:c[0].length,line:e.line,column:e.column})}if(o=n.regex.lastIndex,t=!0,n.push){const t=n.push(e,o);s.push(...t.tokens),o=t.offset}if(n.pop)break e;break}}if(!t)break}return{tokens:s,offset:o,complete:e.length<=o}}};
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: !0 });
+const e = /\n/g;
+function t(t) {
+ const o = [...t.matchAll(e)].map((e) => e.index || 0);
+ o.unshift(-1);
+ const s = n(o, 0, o.length);
+ return (e) => r(s, e);
+}
+function n(e, t, r) {
+ if (r - t == 1) return { offset: e[t], index: t + 1 };
+ const o = Math.ceil((t + r) / 2),
+ s = n(e, t, o),
+ l = n(e, o, r);
+ return { offset: s.offset, low: s, high: l };
+}
+function r(e, t) {
+ return (function (e) {
+ return Object.prototype.hasOwnProperty.call(e, "index");
+ })(e)
+ ? { line: e.index, column: t - e.offset }
+ : r(e.high.offset < t ? e.high : e.low, t);
+}
+function _o(e, t) {
+ return { ...e, regex: s(e, t) };
+}
+function s(e, t) {
+ if (0 === e.name.length) throw new Error(`Rule #${t} has empty name, which is not allowed.`);
+ if (
+ (function (e) {
+ return Object.prototype.hasOwnProperty.call(e, "regex");
+ })(e)
+ )
+ return (function (e) {
+ if (e.global) throw new Error(`Regular expression /${e.source}/${e.flags} contains the global flag, which is not allowed.`);
+ return e.sticky ? e : new RegExp(e.source, e.flags + "y");
+ })(e.regex);
+ if (
+ (function (e) {
+ return Object.prototype.hasOwnProperty.call(e, "str");
+ })(e)
+ ) {
+ if (0 === e.str.length) throw new Error(`Rule #${t} ("${e.name}") has empty "str" property, which is not allowed.`);
+ return new RegExp(l(e.str), "y");
+ }
+ return new RegExp(l(e.name), "y");
+}
+function l(e) {
+ return e.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g, "\\$&");
+}
+exports.createLexer = function (_e, n = "", r = {}) {
+ const s = "string" != typeof n ? n : r,
+ l = "string" == typeof n ? n : "",
+ i = !!s.lineNumbers;
+ return function (e, n = 0) {
+ const r = i ? t(e) : () => ({ line: 0, column: 0 });
+ let o = n;
+ const s = [];
+ e: for (; o < e.length; ) {
+ let t = !1;
+ let z = _e.map(_o);
+ for (const n of z) {
+ n.regex.lastIndex = o;
+ const c = n.regex.exec(e);
+ if (c && c[0].length > 0) {
+ if (!n.discard) {
+ const e = r(o),
+ t = "string" == typeof n.replace ? c[0].replace(new RegExp(n.regex.source, n.regex.flags), n.replace) : c[0];
+ s.push({ state: l, name: n.name, text: t, offset: o, len: c[0].length, line: e.line, column: e.column });
+ }
+ if (((o = n.regex.lastIndex), (t = !0), n.push)) {
+ const t = n.push(e, o);
+ s.push(...t.tokens), (o = t.offset);
+ }
+ if (n.pop) break e;
+ break;
+ }
+ }
+ if (!t) break;
+ }
+ return { tokens: s, offset: o, complete: true };
+ };
+};

View File

@@ -26,12 +26,12 @@ import {
Keyboard,
Platform,
SafeAreaView,
ScrollView,
StatusBar,
Text,
TouchableOpacity,
useWindowDimensions,
View,
ScrollView
useWindowDimensions
} from "react-native";
import {
SafeAreaProvider,
@@ -45,10 +45,9 @@ import Storage from "../app/common/database/storage";
import { eSendEvent } from "../app/services/event-manager";
import { getElevation } from "../app/utils";
import { eOnLoadNote } from "../app/utils/events";
import { sleep } from "../app/utils/time";
import { Editor } from "./editor";
import { Search } from "./search";
import { initDatabase, useShareStore } from "./store";
import { Editor } from "./editor";
const getLinkPreview = (url) => {
return getPreviewData(url, 5000);
};
@@ -63,16 +62,15 @@ async function sanitizeHtml(site) {
}
function makeHtmlFromUrl(url) {
return `<a style="overflow-wrap:anywhere;white-space:pre-wrap" href='${url}' target='_blank'>${url}</a>`;
return `<a href='${url}' target='_blank'>${url}</a>`;
}
function makeHtmlFromPlainText(text) {
if (!text) return "";
return `<p style="overflow-wrap:anywhere;white-space:pre-wrap" >${text.replace(
/(?:\r\n|\r|\n)/g,
"<br>"
)}</p>`;
return `<p>${text
.replace(/[\n]+/g, "\n")
.replace(/(?:\r\n|\r|\n)/g, "</p><p>")}</p>`;
}
function getBaseUrl(site) {
@@ -315,7 +313,6 @@ const ShareView = ({ quicknote = false }) => {
const onPress = async () => {
setLoading(true);
await initDatabase();
await sleep(1500);
if (!noteContent.current) return;
if (appendNote && !db.notes.note(appendNote.id)) {
useShareStore.getState().setAppendNote(null);
@@ -358,6 +355,7 @@ const ShareView = ({ quicknote = false }) => {
}
}
}
await db.sync(false, false);
await Storage.write("notesAddedFromIntent", "added");
close();
setLoading(false);

View File

@@ -29,10 +29,9 @@ import { db } from "../app/common/database";
export async function initDatabase() {
if (!db.isInitialized) {
// Only load collections in database.
await db.initCollections();
await db.notes.init();
await db.init();
}
await db.notes.init();
}
const StorageKeys = {

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "2.4.8",
"version": "2.4.9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "2.4.8",
"version": "2.4.9",
"dependencies": {
"diary": "^0.3.1",
"electron-updater": "^5.3.0",

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "2.4.8",
"version": "2.4.9",
"appAppleId": "1544027013",
"private": true,
"main": "./build/electron.js",

View File

@@ -22,7 +22,6 @@ import { isDevelopment, getPath } from "./utils";
import { createReadStream } from "fs";
import { extname, normalize } from "path";
import { logger } from "./logger";
import { Blob } from "buffer";
import { URL } from "url";
const FILE_NOT_FOUND = -6;
@@ -68,30 +67,7 @@ function registerProtocol() {
mimeType: extensionToMimeType[fileExtension]
});
} else {
var response;
try {
const body = await getBody(request);
response = await fetch(request.url, {
...request,
body,
headers: {
...request.headers,
origin: `${PROTOCOL}://${HOSTNAME}/`
},
redirect: "manual"
});
} catch (e) {
console.error(e);
logger.error(`Error sending request to `, request.url, "Error: ", e);
callback({ statusCode: 400 });
return;
}
callback({
statusCode: response.status,
data: response.body,
headers: Object.fromEntries(response.headers.entries()),
mimeType: response.headers.get("Content-Type")
});
protocol.uninterceptProtocol(PROTOCOL);
}
}
);
@@ -103,35 +79,11 @@ function registerProtocol() {
);
}
const bypassedRoutes = ["/notes/index_v14.json", "/notes/welcome-web"];
const bypassedRoutes = [];
function shouldInterceptRequest(url) {
let shouldIntercept = url.hostname === HOSTNAME;
return shouldIntercept && !bypassedRoutes.includes(url.pathname);
}
/**
*
* @param {Electron.ProtocolRequest} request
*/
async function getBody(request) {
/**
* @type {Electron.Session}
*/
const session = globalThis.window.webContents.session;
const blobParts = [];
if (!request.uploadData || !request.uploadData.length) return null;
for (let data of request.uploadData) {
if (data.type === "rawData") {
blobParts.push(new Uint8Array(data.bytes));
} else if (data.type === "blob") {
const buffer = await session.getBlobData(data.blobUUID);
blobParts.push(new Uint8Array(buffer));
}
}
const blob = new Blob(blobParts);
return await blob.arrayBuffer();
}
const PROTOCOL_URL = `${PROTOCOL}://${HOSTNAME}/`;
export { registerProtocol, PROTOCOL_URL };

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web",
"version": "2.4.8",
"version": "2.4.9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web",
"version": "2.4.8",
"version": "2.4.9",
"license": "GPL-3.0-or-later",
"dependencies": {
"@aws-sdk/util-base64-browser": "^3.208.0",

View File

@@ -1,7 +1,7 @@
{
"name": "@notesnook/web",
"description": "Your private note taking space",
"version": "2.4.8",
"version": "2.4.9",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",

View File

@@ -41,6 +41,8 @@ import { useCheckoutStore } from "./store";
import { getCurrencySymbol } from "./helpers";
import { Theme } from "@notesnook/theme";
import { isMacStoreApp } from "../../../utils/platform";
import { isUserSubscribed } from "../../../hooks/use-is-user-premium";
import { SUBSCRIPTION_STATUS } from "../../../common/constants";
type BuyDialogProps = {
couponCode?: string;
@@ -52,7 +54,7 @@ export function BuyDialog(props: BuyDialogProps) {
const { onClose, couponCode, plan } = props;
const theme = useTheme() as Theme;
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
useEffect(() => {
return () => {
useCheckoutStore.getState().reset();
@@ -156,13 +158,13 @@ type SideBarProps = {
};
function SideBar(props: SideBarProps) {
const { initialPlan, onClose } = props;
const [showPlans, setShowPlans] = useState(!!initialPlan);
const onPlanSelected = useCheckoutStore((state) => state.onPlanSelected);
const [showPlans, setShowPlans] = useState(false);
const onPlanSelected = useCheckoutStore((state) => state.selectPlan);
const selectedPlan = useCheckoutStore((state) => state.selectedPlan);
const pricingInfo = useCheckoutStore((state) => state.pricingInfo);
const user = useUserStore((store) => store.user);
const couponCode = useCheckoutStore((store) => store.couponCode);
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
if (user && selectedPlan)
return (
@@ -176,7 +178,13 @@ function SideBar(props: SideBarProps) {
/>
);
if (user && showPlans)
if (user && !showPlans && isUserSubscribed(user)) {
return (
<AlreadyPremium user={user} onShowPlans={() => setShowPlans(true)} />
);
}
if (user && (showPlans || !!initialPlan))
return (
<PlansList
onPlansLoaded={(plans) => {
@@ -221,7 +229,7 @@ function SideBar(props: SideBarProps) {
function Details() {
const user = useUserStore((store) => store.user);
const selectedPlan = useCheckoutStore((state) => state.selectedPlan);
const onPriceUpdated = useCheckoutStore((state) => state.onPriceUpdated);
const onPriceUpdated = useCheckoutStore((state) => state.updatePrice);
const couponCode = useCheckoutStore((store) => store.couponCode);
const setIsApplyingCoupon = useCheckoutStore(
(store) => store.setIsApplyingCoupon
@@ -347,6 +355,47 @@ function TrialOrUpgrade(props: TrialOrUpgradeProps) {
);
}
type AlreadyPremiumProps = {
user: User | undefined;
onShowPlans: () => void;
};
function AlreadyPremium(props: AlreadyPremiumProps) {
const { user, onShowPlans } = props;
const isCanceled =
user?.subscription?.type === SUBSCRIPTION_STATUS.PREMIUM_CANCELED;
return (
<>
<Rocket width={200} />
<Text variant="heading" mt={4} sx={{ textAlign: "center" }}>
Notesnook Pro
</Text>
{isCanceled ? (
<>
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
Resubscribing to Notesnook Pro will replace your existing
subscription.
</Text>
<Button
variant="primary"
mt={2}
sx={{ borderRadius: 100, px: 6 }}
onClick={onShowPlans}
data-test-id="see-all-plans"
>
Continue
</Button>
</>
) : (
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
You are already subscribed to Notesnook Pro.
</Text>
)}
</>
);
}
type SelectedPlanProps = {
plan: Plan;
pricingInfo: PricingInfo | undefined;
@@ -360,7 +409,7 @@ function SelectedPlan(props: SelectedPlanProps) {
store.setIsApplyingCoupon
]);
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
const couponInputRef = useRef<HTMLInputElement>(null);
const applyCoupon = useCallback(() => {

View File

@@ -23,13 +23,13 @@ import produce from "immer";
interface ICheckoutStore {
selectedPlan?: Plan;
onPlanSelected: (plan?: Plan) => void;
selectPlan: (plan?: Plan) => void;
pricingInfo?: PricingInfo;
onPriceUpdated: (pricingInfo?: PricingInfo) => void;
updatePrice: (pricingInfo?: PricingInfo) => void;
isApplyingCoupon: boolean;
setIsApplyingCoupon: (isApplyingCoupon: boolean) => void;
couponCode?: string;
onApplyCoupon: (couponCode?: string) => void;
applyCoupon: (couponCode?: string) => void;
reset: () => void;
}
export const useCheckoutStore = create<ICheckoutStore>((set) => ({
@@ -37,20 +37,20 @@ export const useCheckoutStore = create<ICheckoutStore>((set) => ({
pricingInfo: undefined,
couponCode: undefined,
isApplyingCoupon: false,
onPlanSelected: (plan) =>
selectPlan: (plan) =>
set(
produce((state: ICheckoutStore) => {
state.selectedPlan = plan;
state.pricingInfo = undefined;
})
),
onPriceUpdated: (pricingInfo) =>
updatePrice: (pricingInfo) =>
set(
produce((state: ICheckoutStore) => {
state.pricingInfo = pricingInfo;
})
),
onApplyCoupon: (couponCode) =>
applyCoupon: (couponCode) =>
set(
produce((state: ICheckoutStore) => {
state.couponCode = couponCode;

View File

@@ -90,13 +90,7 @@ const features: Record<FeatureKeys, Feature> = {
}
]
: isDesktop()
? [
{
title: "Default font size & font family",
subtitle:
"You can now change the default font family and size used by the editor across all your older & newer notes. Go into Settings > Editor settings to change these settings."
}
]
? []
: [],
cta: {
title: "Got it",

View File

@@ -41,3 +41,13 @@ export function isUserPremium(user?: User) {
subStatus === SUBSCRIPTION_STATUS.TRIAL
);
}
export function isUserSubscribed(user?: User) {
if (!user) user = userstore.get().user;
const subStatus = user?.subscription?.type;
return (
subStatus === SUBSCRIPTION_STATUS.PREMIUM ||
subStatus === SUBSCRIPTION_STATUS.PREMIUM_CANCELED
);
}

View File

@@ -35,7 +35,9 @@ import { EV, EVENTS, SYNC_CHECK_IDS } from "@notesnook/core/common";
import { logger } from "../utils/logger";
import Config from "../utils/config";
import { onPageVisibilityChanged } from "../utils/page-visibility";
import { NetworkCheck } from "../utils/network-check";
const networkCheck = new NetworkCheck();
var syncStatusTimeout = 0;
const BATCH_SIZE = 50;
class AppStore extends BaseStore {
@@ -128,7 +130,7 @@ class AppStore extends BaseStore {
if (type === "online") {
// a slight delay to make sure sockets are open and can be connected
// to. Otherwise, this fails miserably.
await new Promise((resolve) => setTimeout(resolve, 2000));
await networkCheck.waitForInternet();
}
await db.connectSSE({ force: type === "online" }).catch(logger.error);
} else if (type === "offline") {

View File

@@ -0,0 +1,38 @@
/*
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/>.
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import Worker from "worker-loader?filename=static/workers/network-check.worker.[contenthash].js!./network-check.worker";
import type { NetworkCheck as NetworkWorker } from "./network-check.worker";
import { wrap, Remote } from "comlink";
export class NetworkCheck {
private worker!: globalThis.Worker;
private network!: Remote<NetworkWorker>;
constructor() {
this.worker = new Worker();
this.network = wrap<NetworkWorker>(this.worker);
}
waitForInternet() {
return this.network.waitForInternet();
}
}

View File

@@ -0,0 +1,40 @@
/*
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 { expose } from "comlink";
const module = {
async waitForInternet() {
let retries = 10;
while (retries-- > 0) {
try {
const response = await fetch("https://api.notesnook.com/health");
if (response.ok) return true;
} catch {
// ignore
}
// wait a bit before trying again.
await new Promise((resolve) => setTimeout(resolve, 2500));
}
return false;
}
};
expose(module);
export type NetworkCheck = typeof module;

View File

@@ -1019,13 +1019,14 @@ function openURL(url: string, force?: boolean) {
function maskEmail(email: string) {
if (!email) return "";
const [username, domain] = email.split("@");
const maskChars = "*".repeat(
username.substring(2, username.length - 2).length
);
return `${username.substring(0, 2)}${maskChars}${username.substring(
username.length - 2
)}@${domain}`;
const [username, provider] = email.split("@");
if (username.length === 1) return `****@${provider}`;
return email.replace(/(.{1})(.*)(?=@)/, function (gp1, gp2, gp3) {
for (let i = 0; i < gp3.length; i++) {
gp2 += "*";
}
return gp2;
});
}
function isSessionExpired() {

View File

@@ -29,13 +29,13 @@ export default class Subscriptions {
}
async cancel() {
const token = this._tokenManager.getAccessToken();
const token = await this._tokenManager.getAccessToken();
if (!token) return;
await http.delete(`${hosts.SUBSCRIPTIONS_HOST}/subscriptions`, token);
}
async updateUrl() {
const token = this._tokenManager.getAccessToken();
const token = await this._tokenManager.getAccessToken();
if (!token) return;
return await http.get(
`${hosts.SUBSCRIPTIONS_HOST}/subscriptions/update_url`,

View File

@@ -24,6 +24,7 @@ import { getContentFromData } from "../content-types";
import { CHECK_IDS, checkIsUserPremium } from "../common";
import { addItem, deleteItem } from "../utils/array";
import { formatDate } from "../utils/date";
import qclone from "qclone";
export default class Note {
/**
@@ -135,12 +136,14 @@ export default class Note {
async duplicate() {
const content = await this._db.content.raw(this._note.contentId);
return await this._db.notes.add({
...this._note,
...qclone(this._note),
id: undefined,
content: {
type: content.type,
data: content.data
},
content: content
? {
type: content.type,
data: content.data
}
: undefined,
readonly: false,
favorite: false,
pinned: false,

View File

@@ -18,10 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Flex } from "@theme-ui/components";
import {
ReactNodeView,
ReactNodeViewProps,
} from "../react";
import { ReactNodeView, ReactNodeViewProps } from "../react";
import { Node as ProsemirrorNode } from "prosemirror-model";
import { Editor } from "../../types";
import { Editor as TiptapEditor } from "@tiptap/core";
@@ -263,7 +260,7 @@ function TableColumnToolbar(props: TableToolbarProps) {
>
<TableProperties
editor={editor}
title="tableProperties"
title="Table properties"
icon="more"
variant={"small"}
/>

View File

@@ -64,6 +64,7 @@ export function Dropdown(props: DropdownProps) {
m: 0,
bg: isPopupOpen ? "hover" : "transparent",
mr: 1,
flexShrink: 0,
display: "flex",
alignItems: "center",
":hover": { bg: "hover" },
@@ -75,7 +76,9 @@ export function Dropdown(props: DropdownProps) {
onMouseDown={(e) => e.preventDefault()}
>
{typeof selectedItem === "string" ? (
<Text sx={{ fontSize: "subBody", mr: 1, color: "text" }}>
<Text
sx={{ fontSize: "subBody", mr: 1, color: "text", flexShrink: 0 }}
>
{selectedItem}
</Text>
) : (

View File

@@ -54,13 +54,12 @@ const clean: ThemeUIStyleObject = {
const error: ThemeUIStyleObject = {
variant: "forms.input",
boxShadow: "0px 0px 0px 1px var(--error) inset",
outline: "none",
outline: "1.5px solid var(--error)",
":focus": {
boxShadow: "0px 0px 0px 1.5px var(--error) inset"
outline: "2px solid var(--error)"
},
":hover:not(:focus)": {
boxShadow: "0px 0px 0px 1px var(--error) inset"
outline: "1.5px solid var(--error)"
}
};