Compare commits

...

15 Commits

Author SHA1 Message Date
Ammar Ahmed
04f6e1068b mobile: fix saving incoming note in merge conflict dialog does not work 2026-04-23 10:43:07 +05:00
01zulfi
9bea4adcf9 web: show titlebar on auth pages (#9093)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-04-22 13:51:21 +05:00
Chloe Oletto
a5790de23c desktop: Stop urgent reminder causes segfault & app freeze on Linux. (#9737)
* desktop: Stop urgent reminder causes segfault & app freeze on Linux.

Signed-off by: Chloe Oletto <NeedsChloesure@riseup.net>

* desktop: Linux segfault
Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>

* Apply suggestion from @thecodrr

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>

---------

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>
Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>
2026-04-22 11:56:47 +05:00
Abdullah Atta
eb4edeea8f web: fix crash on non-paddle related iframe event (#9747)
Stack trace:
```
undefined is not an object (evaluating 'w.data')
E@https://app.notesnook.com/assets/paddle-Z43isEce-h-M.js:23:45428
E@https://app.notesnook.com/assets/paddle-Z43isEce-h-M.js:23:45846
```
2026-04-22 09:46:29 +05:00
01zulfi
e3ccd504da ui: fix submenu cutting off when there are a lot of lazy items (#9739)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-04-22 09:11:28 +05:00
Ammar Ahmed
4627d7d5df Merge pull request #9736 from streetwriters/mobile/release-3.3.20
mobile: release 3.3.20
2026-04-22 08:46:43 +05:00
01zulfi
f216c1b65d web: fix scrollbar blocking date in compact note list item (#9719)
* web: fix scrollbar blocking date in compact note list item
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>

* web: adjust notes list scrollbar width, compact item padding, & group header padding
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-04-21 11:08:30 +05:00
Abdullah Atta
4692ba7183 desktop: use newer appimage bundler (#9744)
this should fix libfuse and libz errors
2026-04-21 10:55:52 +05:00
Ammar Ahmed
543825918c mobile: fix useEffect deps in use-feature-manager hook 2026-04-21 08:46:19 +05:00
Ammar Ahmed
9f995b861b mobile: fix toast position incorrect with keyboard opened 2026-04-21 08:45:45 +05:00
Ammar Ahmed
9963a6c655 mobile: delete vault action keeps loading when entering incorrect password 2026-04-21 08:45:29 +05:00
Ammar Ahmed
1696ec5c72 mobile: fix unable to switch between yearly/monthly plans 2026-04-21 08:14:38 +05:00
Ammar Ahmed
7c95faad21 mobile: fix clear trash option is disabled on login 2026-04-20 15:43:39 +05:00
Ammar Ahmed
ca80441c14 mobile: fix trash count does not update on calling clear trash 2026-04-20 15:30:26 +05:00
Ammar Ahmed
ef5e5e5b4c mobile: release 3.3.20 2026-04-20 12:03:12 +05:00
33 changed files with 1458 additions and 1074 deletions

View File

@@ -198,6 +198,9 @@ module.exports = {
}
}
},
toolsets: {
appimage: "1.0.2"
},
snap: {
autoStart: false,
confinement: "strict",

File diff suppressed because it is too large Load Diff

View File

@@ -48,7 +48,7 @@
"@types/yargs": "^17.0.33",
"chokidar": "^4.0.3",
"electron": "^37.0.0",
"electron-builder": "^26.0.12",
"electron-builder": "^26.8.1",
"esbuild": "0.21.5",
"node-abi": "^4.5.0",
"node-gyp-build": "^4.8.4",

View File

@@ -178,7 +178,9 @@ export const osIntegrationRouter = t.router({
})
});
notification.show();
if (input.urgency === "critical") {
if (input.urgency === "critical" && process.platform !== "linux") {
// due to an Electron bug in versions below 40.x, shell.beep() causes a segfault on Linux.
// TODO: Remove when migrating to Electron 40.x or newer.
shell.beep();
}

View File

@@ -140,7 +140,7 @@ android {
if (project.hasProperty("prBuildNumber")) {
versionCode Integer.parseInt(prBuildNumber())
} else {
versionCode 3099
versionCode 3100
}
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')

View File

@@ -1,8 +1,3 @@
- Fix clearing note title adds generated title instead of staying empty
- Improved table cell selection, table scrolling, and table cell editing on mobile
- Fixed app on launch randomly focuses editor instead of notes list
- Fixed file size check shows error but continues adding the file to editor
- Note title headline format should get content from first paragraph
- Minor bug fixes and performance improvements
- Bug fixes and improvements
Thank you for using Notesnook!
Thank you for using Notesnook!

View File

@@ -194,6 +194,7 @@ export const VaultDialog: React.FC = () => {
close();
}, 100);
} else {
setLoading(false);
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
@@ -227,6 +228,10 @@ export const VaultDialog: React.FC = () => {
setLoading(false);
close();
eSendEvent("vaultUpdated");
ToastManager.show({
message: strings.vaultCleared(),
type: "success"
});
} catch (e) {
ToastManager.show({
heading: strings.passwordIncorrect(),
@@ -741,7 +746,8 @@ export const VaultDialog: React.FC = () => {
width: DDS.isTab ? 350 : "85%",
borderRadius: 10,
backgroundColor: colors.primary.background,
paddingTop: 12
paddingTop: 12,
overflow: "hidden"
}}
>
<DialogHeader
@@ -808,7 +814,8 @@ export const VaultDialog: React.FC = () => {
!isBiometryAvailable ||
isCreateVault ||
isChangePassword ||
isCustomAction ? null : (
isCustomAction ||
isDeleteVault ? null : (
<Button
onPress={() =>
onPressFingerprintAuth(strings.unlockNote(), "")

View File

@@ -76,7 +76,9 @@ const MergeConflicts = () => {
const applyChanges = async () => {
const contentToSave = selectedContent;
if (!contentToSave) return;
const note = await db.notes.note(contentToSave.noteId);
const note = await db.notes.note(
(content.current as UnencryptedContentItem).noteId
);
if (!note) return;
await db.notes.add({
id: note.id,

View File

@@ -18,13 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getFeaturesTable } from "@notesnook/common";
import {
EVENTS,
Plan,
SKUResponse,
SubscriptionPlan,
User
} from "@notesnook/core";
import { EVENTS, Plan, SubscriptionPlan, User } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
@@ -54,6 +48,7 @@ import {
TECHLORE_SVG,
XDA_SVG
} from "../../assets/images/assets";
import { db } from "../../common/database";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import usePricingPlans, {
PlanOverView,
@@ -75,7 +70,6 @@ import { IconButton } from "../ui/icon-button";
import { SvgView } from "../ui/svg";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { db } from "../../common/database";
const Steps = {
select: 1,
@@ -935,7 +929,10 @@ const PricingPlanCard = ({
setStep: (step: number) => void;
}) => {
const { colors } = useThemeColors();
const [regionalDiscount, setRegionaDiscount] = useState<SKUResponse>();
const regionalDiscount =
annualBilling && plan.id === "pro"
? pricingPlans?.regionalDiscount
: undefined;
const { width } = useWindowDimensions();
const isTablet = width > 600;
@@ -958,26 +955,6 @@ const PricingPlanCard = ({
annualBilling
);
useEffect(() => {
if (pricingPlans?.isGithubRelease || !annualBilling) return;
pricingPlans
?.getRegionalDiscount(
plan.id,
pricingPlans.isGithubRelease
? (WebPlan?.period as string)
: `notesnook.${plan.id}.${annualBilling ? "yearly" : "monthly"}`
)
.then((value) => {
setRegionaDiscount(value);
});
}, [WebPlan?.period, annualBilling, plan.id, pricingPlans]);
useEffect(() => {
if (!annualBilling) {
setRegionaDiscount(undefined);
}
}, [annualBilling]);
const isSubscribed =
product?.productId &&
pricingPlans?.user?.subscription?.productId?.includes(plan.id) &&

View File

@@ -16,11 +16,11 @@ 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 { Plan, SKUResponse } from "@notesnook/core";
import { Plan } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import dayjs from "dayjs";
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import {
Linking,
Platform,
@@ -330,7 +330,10 @@ const ProductItem = (props: {
productId: string;
}) => {
const { colors } = useThemeColors();
const [regionalDiscount, setRegionaDiscount] = useState<SKUResponse>();
const regionalDiscount =
props.productId === "notesnook.pro.yearly"
? props.pricingPlans.regionalDiscount
: undefined;
const product =
props.pricingPlans?.currentPlan?.subscriptions?.[
regionalDiscount?.sku || props.productId
@@ -367,30 +370,6 @@ const ProductItem = (props: {
props.pricingPlans.user?.subscription?.productId ===
(product as Plan)?.id);
useEffect(() => {
if (product) {
props.pricingPlans
?.getRegionalDiscount(
props.pricingPlans.currentPlan?.id as string,
props.pricingPlans.isGithubRelease
? ((product as Plan)?.period as string)
: props.productId
)
.then((value) => {
if (
value &&
value.sku?.startsWith(
(props.pricingPlans.selectedProduct as RNIap.Subscription)
?.productId
)
) {
props.pricingPlans.selectProduct(value?.sku as string);
}
setRegionaDiscount(value);
});
}
}, [product, props.pricingPlans, props.productId]);
const discountValue =
(isAnnual && !isGithubRelease) ||
(isGithubRelease && (product as Plan)?.discount?.amount)

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useAreFeaturesAvailable } from "@notesnook/common";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import React, { useEffect } from "react";
import { View } from "react-native";
import {
eSendEvent,
@@ -28,7 +28,11 @@ import {
ToastManager
} from "../../../services/event-manager";
import SettingsService from "../../../services/settings";
import { eAfterSync, eCloseSheet } from "../../../utils/events";
import {
eAfterSync,
eCloseSheet,
eMenuItemUpdate
} from "../../../utils/events";
import { SideMenuItem } from "../../../utils/menu-items";
import { AppFontSize } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
@@ -40,14 +44,22 @@ import PaywallSheet from "../paywall";
import { presentDialog } from "../../dialog/functions";
import { db } from "../../../common/database";
import { useTrashStore } from "../../../stores/use-trash-store";
import { useSettingStore } from "../../../stores/use-setting-store";
export const MenuItemProperties = ({ item }: { item: SideMenuItem }) => {
const { colors } = useThemeColors();
const featuresAvailable = useAreFeaturesAvailable([
"customHomepage",
"customizableSidebar"
]);
const isAppLoading = useSettingStore((state) => state.isAppLoading);
const trash = useTrashStore((state) => state.items);
useEffect(() => {
if (!isAppLoading) {
useTrashStore.getState().refresh();
}
}, [isAppLoading]);
return !featuresAvailable ? null : (
<View
style={{
@@ -121,6 +133,7 @@ export const MenuItemProperties = ({ item }: { item: SideMenuItem }) => {
positivePress: async () => {
await db.trash.clear();
useTrashStore.getState().clear();
eSendEvent(eMenuItemUpdate);
eSendEvent(eAfterSync);
ToastManager.show({
message: strings.trashCleared(),

View File

@@ -71,7 +71,7 @@ export function MenuItem({
if (item.data) {
getTotalNotesRef.current([item.data?.id]);
}
}, [update]);
}, [item.data, update]);
useEffect(() => {
const onSyncComplete = async () => {
@@ -94,7 +94,9 @@ export function MenuItem({
setItemCount(await db.notes.archived.count());
break;
case "Trash":
setItemCount((await db.trash.all()).length);
{
setItemCount((await db.trash.all()).length);
}
break;
}
} else {

View File

@@ -38,10 +38,10 @@ import {
import { getElevationStyle } from "../../utils/elevation";
import { eHideToast, eShowToast } from "../../utils/events";
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { DefaultAppStyles } from "../../utils/styles";
export const Toast = ({ context = "global" }) => {
const { colors, isDark } = useThemeColors();
@@ -119,10 +119,7 @@ export const Toast = ({ context = "global" }) => {
width: DDS.isTab ? dimensions.width / 2 : "100%",
alignItems: "center",
alignSelf: "center",
bottom:
insets.bottom +
15 +
(keyboard.keyboardShown ? Math.max(0, keyboard.keyboardHeight) : 0),
bottom: insets.bottom + 15,
position: "absolute",
zIndex: 999,
elevation: 15

View File

@@ -37,11 +37,10 @@ export default function useFeatureManager() {
"disableTrashCleanup",
"fullOfflineMode"
]);
const user = useUserStore((state) => state.user);
const plan = useUserStore((state) => state.user?.subscription?.plan);
useEffect(() => {
if (!user || !features) return;
if (!useUserStore.getState().user || !features) return;
if (!features.createNoteFromNotificationDrawer.isAllowed) {
SettingsService.setProperty("notifNotes", false);
@@ -82,7 +81,8 @@ export default function useFeatureManager() {
db.settings.setDefaultTag(undefined);
}
}
}, [features, plan, user]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [features, plan]);
return true;
}

View File

@@ -16,16 +16,17 @@ 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 { Plan, SubscriptionPlan, SubscriptionPlanId } from "@notesnook/core";
import { useEffect, useState } from "react";
import { Plan, SubscriptionPlan } from "@notesnook/core";
import React, { useEffect, useState } from "react";
import { useAsync } from "react-async-hook";
import { Platform } from "react-native";
import Config from "react-native-config";
import * as RNIap from "react-native-iap";
import { DatabaseLogger, db } from "../common/database";
import PremiumService from "../services/premium";
import SettingsService from "../services/settings";
import { useSettingStore } from "../stores/use-setting-store";
import { useUserStore } from "../stores/use-user-store";
import SettingsService from "../services/settings";
function numberWithCommas(x: string) {
const parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
@@ -155,9 +156,18 @@ const planIdToIndex = (planId: string) => {
return planIndex;
};
let WebPlanCache: Plan[];
const isGithubRelease = Config.GITHUB_RELEASE === "true";
const usePricingPlans = (options?: PricingPlansOptions) => {
const isGithubRelease = Config.GITHUB_RELEASE === "true";
const user = useUserStore((state) => state.user);
const regionalDiscount = useAsync(
() =>
db.pricing.sku(
Platform.OS === "android" ? "google" : "apple",
"yearly",
"pro"
),
[]
);
const [currentPlan, setCurrentPlan] = useState<string>(
options?.planId || pricingPlans[2].id
);
@@ -172,17 +182,20 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
const [userCanRequestTrial, setUserCanRequestTrial] = useState(false);
const [webPricingPlans, setWebPricingPlans] = useState<Plan[]>([]);
const getProduct = (planId: string, skuId: string) => {
if (isGithubRelease)
return webPricingPlans.find(
(plan) => planIdToIndex(planId) === plan.plan && skuId === plan.period
);
const getProduct = React.useCallback(
(planId: string, skuId: string) => {
if (isGithubRelease)
return webPricingPlans.find(
(plan) => planIdToIndex(planId) === plan.plan && skuId === plan.period
);
return (
plans.find((p) => p.id === planId)?.subscriptions?.[skuId] ||
plans.find((p) => p.id === planId)?.products?.[skuId]
);
};
return (
plans.find((p) => p.id === planId)?.subscriptions?.[skuId] ||
plans.find((p) => p.id === planId)?.products?.[skuId]
);
},
[plans, webPricingPlans]
);
const getProductAndroid = (planId: string, skuId: string) => {
if (isGithubRelease)
@@ -196,37 +209,45 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
return getProduct(planId, skuId) as RNIap.SubscriptionIOS;
};
const hasTrialOffer = (planId?: string, productId?: string) => {
if (!selectedProductSku && !productId) return false;
const hasTrialOffer = React.useCallback(
(planId?: string, productId?: string) => {
if (!selectedProductSku && !productId) return false;
if (productId?.includes("5year")) return false;
if (isGithubRelease) {
if (
user?.subscription?.trialsAvailed?.some(
(plan) => plan === planIdToIndex(planId || currentPlan)
)
) {
return false;
} else {
return true;
if (productId?.includes("5year")) return false;
if (isGithubRelease) {
if (
user?.subscription?.trialsAvailed?.some(
(plan) => plan === planIdToIndex(planId || currentPlan)
)
) {
return false;
} else {
return true;
}
}
}
return Platform.OS === "ios"
? (
getProduct(
planId || currentPlan,
productId || selectedProductSku
) as RNIap.SubscriptionIOS
)?.introductoryPricePaymentModeIOS === "FREETRIAL"
: (
getProduct(
planId || currentPlan,
productId || selectedProductSku
) as RNIap.SubscriptionAndroid
)?.subscriptionOfferDetails?.[0]?.pricingPhases?.pricingPhaseList
?.length > 1;
};
return Platform.OS === "ios"
? (
getProduct(
planId || currentPlan,
productId || selectedProductSku
) as RNIap.SubscriptionIOS
)?.introductoryPricePaymentModeIOS === "FREETRIAL"
: (
getProduct(
planId || currentPlan,
productId || selectedProductSku
) as RNIap.SubscriptionAndroid
)?.subscriptionOfferDetails?.[0]?.pricingPhases?.pricingPhaseList
?.length > 1;
},
[
currentPlan,
getProduct,
selectedProductSku,
user?.subscription?.trialsAvailed
]
);
// user && (!user.subscription || !user.subscription.expiry) ? true : false;
@@ -649,21 +670,6 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
);
};
async function getRegionalDiscount(plan: string, productId: string) {
if (productId !== "notesnook.pro.yearly") {
return;
}
try {
return await db.pricing.sku(
Platform.OS === "android" ? "google" : "apple",
"yearly",
plan as SubscriptionPlanId
);
} catch (e) {
console.log(e);
}
}
function isSubscribedToPlan(planId: string) {
if (!PremiumService.get()) return false;
return user?.subscription?.productId?.includes(planId);
@@ -734,7 +740,7 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
(plan) => plan.plan === planIndex && plan.period === period
);
},
getRegionalDiscount,
regionalDiscount: regionalDiscount.result,
isGithubRelease: isGithubRelease,
isSubscribed: () => user?.subscription?.plan !== SubscriptionPlan.FREE,
finish: () => options?.onBuy?.()

View File

@@ -1,6 +1,6 @@
// Production iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2179
IOS_MARKETING_VERSION = 3.3.19
IOS_CURRENT_PROJECT_VERSION = 2180
IOS_MARKETING_VERSION = 3.3.20
IOS_MAIN_BUNDLE_ID = org.streetwriters.notesnook
IOS_WIDGET_BUNDLE_ID = org.streetwriters.notesnook.notewidget
IOS_SHARE_BUNDLE_ID = org.streetwriters.notesnook.share

View File

@@ -1,6 +1,6 @@
// Production iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2179
IOS_MARKETING_VERSION = 3.3.19
IOS_CURRENT_PROJECT_VERSION = 2180
IOS_MARKETING_VERSION = 3.3.20
IOS_MAIN_BUNDLE_ID = org.streetwriters.notesnook
IOS_WIDGET_BUNDLE_ID = org.streetwriters.notesnook.notewidget
IOS_SHARE_BUNDLE_ID = org.streetwriters.notesnook.share

View File

@@ -1,6 +1,6 @@
// Staging iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2179
IOS_MARKETING_VERSION = 3.3.19
IOS_CURRENT_PROJECT_VERSION = 2180
IOS_MARKETING_VERSION = 3.3.20
IOS_MAIN_BUNDLE_ID = org.streetwriters.notesnook
IOS_WIDGET_BUNDLE_ID = org.streetwriters.notesnook.notewidget
IOS_SHARE_BUNDLE_ID = org.streetwriters.notesnook.share

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.3.19",
"version": "3.3.20",
"private": true,
"license": "GPL-3.0-or-later",
"scripts": {
@@ -202,4 +202,4 @@
"engines": {
"node": ">=20"
}
}
}

View File

@@ -324,6 +324,7 @@ function GroupHeader(props: GroupHeaderProps) {
cursor: "pointer",
px: 1,
py: 1,
pr: 2,
borderBottom: "1px solid var(--border)",
// border: isMenuTarget ? "1px solid" : "none",
// borderColor: isMenuTarget ? "accent" : "transparent",

View File

@@ -55,6 +55,8 @@ export const CustomScrollbarsVirtualList = forwardRef<
return (
<ScrollContainer
{...props}
trackStyle={() => ({ width: 6 })}
thumbStyle={() => ({ width: 6 })}
forwardedRef={(sRef) => {
if (typeof ref === "function") ref(sRef);
else if (ref) ref.current = sRef;

View File

@@ -148,6 +148,7 @@ function ListItem<TItem extends Item, TContext>(
sx={{
px: 1,
py: isCompact ? 0 : 1,
pr: isCompact ? 10 : 0,
height: isCompact ? 25 : "inherit",
cursor: "pointer",
position: "relative",

View File

@@ -713,7 +713,7 @@ function colorsToMenuItems(
const isChecked = !!noteColor && noteColor.id === color.id;
return {
type: "button",
key: color.title,
key: color.id,
title: color.title,
icon: Circle.path,
styles: { icon: { color: color.colorCode } },

View File

@@ -68,13 +68,23 @@ export function getWindowControls(
];
}
export const TITLE_BAR_HEIGHT = 37;
export function TitleBar({ isUnderlay = isMac() }: { isUnderlay?: boolean }) {
export function TitleBar({
isUnderlay = isMac(),
force = false
}: {
isUnderlay?: boolean;
force?: boolean;
}) {
const { isFullscreen, hasNativeWindowControls, isMaximized } =
useWindowControls();
const isTablet = useTablet();
const isMobile = useMobile();
if ((!isMac() && !isMobile && !isTablet) || (isFullscreen && isMac()))
if (
!force &&
((!isMac() && !isMobile && !isTablet) || (isFullscreen && isMac()))
) {
return null;
}
const tools = getWindowControls(
hasNativeWindowControls,

View File

@@ -128,7 +128,10 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
return;
}
if (!callback_data.data || SUBSCRIBED_EVENTS.indexOf(event_name) === -1) {
if (
!callback_data?.data ||
SUBSCRIBED_EVENTS.indexOf(event_name) === -1
) {
return;
}

View File

@@ -47,7 +47,7 @@ export interface Checkout {
export type PaddleEvent = {
action: "event";
event_name: CheckoutEventNames;
callback_data: PaddleEventData;
callback_data?: PaddleEventData;
};
export type PlanMetadata = Record<

View File

@@ -28,7 +28,7 @@ import {
import { desktop } from "./common/desktop-bridge";
import { useKeyStore } from "./interfaces/key-store";
import { usePromise } from "@notesnook/common";
import { AuthProps } from "./views/auth";
import { AuthProps, isAuthRouteActive } from "./views/auth";
import { loadDatabase } from "./hooks/use-database";
import AppLock from "./views/app-lock";
import { Text } from "@theme-ui/components";
@@ -57,7 +57,7 @@ export async function startApp(children?: React.ReactNode) {
root.render(
<>
<TitleBar />
<TitleBar force={isAuthRouteActive()} />
<ErrorBoundary>
<GlobalErrorHandler>
<BaseThemeProvider

View File

@@ -30,7 +30,12 @@ import {
Warn
} from "../components/icons";
import Field, { FieldProps } from "../components/field";
import { getQueryParams, hardNavigate, makeURL } from "../navigation";
import {
getCurrentPath,
getQueryParams,
hardNavigate,
makeURL
} from "../navigation";
import { store as userstore } from "../stores/user-store";
import { db } from "../common/db";
import Config from "../utils/config";
@@ -163,6 +168,11 @@ const routePaths: Record<AuthRoutes, string> = {
signup: "/signup"
};
export function isAuthRouteActive() {
const path = getCurrentPath();
return Object.values(routePaths).includes(path);
}
const authorizedRoutes: AuthRoutes[] = [
"login:email",
"login:password",

View File

@@ -0,0 +1,3 @@
- Bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -6,6 +6,12 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: en\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Plural-Forms: \n"
#: src/strings.ts:2411
msgid " \"Notebook > Notes\""

View File

@@ -6,6 +6,12 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: pseudo-LOCALE\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Plural-Forms: \n"
#: src/strings.ts:2411
msgid " \"Notebook > Notes\""

View File

@@ -21,8 +21,8 @@ import React, {
useCallback,
useRef,
useEffect,
PropsWithChildren,
useState
useState,
PropsWithChildren
} from "react";
import { Box, FlexProps, Text } from "@theme-ui/components";
import { getPosition } from "../../utils/position.js";
@@ -68,6 +68,7 @@ export function Menu(props: MenuProps) {
const focusedItem = items[focusIndex];
const subMenuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const item = items[focusIndex];
if (!item || !subMenuRef.current) return;
@@ -80,15 +81,23 @@ export function Menu(props: MenuProps) {
return;
}
const { top, left } = getPosition(subMenuRef.current, {
// yOffset: menuItemElement.offsetHeight,
target: menuItemElement,
location: "right"
});
function repositionSubmenu() {
if (!subMenuRef.current || !menuItemElement) return;
const { top, left } = getPosition(subMenuRef.current, {
target: menuItemElement,
location: "right"
});
subMenuRef.current.style.top = `${top}px`;
subMenuRef.current.style.left = `${left}px`;
}
repositionSubmenu();
subMenuRef.current.style.visibility = "visible";
subMenuRef.current.style.top = `${top}px`;
subMenuRef.current.style.left = `${left}px`;
const observer = new ResizeObserver(() => repositionSubmenu());
observer.observe(subMenuRef.current);
return () => observer.disconnect();
}, [isSubmenuOpen, focusIndex, items]);
return (

View File

@@ -209,14 +209,21 @@ Options:
// ---------------------------------------------------------------------------
// Stack frame parsing
//
// Handles frames like:
// Handles frames like (V8/Node):
// at XB (https://example.com/assets/app-HASH.js:136:27466)
// at https://example.com/assets/app-HASH.js:12:345 (anonymous)
// at https://example.com/assets/app-HASH.js:12:345
// at+XB+(https://example.com/assets/app-HASH.js:136:27466) (URL-encoded)
// And Safari/WebKit:
// E@https://example.com/assets/app-HASH.js:23:45428
// @https://example.com/assets/app-HASH.js:23:45428 (anonymous)
// ---------------------------------------------------------------------------
// Groups:
// V8 named: m[1]=symbol m[2]=url m[3]=line m[4]=col
// V8 anonymous: m[5]=url m[6]=line m[7]=col
// Safari: m[8]=symbol m[9]=url m[10]=line m[11]=col
const FRAME_RE =
/at\s+(?:async\s+)?(\S+)\s+\(?(https?:\/\/[^\s):]+):(\d+):(\d+)\)?|at\s+(?:async\s+)?(https?:\/\/[^\s):]+):(\d+):(\d+)/g;
/at\s+(?:async\s+)?(\S+)\s+\(?(https?:\/\/[^\s):]+):(\d+):(\d+)\)?|at\s+(?:async\s+)?(https?:\/\/[^\s):]+):(\d+):(\d+)|(\S*)@(https?:\/\/[^\s:)]+):(\d+):(\d+)/g;
function parseStackTrace(raw) {
// Replace URL-encoded + with spaces so the regex works uniformly
@@ -233,7 +240,7 @@ function parseStackTrace(raw) {
}
if (m[2]) {
// "at symbol (url:line:col)"
// V8: "at symbol (url:line:col)"
frames.push({
raw: line,
parsed: true,
@@ -242,8 +249,8 @@ function parseStackTrace(raw) {
line: parseInt(m[3], 10),
col: parseInt(m[4], 10)
});
} else {
// "at url:line:col"
} else if (m[5]) {
// V8: "at url:line:col"
frames.push({
raw: line,
parsed: true,
@@ -252,6 +259,16 @@ function parseStackTrace(raw) {
line: parseInt(m[6], 10),
col: parseInt(m[7], 10)
});
} else {
// Safari/WebKit: "symbol@url:line:col"
frames.push({
raw: line,
parsed: true,
symbol: m[8] || null,
url: m[9],
line: parseInt(m[10], 10),
col: parseInt(m[11], 10)
});
}
}
@@ -338,7 +355,10 @@ function tryDemangleDotted(consumer, generatedLine, generatedCol, symbol) {
let col = generatedCol;
for (let i = parts.length - 1; i >= 0; i--) {
const pos = consumer.originalPositionFor({ line: generatedLine, column: col });
const pos = consumer.originalPositionFor({
line: generatedLine,
column: col
});
if (pos.name) resolved[i] = pos.name;
// Step back past this identifier and the dot that precedes it
col -= parts[i].length + 1;
@@ -507,7 +527,13 @@ async function main() {
console.log(` at ${name} (${src}:${pos.line}:${pos.column})`);
// Source context
const ctx = getSourceContext(consumer, pos.source, pos.line, pos.column, opts.context);
const ctx = getSourceContext(
consumer,
pos.source,
pos.line,
pos.column,
opts.context
);
if (ctx) {
for (const l of ctx) {
const gutter = l.isTarget ? ">" : " ";