mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 19:49:54 +02:00
Compare commits
15 Commits
v3.3.15
...
mobile/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04f6e1068b | ||
|
|
9bea4adcf9 | ||
|
|
a5790de23c | ||
|
|
eb4edeea8f | ||
|
|
e3ccd504da | ||
|
|
4627d7d5df | ||
|
|
f216c1b65d | ||
|
|
4692ba7183 | ||
|
|
543825918c | ||
|
|
9f995b861b | ||
|
|
9963a6c655 | ||
|
|
1696ec5c72 | ||
|
|
7c95faad21 | ||
|
|
ca80441c14 | ||
|
|
ef5e5e5b4c |
@@ -198,6 +198,9 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
toolsets: {
|
||||
appimage: "1.0.2"
|
||||
},
|
||||
snap: {
|
||||
autoStart: false,
|
||||
confinement: "strict",
|
||||
|
||||
2128
apps/desktop/package-lock.json
generated
2128
apps/desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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!
|
||||
@@ -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(), "")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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?.()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 } },
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
3
fastlane/metadata/android/en-US/changelogs/15504.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/15504.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Bug fixes and improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -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\""
|
||||
|
||||
@@ -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\""
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 ? ">" : " ";
|
||||
|
||||
Reference in New Issue
Block a user