mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 11:39:21 +02:00
Compare commits
25 Commits
2.6.10-and
...
fix-logger
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbcef9d44a | ||
|
|
b9aa49e17a | ||
|
|
bab65528db | ||
|
|
4a743d98b7 | ||
|
|
4b44c39ec8 | ||
|
|
a7d85cb3f6 | ||
|
|
50137e0994 | ||
|
|
8b5babd5f8 | ||
|
|
39e83200c8 | ||
|
|
ead0e0107e | ||
|
|
1c888bd4fd | ||
|
|
ff92530339 | ||
|
|
ee38032403 | ||
|
|
2ce245659a | ||
|
|
2c635eb4d7 | ||
|
|
d653223df0 | ||
|
|
290b0dfc78 | ||
|
|
06bb9e654b | ||
|
|
576fe78e33 | ||
|
|
0c16bfb5ff | ||
|
|
a101dbdf31 | ||
|
|
682609044f | ||
|
|
c0a7fb3a5c | ||
|
|
a153aa791c | ||
|
|
76b3dd1f96 |
4
apps/desktop/package-lock.json
generated
4
apps/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.6.9",
|
||||
"version": "2.6.11",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.6.9",
|
||||
"version": "2.6.11",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.6.9",
|
||||
"version": "2.6.11",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { protocol, net } from "electron";
|
||||
import { protocol } from "electron";
|
||||
import { isDevelopment } from "./index";
|
||||
import { createReadStream } from "fs";
|
||||
import { extname, normalize } from "path";
|
||||
@@ -62,8 +62,25 @@ function registerProtocol() {
|
||||
headers: { "Content-Type": extensionToMimeType[fileExtension] }
|
||||
});
|
||||
} else {
|
||||
return net.fetch(request, {
|
||||
bypassCustomProtocolHandlers: true
|
||||
if (request.headers.has("X-Content-Length")) {
|
||||
request.headers.set(
|
||||
"Content-Length",
|
||||
request.headers.get("X-Content-Length") || "0"
|
||||
);
|
||||
request.headers.delete("X-Content-Length");
|
||||
}
|
||||
const headers = Object.fromEntries(request.headers.entries());
|
||||
|
||||
return await fetch(request.url, {
|
||||
signal: request.signal,
|
||||
mode: request.mode,
|
||||
headers,
|
||||
method: request.method,
|
||||
body: request.body,
|
||||
credentials: request.credentials,
|
||||
referrer: (request as any).referrer,
|
||||
duplex: "half",
|
||||
redirect: "manual"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,8 +16,8 @@ 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 { MMKVLoader } from "react-native-mmkv-storage";
|
||||
import { initalize } from "@notesnook/core/dist/logger";
|
||||
import { MMKVLoader } from "react-native-mmkv-storage";
|
||||
import { KV } from "./storage";
|
||||
|
||||
const LoggerStorage = new MMKVLoader()
|
||||
@@ -26,4 +26,4 @@ const LoggerStorage = new MMKVLoader()
|
||||
|
||||
initalize(new KV(LoggerStorage));
|
||||
|
||||
export {};
|
||||
export { LoggerStorage };
|
||||
|
||||
@@ -90,6 +90,10 @@ export class KV {
|
||||
return this.storage.removeItem(key);
|
||||
}
|
||||
|
||||
async removeMulti(keys) {
|
||||
return this.storage.removeItems(...keys);
|
||||
}
|
||||
|
||||
async clear() {
|
||||
return this.storage.clearStore();
|
||||
}
|
||||
@@ -145,6 +149,7 @@ export default {
|
||||
clear: () => DefaultStorage.clear(),
|
||||
getAllKeys: () => DefaultStorage.getAllKeys(),
|
||||
writeMulti: (items) => DefaultStorage.writeMulti(items),
|
||||
removeMulti: (keys) => DefaultStorage.removeMulti(keys),
|
||||
encrypt,
|
||||
decrypt,
|
||||
decryptMulti,
|
||||
|
||||
@@ -34,10 +34,69 @@ import BaseDialog from "../dialog/base-dialog";
|
||||
import { allowedOnPlatform, renderItem } from "./functions";
|
||||
import { useCallback } from "react";
|
||||
|
||||
/**
|
||||
* Test announcement
|
||||
* {
|
||||
id: "some-announcement",
|
||||
type: "dialog",
|
||||
body: [
|
||||
{
|
||||
type: "title",
|
||||
text: "This is a title",
|
||||
platforms: ["all"]
|
||||
},
|
||||
{
|
||||
type: "description",
|
||||
text: "Most of you are too busy to keep up to date with what's happening in Notesnook. That is unfortunate because Notesnook has come a looooong way.",
|
||||
style: {
|
||||
marginBottom: 1
|
||||
},
|
||||
platforms: ["all"]
|
||||
},
|
||||
{
|
||||
type: "description",
|
||||
text: "To solve this, we are launching the Notesnook Digest — a newsletter to help you stay updated about Notesnook development. And to keep things interesting I'll also sprinkle this newsletter with other interesting stuff like privacy tips & news, interesting books, things I am looking forward to etc.",
|
||||
style: {
|
||||
marginBottom: 1
|
||||
},
|
||||
platforms: ["all"]
|
||||
},
|
||||
{
|
||||
type: "description",
|
||||
text: "So be sure to subscribe. There won't be a proper schedule to this (yet) maybe once or twice a month. I promise no spam — only more awesomeness.",
|
||||
style: {
|
||||
marginBottom: 1
|
||||
},
|
||||
platforms: ["all"]
|
||||
},
|
||||
{
|
||||
type: "description",
|
||||
text: "— May privacy reign.",
|
||||
style: {
|
||||
marginBottom: 1
|
||||
},
|
||||
platforms: ["all"]
|
||||
},
|
||||
{
|
||||
type: "callToActions",
|
||||
actions: [
|
||||
{
|
||||
type: "promo",
|
||||
title: "15% Off",
|
||||
platforms: ["android"],
|
||||
data: "com.streetwriters.notesnook.sub.yr.15"
|
||||
}
|
||||
],
|
||||
platforms: ["all"]
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
export const AnnouncementDialog = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [info, setInfo] = useState(null);
|
||||
const [info, setInfo] = useState();
|
||||
const remove = useMessageStore((state) => state.remove);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -51,7 +110,9 @@ export const AnnouncementDialog = () => {
|
||||
|
||||
const open = (data) => {
|
||||
setInfo(data);
|
||||
setVisible(true);
|
||||
setImmediate(() => {
|
||||
setVisible(true);
|
||||
});
|
||||
};
|
||||
|
||||
const close = useCallback(() => {
|
||||
|
||||
@@ -48,23 +48,23 @@ export const Expiring = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [status, setStatus] = useState({
|
||||
title: "Your trial is ending soon",
|
||||
offer: null,
|
||||
offer: "Get 30% off",
|
||||
extend: true
|
||||
});
|
||||
const pricing = usePricing("yearly");
|
||||
|
||||
const promo = status.offer
|
||||
? {
|
||||
promoCode:
|
||||
pricing?.info?.discount > 30
|
||||
? pricing.info.sku
|
||||
: "com.streetwriters.notesnook.sub.yr.trialoffer",
|
||||
text: `GET ${
|
||||
pricing?.info?.discount > 30 ? pricing?.info?.discount : 30
|
||||
}% OFF on yearly`,
|
||||
discount: pricing?.info?.discount > 30 ? pricing?.info?.discount : 30
|
||||
}
|
||||
: null;
|
||||
const promo =
|
||||
status.offer && pricing?.info
|
||||
? {
|
||||
promoCode:
|
||||
pricing?.info?.discount > 30
|
||||
? pricing.info.sku
|
||||
: "com.streetwriters.notesnook.sub.yr.trialoffer",
|
||||
text: `GET ${
|
||||
pricing?.info?.discount > 30 ? pricing?.info?.discount : 30
|
||||
}% OFF on yearly`,
|
||||
discount: pricing?.info?.discount > 30 ? pricing?.info?.discount : 30
|
||||
}
|
||||
: null;
|
||||
|
||||
useEffect(() => {
|
||||
eSubscribeEvent(eOpenTrialEndingDialog, open);
|
||||
|
||||
@@ -28,7 +28,8 @@ import RNIap from "react-native-iap";
|
||||
export const PricingItem = ({
|
||||
product,
|
||||
onPress,
|
||||
compact
|
||||
compact,
|
||||
strikethrough
|
||||
}: {
|
||||
product: {
|
||||
type: "yearly" | "monthly";
|
||||
@@ -36,6 +37,7 @@ export const PricingItem = ({
|
||||
info: string;
|
||||
offerType?: "yearly" | "monthly";
|
||||
};
|
||||
strikethrough?: boolean;
|
||||
onPress?: () => void;
|
||||
compact?: boolean;
|
||||
}) => {
|
||||
@@ -50,8 +52,10 @@ export const PricingItem = ({
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: compact ? 15 : 10,
|
||||
width: compact ? null : "100%",
|
||||
minWidth: 150
|
||||
minWidth: 150,
|
||||
opacity: strikethrough ? 0.7 : 1
|
||||
}}
|
||||
disabled={strikethrough}
|
||||
>
|
||||
{!compact && (
|
||||
<View>
|
||||
@@ -67,8 +71,18 @@ export const PricingItem = ({
|
||||
)}
|
||||
|
||||
<View>
|
||||
<Paragraph size={SIZE.sm}>
|
||||
<Heading size={SIZE.lg - 2}>
|
||||
<Paragraph
|
||||
style={{
|
||||
textDecorationLine: strikethrough ? "line-through" : undefined
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
textDecorationLine: strikethrough ? "line-through" : undefined
|
||||
}}
|
||||
size={SIZE.lg - 2}
|
||||
>
|
||||
{Platform.OS === "android"
|
||||
? (product.data as RNIap.SubscriptionAndroid | undefined)
|
||||
?.subscriptionOfferDetails[0].pricingPhases
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { ActivityIndicator, Platform, Text, View } from "react-native";
|
||||
import * as RNIap from "react-native-iap";
|
||||
import { db } from "../../common/database";
|
||||
import { DatabaseLogger, db } from "../../common/database";
|
||||
import { usePricing } from "../../hooks/use-pricing";
|
||||
import {
|
||||
eSendEvent,
|
||||
@@ -49,6 +49,20 @@ import { Walkthrough } from "../walkthroughs";
|
||||
import { PricingItem } from "./pricing-item";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
|
||||
const UUID_PREFIX = "0bdaea";
|
||||
const UUID_VERSION = "4";
|
||||
const UUID_VARIANT = "a";
|
||||
|
||||
function toUUID(str: string) {
|
||||
return [
|
||||
UUID_PREFIX + str.substring(0, 2), // 6 digit prefix + first 2 oid digits
|
||||
str.substring(2, 6), // # next 4 oid digits
|
||||
UUID_VERSION + str.substring(6, 9), // # 1 digit version(0x4) + next 3 oid digits
|
||||
UUID_VARIANT + str.substring(9, 12), // # 1 digit variant(0b101) + 1 zero bit + next 3 oid digits
|
||||
str.substring(12)
|
||||
].join("-");
|
||||
}
|
||||
|
||||
const promoCyclesMonthly = {
|
||||
1: "first month",
|
||||
2: "first 2 months",
|
||||
@@ -177,11 +191,15 @@ export const PricingPlans = ({
|
||||
.offerToken
|
||||
: null;
|
||||
|
||||
DatabaseLogger.info(
|
||||
`Subscription Requested initiated for user ${toUUID(user.id)}`
|
||||
);
|
||||
|
||||
await RNIap.requestSubscription({
|
||||
sku: product?.productId,
|
||||
obfuscatedAccountIdAndroid: user.id,
|
||||
obfuscatedProfileIdAndroid: user.id,
|
||||
appAccountToken: user.id,
|
||||
appAccountToken: toUUID(user.id),
|
||||
andDangerouslyFinishTransactionAutomaticallyIOS: false,
|
||||
subscriptionOffers: androidOfferToken
|
||||
? [
|
||||
@@ -213,6 +231,35 @@ export const PricingPlans = ({
|
||||
}
|
||||
};
|
||||
|
||||
function getStandardPrice() {
|
||||
if (!product) return;
|
||||
const productType = product.offerType;
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
const pricingPhaseListItem = (product.data as RNIap.SubscriptionAndroid)
|
||||
?.subscriptionOfferDetails[0]?.pricingPhases.pricingPhaseList?.[1];
|
||||
|
||||
if (!pricingPhaseListItem) {
|
||||
const product =
|
||||
productType === "monthly"
|
||||
? monthlyPlan?.product
|
||||
: yearlyPlan?.product;
|
||||
return (product as RNIap.SubscriptionAndroid)
|
||||
?.subscriptionOfferDetails[0]?.pricingPhases.pricingPhaseList?.[0]
|
||||
?.formattedPrice;
|
||||
}
|
||||
|
||||
return pricingPhaseListItem?.formattedPrice;
|
||||
} else {
|
||||
const productDefault =
|
||||
productType === "monthly" ? monthlyPlan?.product : yearlyPlan?.product;
|
||||
return (
|
||||
(product.data as RNIap.SubscriptionIOS)?.localizedPrice ||
|
||||
(productDefault as RNIap.SubscriptionIOS)?.localizedPrice
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return loading ? (
|
||||
<View
|
||||
style={{
|
||||
@@ -292,34 +339,84 @@ export const PricingPlans = ({
|
||||
) : (
|
||||
<>
|
||||
{product?.type === "promo" ? (
|
||||
<Heading
|
||||
<View
|
||||
style={{
|
||||
paddingVertical: 15,
|
||||
alignSelf: "center",
|
||||
textAlign: "center"
|
||||
alignItems: "center"
|
||||
}}
|
||||
size={SIZE.lg - 4}
|
||||
>
|
||||
{Platform.OS === "android"
|
||||
? (product.data as RNIap.SubscriptionAndroid)
|
||||
?.subscriptionOfferDetails[0].pricingPhases
|
||||
.pricingPhaseList?.[0].formattedPrice
|
||||
: (product.data as RNIap.SubscriptionIOS)?.introductoryPrice}
|
||||
<Paragraph
|
||||
{product?.offerType === "monthly" ? (
|
||||
<PricingItem
|
||||
product={{
|
||||
type: "monthly",
|
||||
data: monthlyPlan?.product,
|
||||
info: "Pay once a month, cancel anytime."
|
||||
}}
|
||||
strikethrough={true}
|
||||
/>
|
||||
) : (
|
||||
<PricingItem
|
||||
onPress={() => {
|
||||
if (!monthlyPlan?.product) return;
|
||||
buySubscription(monthlyPlan?.product);
|
||||
}}
|
||||
product={{
|
||||
type: "yearly",
|
||||
data: yearlyPlan?.product,
|
||||
info: "Pay once a year, cancel anytime."
|
||||
}}
|
||||
strikethrough={true}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Heading
|
||||
style={{
|
||||
textDecorationLine: "line-through",
|
||||
color: colors.secondary.paragraph
|
||||
paddingTop: 15,
|
||||
fontSize: SIZE.lg
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
>
|
||||
{Platform.OS === "android"
|
||||
? (product.data as RNIap.SubscriptionAndroid)
|
||||
?.subscriptionOfferDetails[1]?.pricingPhases
|
||||
.pricingPhaseList?.[1]?.formattedPrice
|
||||
: (product.data as RNIap.SubscriptionIOS)?.localizedPrice}
|
||||
</Paragraph>{" "}
|
||||
for {product.cycleText}
|
||||
</Heading>
|
||||
Special offer for you
|
||||
</Heading>
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingVertical: 20,
|
||||
paddingBottom: 10
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
textAlign: "center"
|
||||
}}
|
||||
size={SIZE.xxl}
|
||||
>
|
||||
{Platform.OS === "android"
|
||||
? (product.data as RNIap.SubscriptionAndroid)
|
||||
?.subscriptionOfferDetails[0].pricingPhases
|
||||
.pricingPhaseList?.[0]?.formattedPrice
|
||||
: (product.data as RNIap.SubscriptionIOS)
|
||||
?.introductoryPrice ||
|
||||
(product.data as RNIap.SubscriptionIOS)
|
||||
?.localizedPrice}{" "}
|
||||
{product?.cycleText
|
||||
? `for ${product.cycleText}`
|
||||
: product?.offerType}
|
||||
</Heading>
|
||||
{product?.cycleText ? (
|
||||
<Paragraph
|
||||
style={{
|
||||
color: colors.secondary.paragraph,
|
||||
alignSelf: "center",
|
||||
textAlign: "center"
|
||||
}}
|
||||
size={SIZE.md}
|
||||
>
|
||||
then {getStandardPrice()} {product?.offerType}.
|
||||
</Paragraph>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{user && !product ? (
|
||||
@@ -334,7 +431,12 @@ export const PricingPlans = ({
|
||||
marginBottom: 20
|
||||
}}
|
||||
>
|
||||
<Heading color={colors.primary.accent}>
|
||||
<Heading
|
||||
style={{
|
||||
textAlign: "center"
|
||||
}}
|
||||
color={colors.primary.accent}
|
||||
>
|
||||
Get {monthlyPlan?.info?.discount}% off in{" "}
|
||||
{monthlyPlan?.info?.country}
|
||||
</Heading>
|
||||
@@ -369,7 +471,7 @@ export const PricingPlans = ({
|
||||
product={{
|
||||
type: "monthly",
|
||||
data: monthlyPlan?.product,
|
||||
info: "Pay monthly, cancel anytime."
|
||||
info: "Pay once a month, cancel anytime."
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -391,7 +493,7 @@ export const PricingPlans = ({
|
||||
product={{
|
||||
type: "yearly",
|
||||
data: yearlyPlan?.product,
|
||||
info: "Pay yearly"
|
||||
info: "Pay once a year, cancel anytime."
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -463,7 +565,7 @@ export const PricingPlans = ({
|
||||
width={250}
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
marginTop: 30,
|
||||
marginTop: product?.type === "promo" ? 0 : 30,
|
||||
marginBottom: 10
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -460,7 +460,6 @@ export const useAppEvents = () => {
|
||||
}
|
||||
|
||||
clearMessage();
|
||||
subscribeToIAPListeners();
|
||||
if (!login) {
|
||||
user = await db.user.fetchUser();
|
||||
setUser(user);
|
||||
@@ -479,7 +478,11 @@ export const useAppEvents = () => {
|
||||
userEmailConfirmed: true
|
||||
});
|
||||
}
|
||||
|
||||
subscribeToIAPListeners();
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(error);
|
||||
|
||||
ToastEvent.error(e, "An error occurred", "global");
|
||||
}
|
||||
|
||||
@@ -503,14 +506,15 @@ export const useAppEvents = () => {
|
||||
);
|
||||
|
||||
const subscribeToIAPListeners = useCallback(async () => {
|
||||
RNIap.flushFailedPurchasesCachedAsPendingAndroid()
|
||||
.catch(() => {})
|
||||
.then(() => {
|
||||
refValues.current.subsriptionSuccessListener =
|
||||
RNIap.purchaseUpdatedListener(onSuccessfulSubscription);
|
||||
refValues.current.subsriptionErrorListener =
|
||||
RNIap.purchaseErrorListener(onSubscriptionError);
|
||||
});
|
||||
if (Platform.OS === "android") {
|
||||
try {
|
||||
await RNIap.flushFailedPurchasesCachedAsPendingAndroid();
|
||||
} catch (e) {}
|
||||
}
|
||||
refValues.current.subsriptionSuccessListener =
|
||||
RNIap.purchaseUpdatedListener(onSuccessfulSubscription);
|
||||
refValues.current.subsriptionErrorListener =
|
||||
RNIap.purchaseErrorListener(onSubscriptionError);
|
||||
}, []);
|
||||
|
||||
const unSubscribeFromIAPListeners = () => {
|
||||
|
||||
@@ -42,6 +42,9 @@ export const Subscription = () => {
|
||||
user?.subscription?.type !== SUBSCRIPTION_STATUS.PREMIUM &&
|
||||
user?.subscription?.type !== SUBSCRIPTION_STATUS.BETA;
|
||||
|
||||
const hasCancelledPremium =
|
||||
SUBSCRIPTION_STATUS.PREMIUM_CANCELLED === user?.subscription?.type;
|
||||
|
||||
const subscriptionProviderInfo =
|
||||
SUBSCRIPTION_PROVIDER[user?.subscription?.provider];
|
||||
|
||||
@@ -63,10 +66,7 @@ export const Subscription = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
user?.subscription?.type === SUBSCRIPTION_STATUS.PREMIUM_CANCELLED &&
|
||||
Platform.OS === "android"
|
||||
) {
|
||||
if (hasCancelledPremium && Platform.OS === "android") {
|
||||
if (user.subscription?.provider === 3) {
|
||||
ToastEvent.show({
|
||||
heading: "Subscribed on web",
|
||||
@@ -111,16 +111,14 @@ export const Subscription = () => {
|
||||
title={
|
||||
!user?.isEmailConfirmed
|
||||
? "Confirm your email"
|
||||
: user.subscription?.provider === 3 &&
|
||||
user.subscription?.type ===
|
||||
SUBSCRIPTION_STATUS.PREMIUM_CANCELLED
|
||||
: user.subscription?.provider === 3 && hasCancelledPremium
|
||||
? "Manage subscription from desktop app"
|
||||
: user.subscription?.type ===
|
||||
SUBSCRIPTION_STATUS.PREMIUM_CANCELLED &&
|
||||
: hasCancelledPremium &&
|
||||
Platform.OS === "android" &&
|
||||
Config.GITHUB_RELEASE !== "true"
|
||||
? "Resubscribe from Google Playstore"
|
||||
: user.subscription?.type === SUBSCRIPTION_STATUS.PREMIUM_EXPIRED
|
||||
: user.subscription?.type ===
|
||||
SUBSCRIPTION_STATUS.PREMIUM_EXPIRED || hasCancelledPremium
|
||||
? `Resubscribe to Pro (${getPrice() || "$4.49"} / mo)`
|
||||
: `Get Pro (${getPrice() || "$4.49"} / mo)`
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ const subscriptions = {
|
||||
*
|
||||
* @returns {RNIap.Purchase} subscription
|
||||
*/
|
||||
get: async () => {
|
||||
get: () => {
|
||||
if (Platform.OS === "android") return;
|
||||
let _subscriptions = MMKV.getString("subscriptionsIOS");
|
||||
if (!_subscriptions) return [];
|
||||
@@ -299,12 +299,14 @@ const subscriptions = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
console.log("Subscription.verify", requestData);
|
||||
try {
|
||||
let result = await fetch(
|
||||
"https://payments.streetwriters.co/apple/verify",
|
||||
"http://192.168.43.5:4264/apple/verify",
|
||||
requestData
|
||||
);
|
||||
|
||||
console.log("Subscribed", result);
|
||||
let text = await result.text();
|
||||
|
||||
if (!result.ok) {
|
||||
@@ -312,6 +314,8 @@ const subscriptions = {
|
||||
await subscriptions.clear(subscription);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
await subscriptions.clear(subscription);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("subscription error", e);
|
||||
@@ -321,15 +325,18 @@ const subscriptions = {
|
||||
},
|
||||
clear: async (_subscription) => {
|
||||
if (Platform.OS === "android") return;
|
||||
let _subscriptions = await subscriptions.get();
|
||||
let _subscriptions = subscriptions.get();
|
||||
let subscription = null;
|
||||
if (_subscription) {
|
||||
subscription = _subscription;
|
||||
} else {
|
||||
subscription = _subscriptions.length > 0 ? _subscriptions[0] : null;
|
||||
}
|
||||
|
||||
if (subscription) {
|
||||
await RNIap.finishTransaction(subscription.transactionId);
|
||||
await RNIap.finishTransaction({
|
||||
purchase: subscription
|
||||
});
|
||||
await RNIap.clearTransactionIOS();
|
||||
await subscriptions.remove(subscription.transactionId);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ export const SUBSCRIPTION_STATUS_STRINGS = {
|
||||
2: Platform.OS === "ios" ? "Pro" : "Beta",
|
||||
5: "Pro",
|
||||
6: "Expired",
|
||||
7: "Pro"
|
||||
7: "Pro (cancelled)"
|
||||
};
|
||||
|
||||
export const SUBSCRIPTION_PROVIDER = {
|
||||
|
||||
@@ -111,7 +111,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 2070
|
||||
versionCode 2071
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
- Fixed HTML & PDF exports
|
||||
- Improved task lists in editor
|
||||
- Improved loading images
|
||||
- Allow restoring backups when logged out
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -997,7 +997,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
CURRENT_PROJECT_VERSION = 2062;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1071,7 +1071,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MARKETING_VERSION = 2.6.11;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1102,7 +1102,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
CURRENT_PROJECT_VERSION = 2062;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1176,7 +1176,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MARKETING_VERSION = 2.6.11;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1335,7 +1335,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
CURRENT_PROJECT_VERSION = 2062;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1347,7 +1347,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MARKETING_VERSION = 2.6.11;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1378,7 +1378,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
CURRENT_PROJECT_VERSION = 2062;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1391,7 +1391,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MARKETING_VERSION = 2.6.11;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1421,7 +1421,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
CURRENT_PROJECT_VERSION = 2062;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1495,7 +1495,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MARKETING_VERSION = 2.6.11;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1526,7 +1526,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2061;
|
||||
CURRENT_PROJECT_VERSION = 2062;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1601,7 +1601,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.10;
|
||||
MARKETING_VERSION = 2.6.11;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -327,7 +327,7 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-keep-awake (1.2.0):
|
||||
- React-Core
|
||||
- react-native-mmkv-storage (0.10.0-alpha.9):
|
||||
- react-native-mmkv-storage (0.10.0-alpha.11):
|
||||
- MMKV (~> 1.3.1)
|
||||
- React
|
||||
- React-Core
|
||||
@@ -875,7 +875,7 @@ SPEC CHECKSUMS:
|
||||
react-native-image-resizer: 00ceb0e05586c7aadf061eea676957a6c2ec60fa
|
||||
react-native-in-app-review: db8bb167a5f238e7ceca5c242d6b36ce8c4404a4
|
||||
react-native-keep-awake: caee3ff89eaa21dfe29010f0d143566874a04441
|
||||
react-native-mmkv-storage: d4ad55ab411b7f0d4e6269d801b31821ef48970b
|
||||
react-native-mmkv-storage: e798639f91601896f1c84f4eb3d6f19af2f4a160
|
||||
react-native-netinfo: ccbe1085dffd16592791d550189772e13bf479e2
|
||||
react-native-notification-sounds: da78c828fe1bcbb92d8b505d5261890ed315ff39
|
||||
react-native-orientation: f1caf84d65f1a4fd4511a18f2b924e634ad7a628
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"react-native-image-picker": "4.1.2",
|
||||
"react-native-in-app-review": "4.3.3",
|
||||
"react-native-keychain": "4.0.5",
|
||||
"react-native-mmkv-storage": "^0.10.0-alpha.9",
|
||||
"react-native-mmkv-storage": "^0.10.0-alpha.11",
|
||||
"react-native-modal-datetime-picker": "14.0.0",
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
|
||||
456
apps/mobile/package-lock.json
generated
456
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.6.10",
|
||||
"version": "2.6.11",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
|
||||
4
apps/web/package-lock.json
generated
4
apps/web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.6.9",
|
||||
"version": "2.6.11",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.6.9",
|
||||
"version": "2.6.11",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@aws-sdk/util-base64-browser": "^3.208.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.6.9",
|
||||
"version": "2.6.11",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
|
||||
@@ -291,7 +291,9 @@ function CalltoAction({ action, variant, sx, dismissAnnouncement }) {
|
||||
case "link": {
|
||||
const url = new URL(action.data);
|
||||
const target =
|
||||
url.origin === window.location.origin ? "_self" : "_blank";
|
||||
url.origin === window.location.origin && !IS_DESKTOP_APP
|
||||
? "_self"
|
||||
: "_blank";
|
||||
window.open(action.data, target, "noopener noreferrer");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -80,11 +80,10 @@ function BaseDialog(props: React.PropsWithChildren<DialogProps>) {
|
||||
border: 0,
|
||||
zIndex: 999,
|
||||
backgroundColor: "var(--backdrop)"
|
||||
},
|
||||
overlay: {
|
||||
opacity: 1
|
||||
}
|
||||
// overlay: {
|
||||
// zIndex: 999,
|
||||
// background: "var(--backdrop)"
|
||||
// }
|
||||
}}
|
||||
>
|
||||
<ScopedThemeProvider
|
||||
|
||||
@@ -430,13 +430,15 @@ export function Editor(props: EditorProps) {
|
||||
onContentChange={onContentChange}
|
||||
onChange={onEditorChange}
|
||||
onDownloadAttachment={(attachment) => saveAttachment(attachment.hash)}
|
||||
onPreviewAttachment={async ({ hash, dataurl }) => {
|
||||
onPreviewAttachment={async (data) => {
|
||||
const { hash } = data;
|
||||
const attachment = db.attachments?.attachment(hash);
|
||||
if (attachment && attachment.metadata.type.startsWith("image/")) {
|
||||
const container = document.getElementById("dialogContainer");
|
||||
if (!(container instanceof HTMLElement)) return;
|
||||
|
||||
dataurl = dataurl || (await downloadAttachment(hash, "base64"));
|
||||
const dataurl =
|
||||
data.bloburl || (await downloadAttachment(hash, "base64"));
|
||||
if (!dataurl)
|
||||
return showToast("error", "This image cannot be previewed.");
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ export type Attachment = {
|
||||
mime: string;
|
||||
size: number;
|
||||
dataurl?: string;
|
||||
bloburl?: string;
|
||||
};
|
||||
|
||||
type AddAttachmentOptions = {
|
||||
|
||||
@@ -97,8 +97,7 @@ function StatusBar() {
|
||||
ml={1}
|
||||
sx={{ color: "paragraph" }}
|
||||
>
|
||||
{user?.email}
|
||||
{user?.isEmailConfirmed ? "" : " (not verified)"}
|
||||
{user?.isEmailConfirmed ? "" : "Email not confirmed"}
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ export default function ReminderPreviewDialog(
|
||||
id: reminder.id,
|
||||
snoozeUntil: Date.now() + time.interval
|
||||
});
|
||||
props.onClose(false);
|
||||
}}
|
||||
sx={{
|
||||
borderRadius: 100,
|
||||
|
||||
@@ -226,8 +226,10 @@ type UploadAdditionalData = {
|
||||
|
||||
async function uploadFile(filename: string, requestOptions: RequestOptions) {
|
||||
const fileHandle = await streamablefs.readFile(filename);
|
||||
if (!fileHandle)
|
||||
throw new Error(`File stream not found. (File hash: ${filename})`);
|
||||
if (!fileHandle || !(await exists(filename)))
|
||||
throw new Error(
|
||||
`File is corrupt or missing data. Please upload the file again. (File hash: ${filename})`
|
||||
);
|
||||
try {
|
||||
if (fileHandle.file.additionalData?.uploaded) {
|
||||
await checkUpload(filename);
|
||||
@@ -359,6 +361,7 @@ async function multiPartUploadFile(
|
||||
);
|
||||
};
|
||||
|
||||
onUploadProgress({ bytes: 0, loaded: 0 });
|
||||
for (let i = uploadedChunks.length; i < TOTAL_PARTS; ++i) {
|
||||
const blob = await fileHandle.readChunks(
|
||||
i * UPLOAD_PART_REQUIRED_CHUNKS,
|
||||
@@ -370,7 +373,7 @@ async function multiPartUploadFile(
|
||||
.request({
|
||||
url,
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "" },
|
||||
headers: { "Content-Type": "", "X-Content-Length": data.byteLength },
|
||||
signal,
|
||||
data,
|
||||
onUploadProgress
|
||||
@@ -391,6 +394,8 @@ async function multiPartUploadFile(
|
||||
});
|
||||
await fileHandle.addAdditionalData("uploadedChunks", uploadedChunks);
|
||||
await fileHandle.addAdditionalData("uploadedBytes", uploadedBytes);
|
||||
|
||||
onUploadProgress({ bytes: 0, loaded: blob.size });
|
||||
}
|
||||
|
||||
await axios
|
||||
|
||||
@@ -70,6 +70,10 @@ export class NNStorage {
|
||||
return this.database.delete(key);
|
||||
}
|
||||
|
||||
removeMulti(keys: string[]) {
|
||||
return this.database.deleteMany(keys);
|
||||
}
|
||||
|
||||
clear() {
|
||||
return this.database.clear();
|
||||
}
|
||||
|
||||
@@ -56,26 +56,23 @@ export function register(config: ServiceWorkerRegistrationConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
const swUrl = `${PUBLIC_URL}/service-worker.js`;
|
||||
const swUrl = `${PUBLIC_URL}/service-worker.js`;
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
"This web app is being served cache-first by a service " +
|
||||
"worker. To learn more, visit https://cra.link/PWA"
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
"This web app is being served cache-first by a service " +
|
||||
"worker. To learn more, visit https://cra.link/PWA"
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
bugs.md
14
bugs.md
@@ -1,14 +0,0 @@
|
||||
Mobile:
|
||||
|
||||
1. Select all notes in a topic -> Click on add button
|
||||
2. Edit button in a notebook doesn't work
|
||||
3. Edit notebook sheet doesn't close after saving edits
|
||||
4. Removing notes from a topic doesn't update the notes count in the list automatically
|
||||
5. Notes inside a notebook/topic keep showing the notebook tag at the top
|
||||
6.
|
||||
|
||||
Web:
|
||||
|
||||
1. When a note is in a single topic, its impossible to move it to its parent notebook
|
||||
2. When a note is in a single notebook, its impossible to remove it
|
||||
3. Disable sync -> Refresh app -> Enable sync & notice how the sync status is not updated nor does the sync run automatically
|
||||
@@ -1,81 +0,0 @@
|
||||
workflows:
|
||||
react-native-ios:
|
||||
name: Notesnook iOS Build
|
||||
max_build_duration: 120
|
||||
instance_type: mac_mini
|
||||
environment:
|
||||
groups:
|
||||
- appstore_credentials
|
||||
- certificate_credentials
|
||||
- provisioning_profile
|
||||
|
||||
vars:
|
||||
XCODE_WORKSPACE: "Notesnook.xcworkspace" # <-- Put the name of your Xcode workspace here
|
||||
XCODE_SCHEME: "Notesnook" # <-- Put the name of your Xcode scheme here
|
||||
BUNDLE_ID: "org.streetwriters.notesnook" # <-- Put your Bundle Id here e.g com.domain.myapp
|
||||
APP_STORE_APP_ID: 1544027013 # <-- Put the app id number here. This is found in App Store Connect > App > General > App Information
|
||||
|
||||
node: 16.13.2
|
||||
xcode: latest
|
||||
cocoapods: default
|
||||
cache:
|
||||
cache_paths:
|
||||
- $HOME/Library/Caches/CocoaPods
|
||||
scripts:
|
||||
- name: Install npm dependencies
|
||||
script: |
|
||||
npm i
|
||||
- name: Install CocoaPods dependencies
|
||||
script: |
|
||||
yarn prepare:ios
|
||||
- name: Set up keychain to be used for codesigning using Codemagic CLI 'keychain' command
|
||||
script: |
|
||||
keychain initialize
|
||||
- name: Set up Provisioning profiles from environment variables
|
||||
script: |
|
||||
PROFILES_HOME="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||
mkdir -p "$PROFILES_HOME"
|
||||
for profile in "${!FCI_PROVISIONING_PROFILE_@}"; do
|
||||
PROFILE_PATH="$(mktemp "$HOME/Library/MobileDevice/Provisioning Profiles"/ios_$(uuidgen).mobileprovision)"
|
||||
echo ${!profile} | base64 --decode > "$PROFILE_PATH"
|
||||
echo "Saved provisioning profile $PROFILE_PATH"
|
||||
done
|
||||
- name: Set up signing certificate
|
||||
script: |
|
||||
echo $FCI_CERTIFICATE | base64 --decode > /tmp/certificate.p12
|
||||
if [ -z ${FCI_CERTIFICATE_PASSWORD+x} ]; then
|
||||
# when using a certificate that is not password-protected
|
||||
keychain add-certificates --certificate /tmp/certificate.p12
|
||||
else
|
||||
# when using a password-protected certificate
|
||||
keychain add-certificates --certificate /tmp/certificate.p12 --certificate-password $FCI_CERTIFICATE_PASSWORD
|
||||
fi
|
||||
- name: Set up code signing settings on Xcode project
|
||||
script: xcode-project use-profiles
|
||||
- name: Build packages
|
||||
script: |
|
||||
yarn build
|
||||
- name: Build ipa for distribution
|
||||
script: |
|
||||
xcode-project build-ipa --workspace "$FCI_BUILD_DIR/apps/mobile/native/ios/$XCODE_WORKSPACE" --scheme "$XCODE_SCHEME"
|
||||
artifacts:
|
||||
- build/ios/ipa/*.ipa
|
||||
- /tmp/xcodebuild_logs/*.log
|
||||
- $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.app
|
||||
- $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.dSYM
|
||||
publishing:
|
||||
# See the following link for details about email publishing - https://docs.codemagic.io/publishing-yaml/distribution/#email
|
||||
email:
|
||||
recipients:
|
||||
- ammarahmed6506@gmail.com
|
||||
notify:
|
||||
success: true # To not receive a notification when a build succeeds
|
||||
failure: false # To not receive a notification when a build fails
|
||||
app_store_connect:
|
||||
api_key: $APP_STORE_CONNECT_PRIVATE_KEY # Contents of the API key, can also reference environment variable such as $APP_STORE_CONNECT_PRIVATE_KEY
|
||||
key_id: $APP_STORE_CONNECT_KEY_IDENTIFIER # Alphanumeric value that identifies the API key, can also reference environment variable such as $APP_STORE_CONNECT_KEY_IDENTIFIER
|
||||
issuer_id: $APP_STORE_CONNECT_ISSUER_ID # Alphanumeric value that identifies who created the API key, can also reference environment variable such as $APP_STORE_CONNECT_ISSUER_ID
|
||||
submit_to_testflight: false # Optional boolean, defaults to false. Whether or not to submit the uploaded build to TestFlight beta review. Required for distributing to beta groups. Note: This action is performed during post-processing.
|
||||
# beta_groups: # Specify the names of beta tester groups that will get access to the build once it has passed beta review.
|
||||
# - group name 1
|
||||
# - group name 2
|
||||
@@ -306,7 +306,7 @@ export function Main() {
|
||||
</Button>
|
||||
))}
|
||||
|
||||
{clipData && !isClipping && (
|
||||
{clipData && clipData.data && !isClipping && (
|
||||
<Text
|
||||
variant="body"
|
||||
sx={{
|
||||
@@ -322,7 +322,6 @@ export function Main() {
|
||||
}
|
||||
}}
|
||||
onClick={async () => {
|
||||
if (!clipData) return;
|
||||
const winUrl = URL.createObjectURL(
|
||||
new Blob(["\ufeff", clipData.data], { type: "text/html" })
|
||||
);
|
||||
|
||||
3
fastlane/metadata/android/en-US/changelogs/10359.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/10359.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -210,7 +210,12 @@ export default class Attachments extends Collection {
|
||||
if (!localOnly && !(await this._canDetach(attachment)))
|
||||
throw new Error("This attachment is inside a locked note.");
|
||||
|
||||
if (await this._db.fs.deleteFile(attachment.metadata.hash, localOnly)) {
|
||||
if (
|
||||
await this._db.fs.deleteFile(
|
||||
attachment.metadata.hash,
|
||||
localOnly || !attachment.dateUploaded
|
||||
)
|
||||
) {
|
||||
if (!localOnly) {
|
||||
await this.detach(attachment);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,10 @@ export default class Storage {
|
||||
return this.storage.remove(key);
|
||||
}
|
||||
|
||||
removeMulti(keys) {
|
||||
return this.storage.removeMulti(keys);
|
||||
}
|
||||
|
||||
getAllKeys() {
|
||||
return this.storage.getAllKeys();
|
||||
}
|
||||
|
||||
@@ -59,8 +59,7 @@ class DatabaseLogWriter {
|
||||
*/
|
||||
constructor(storage) {
|
||||
this.storage = storage;
|
||||
this.key = new Date().toLocaleDateString();
|
||||
this.queue = {};
|
||||
this.queue = new Map();
|
||||
this.hasCleared = false;
|
||||
setInterval(() => {
|
||||
setTimeout(() => {
|
||||
@@ -74,34 +73,29 @@ class DatabaseLogWriter {
|
||||
}
|
||||
|
||||
push(message) {
|
||||
this.queue[`${this.key}:${message.timestamp}`] = message;
|
||||
}
|
||||
|
||||
async read() {
|
||||
const logKeys = await this.storage.getAllKeys();
|
||||
const keys = [];
|
||||
for (let key of logKeys) {
|
||||
if (key.startsWith(this.key)) keys.push(key);
|
||||
}
|
||||
return Object.values(await this.storage.readMulti(keys));
|
||||
const key = new Date(message.timestamp).toLocaleDateString();
|
||||
this.queue.set(`${key}:${message.timestamp}`, message);
|
||||
}
|
||||
|
||||
async flush() {
|
||||
if (Object.keys(this.queue).length === 0) return;
|
||||
const queueCopy = Object.entries(this.queue);
|
||||
this.queue = {};
|
||||
if (this.queue.size === 0) return;
|
||||
const queueCopy = Array.from(this.queue.entries());
|
||||
this.queue = new Map();
|
||||
|
||||
await this.storage.writeMulti(queueCopy);
|
||||
}
|
||||
|
||||
async rotate() {
|
||||
const logKeys = (await this.storage.getAllKeys()).sort();
|
||||
const keysToRemove = [];
|
||||
for (let key of logKeys) {
|
||||
const keyParts = key.split(":");
|
||||
if (keyParts.length === 1 || parseInt(keyParts[1]) < Date.now() - WEEK) {
|
||||
await this.storage.remove(key);
|
||||
keysToRemove.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (keysToRemove.length) await this.storage.removeMulti(keysToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,42 +110,41 @@ class DatabaseLogManager {
|
||||
|
||||
async get() {
|
||||
const logKeys = await this.storage.getAllKeys();
|
||||
const logs = {};
|
||||
const logs = await this.storage.readMulti(logKeys);
|
||||
const logGroups = {};
|
||||
|
||||
for (const logKey of logKeys) {
|
||||
const keyParts = logKey.split(":");
|
||||
for (const [key, log] of logs) {
|
||||
const keyParts = key.split(":");
|
||||
if (keyParts.length === 1) continue;
|
||||
const key = keyParts[0];
|
||||
|
||||
const log = await this.storage.read(logKey, true);
|
||||
|
||||
if (!logs[key]) logs[key] = [];
|
||||
logs[key].push(log);
|
||||
const groupKey = keyParts[0];
|
||||
if (!logGroups[groupKey]) logGroups[groupKey] = [];
|
||||
logGroups[groupKey].push(log);
|
||||
}
|
||||
|
||||
return Object.keys(logs).map((key) => ({
|
||||
key: key,
|
||||
logs: logs[key]?.sort((a, b) => a.timestamp - b.timestamp)
|
||||
}));
|
||||
return Object.keys(logGroups)
|
||||
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }))
|
||||
.map((key) => ({
|
||||
key,
|
||||
logs: logGroups[key]?.sort((a, b) => a.timestamp - b.timestamp)
|
||||
}));
|
||||
}
|
||||
|
||||
async clear() {
|
||||
const logKeys = await this.storage.getAllKeys();
|
||||
for (const key of logKeys) {
|
||||
await this.storage.remove(key);
|
||||
}
|
||||
await this.storage.removeMulti(logKeys);
|
||||
}
|
||||
|
||||
async delete(key) {
|
||||
const logKeys = await this.storage.getAllKeys();
|
||||
const keysToRemove = [];
|
||||
for (const logKey of logKeys) {
|
||||
const keyParts = logKey.split(":");
|
||||
if (keyParts.length === 1) continue;
|
||||
const currKey = keyParts[0];
|
||||
if (currKey === key) {
|
||||
await this.storage.remove(logKey);
|
||||
}
|
||||
if (currKey === key) keysToRemove.push(logKey);
|
||||
}
|
||||
if (keysToRemove.length) await this.storage.removeMulti(keysToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
149
packages/editor/package-lock.json
generated
149
packages/editor/package-lock.json
generated
@@ -39,6 +39,7 @@
|
||||
"@tiptap/extension-underline": "2.1.12",
|
||||
"@tiptap/pm": "2.1.12",
|
||||
"@tiptap/starter-kit": "2.1.12",
|
||||
"async-mutex": "^0.4.0",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
"detect-indent": "^7.0.0",
|
||||
"entities": "^4.5.0",
|
||||
@@ -1739,6 +1740,14 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz",
|
||||
"integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
@@ -3113,7 +3122,6 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
@@ -3134,7 +3142,6 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -3267,7 +3274,6 @@
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
|
||||
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
@@ -3533,8 +3539,7 @@
|
||||
"node_modules/tslib": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||
},
|
||||
"node_modules/type-detect": {
|
||||
"version": "4.0.8",
|
||||
@@ -4085,7 +4090,8 @@
|
||||
"@emotion/use-insertion-effect-with-fallbacks": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
|
||||
"integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw=="
|
||||
"integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@emotion/utils": {
|
||||
"version": "1.2.1",
|
||||
@@ -4559,87 +4565,104 @@
|
||||
"@tiptap/core": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.1.12.tgz",
|
||||
"integrity": "sha512-ZGc3xrBJA9KY8kln5AYTj8y+GDrKxi7u95xIl2eccrqTY5CQeRu6HRNM1yT4mAjuSaG9jmazyjGRlQuhyxCKxQ=="
|
||||
"integrity": "sha512-ZGc3xrBJA9KY8kln5AYTj8y+GDrKxi7u95xIl2eccrqTY5CQeRu6HRNM1yT4mAjuSaG9jmazyjGRlQuhyxCKxQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-blockquote": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.1.12.tgz",
|
||||
"integrity": "sha512-Qb3YRlCfugx9pw7VgLTb+jY37OY4aBJeZnqHzx4QThSm13edNYjasokbX0nTwL1Up4NPTcY19JUeHt6fVaVVGg=="
|
||||
"integrity": "sha512-Qb3YRlCfugx9pw7VgLTb+jY37OY4aBJeZnqHzx4QThSm13edNYjasokbX0nTwL1Up4NPTcY19JUeHt6fVaVVGg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-bold": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.1.12.tgz",
|
||||
"integrity": "sha512-AZGxIxcGU1/y6V2YEbKsq6BAibL8yQrbRm6EdcBnby41vj1WziewEKswhLGmZx5IKM2r2ldxld03KlfSIlKQZg=="
|
||||
"integrity": "sha512-AZGxIxcGU1/y6V2YEbKsq6BAibL8yQrbRm6EdcBnby41vj1WziewEKswhLGmZx5IKM2r2ldxld03KlfSIlKQZg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-bullet-list": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.1.12.tgz",
|
||||
"integrity": "sha512-vtD8vWtNlmAZX8LYqt2yU9w3mU9rPCiHmbp4hDXJs2kBnI0Ju/qAyXFx6iJ3C3XyuMnMbJdDI9ee0spAvFz7cQ=="
|
||||
"integrity": "sha512-vtD8vWtNlmAZX8LYqt2yU9w3mU9rPCiHmbp4hDXJs2kBnI0Ju/qAyXFx6iJ3C3XyuMnMbJdDI9ee0spAvFz7cQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-character-count": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.1.12.tgz",
|
||||
"integrity": "sha512-+GFbBG13nvF8mFIeisSERG/Q3CuRsTNwVZIRbJTLgGdbHXFqPhJh4Xfm7cv7OaOYevUlVyO+z5pGD7wIl1bLqQ=="
|
||||
"integrity": "sha512-+GFbBG13nvF8mFIeisSERG/Q3CuRsTNwVZIRbJTLgGdbHXFqPhJh4Xfm7cv7OaOYevUlVyO+z5pGD7wIl1bLqQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-code": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.1.12.tgz",
|
||||
"integrity": "sha512-CRiRq5OTC1lFgSx6IMrECqmtb93a0ZZKujEnaRhzWliPBjLIi66va05f/P1vnV6/tHaC3yfXys6dxB5A4J8jxw=="
|
||||
"integrity": "sha512-CRiRq5OTC1lFgSx6IMrECqmtb93a0ZZKujEnaRhzWliPBjLIi66va05f/P1vnV6/tHaC3yfXys6dxB5A4J8jxw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-code-block": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.1.12.tgz",
|
||||
"integrity": "sha512-RXtSYCVsnk8D+K80uNZShClfZjvv1EgO42JlXLVGWQdIgaNyuOv/6I/Jdf+ZzhnpsBnHufW+6TJjwP5vJPSPHA=="
|
||||
"integrity": "sha512-RXtSYCVsnk8D+K80uNZShClfZjvv1EgO42JlXLVGWQdIgaNyuOv/6I/Jdf+ZzhnpsBnHufW+6TJjwP5vJPSPHA==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-color": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-2.1.12.tgz",
|
||||
"integrity": "sha512-Myd6iSbPJvvclr+NRBEdE0k52QlQrXZnJljk4JKn0b25cl60ERA40FH9QLBjkpTed7SDbI3oX7LWIzTUoCj39w=="
|
||||
"integrity": "sha512-Myd6iSbPJvvclr+NRBEdE0k52QlQrXZnJljk4JKn0b25cl60ERA40FH9QLBjkpTed7SDbI3oX7LWIzTUoCj39w==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-document": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.1.12.tgz",
|
||||
"integrity": "sha512-0QNfAkCcFlB9O8cUNSwTSIQMV9TmoEhfEaLz/GvbjwEq4skXK3bU+OQX7Ih07waCDVXIGAZ7YAZogbvrn/WbOw=="
|
||||
"integrity": "sha512-0QNfAkCcFlB9O8cUNSwTSIQMV9TmoEhfEaLz/GvbjwEq4skXK3bU+OQX7Ih07waCDVXIGAZ7YAZogbvrn/WbOw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-dropcursor": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.1.12.tgz",
|
||||
"integrity": "sha512-0tT/q8nL4NBCYPxr9T0Brck+RQbWuczm9nV0bnxgt0IiQXoRHutfPWdS7GA65PTuVRBS/3LOco30fbjFhkfz/A=="
|
||||
"integrity": "sha512-0tT/q8nL4NBCYPxr9T0Brck+RQbWuczm9nV0bnxgt0IiQXoRHutfPWdS7GA65PTuVRBS/3LOco30fbjFhkfz/A==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-font-family": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-font-family/-/extension-font-family-2.1.12.tgz",
|
||||
"integrity": "sha512-1PAvmtilBD1OWfr1I+oKND1MLNGWMEx/Qa7xC2YDzonxhiBd56Xog6fDE/+2Bbud0vPEIrMHrg8QOjCDH413vw=="
|
||||
"integrity": "sha512-1PAvmtilBD1OWfr1I+oKND1MLNGWMEx/Qa7xC2YDzonxhiBd56Xog6fDE/+2Bbud0vPEIrMHrg8QOjCDH413vw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-gapcursor": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.1.12.tgz",
|
||||
"integrity": "sha512-zFYdZCqPgpwoB7whyuwpc8EYLYjUE5QYKb8vICvc+FraBUDM51ujYhFSgJC3rhs8EjI+8GcK8ShLbSMIn49YOQ=="
|
||||
"integrity": "sha512-zFYdZCqPgpwoB7whyuwpc8EYLYjUE5QYKb8vICvc+FraBUDM51ujYhFSgJC3rhs8EjI+8GcK8ShLbSMIn49YOQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-hard-break": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.1.12.tgz",
|
||||
"integrity": "sha512-nqKcAYGEOafg9D+2cy1E4gHNGuL12LerVa0eS2SQOb+PT8vSel9OTKU1RyZldsWSQJ5rq/w4uIjmLnrSR2w6Yw=="
|
||||
"integrity": "sha512-nqKcAYGEOafg9D+2cy1E4gHNGuL12LerVa0eS2SQOb+PT8vSel9OTKU1RyZldsWSQJ5rq/w4uIjmLnrSR2w6Yw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-heading": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.1.12.tgz",
|
||||
"integrity": "sha512-MoANP3POAP68Ko9YXarfDKLM/kXtscgp6m+xRagPAghRNujVY88nK1qBMZ3JdvTVN6b/ATJhp8UdrZX96TLV2w=="
|
||||
"integrity": "sha512-MoANP3POAP68Ko9YXarfDKLM/kXtscgp6m+xRagPAghRNujVY88nK1qBMZ3JdvTVN6b/ATJhp8UdrZX96TLV2w==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-history": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.1.12.tgz",
|
||||
"integrity": "sha512-6b7UFVkvPjq3LVoCTrYZAczt5sQrQUaoDWAieVClVZoFLfjga2Fwjcfgcie8IjdPt8YO2hG/sar/c07i9vM0Sg=="
|
||||
"integrity": "sha512-6b7UFVkvPjq3LVoCTrYZAczt5sQrQUaoDWAieVClVZoFLfjga2Fwjcfgcie8IjdPt8YO2hG/sar/c07i9vM0Sg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-horizontal-rule": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.1.12.tgz",
|
||||
"integrity": "sha512-RRuoK4KxrXRrZNAjJW5rpaxjiP0FJIaqpi7nFbAua2oHXgsCsG8qbW2Y0WkbIoS8AJsvLZ3fNGsQ8gpdliuq3A=="
|
||||
"integrity": "sha512-RRuoK4KxrXRrZNAjJW5rpaxjiP0FJIaqpi7nFbAua2oHXgsCsG8qbW2Y0WkbIoS8AJsvLZ3fNGsQ8gpdliuq3A==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-italic": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.1.12.tgz",
|
||||
"integrity": "sha512-/XYrW4ZEWyqDvnXVKbgTXItpJOp2ycswk+fJ3vuexyolO6NSs0UuYC6X4f+FbHYL5VuWqVBv7EavGa+tB6sl3A=="
|
||||
"integrity": "sha512-/XYrW4ZEWyqDvnXVKbgTXItpJOp2ycswk+fJ3vuexyolO6NSs0UuYC6X4f+FbHYL5VuWqVBv7EavGa+tB6sl3A==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-link": {
|
||||
"version": "2.1.12",
|
||||
@@ -4652,92 +4675,110 @@
|
||||
"@tiptap/extension-list-item": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.1.12.tgz",
|
||||
"integrity": "sha512-Gk7hBFofAPmNQ8+uw8w5QSsZOMEGf7KQXJnx5B022YAUJTYYxO3jYVuzp34Drk9p+zNNIcXD4kc7ff5+nFOTrg=="
|
||||
"integrity": "sha512-Gk7hBFofAPmNQ8+uw8w5QSsZOMEGf7KQXJnx5B022YAUJTYYxO3jYVuzp34Drk9p+zNNIcXD4kc7ff5+nFOTrg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-list-keymap": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-2.1.12.tgz",
|
||||
"integrity": "sha512-f19nGaqhIZhssM2k8nYR+zcoMc7UCLcW6YCNhTXSrybUsb6SMFVob9OL7+sy1x2n5Was5IqsvyAGakLjdTEwAw=="
|
||||
"integrity": "sha512-f19nGaqhIZhssM2k8nYR+zcoMc7UCLcW6YCNhTXSrybUsb6SMFVob9OL7+sy1x2n5Was5IqsvyAGakLjdTEwAw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-ordered-list": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.1.12.tgz",
|
||||
"integrity": "sha512-tF6VGl+D2avCgn9U/2YLJ8qVmV6sPE/iEzVAFZuOSe6L0Pj7SQw4K6AO640QBob/d8VrqqJFHCb6l10amJOnXA=="
|
||||
"integrity": "sha512-tF6VGl+D2avCgn9U/2YLJ8qVmV6sPE/iEzVAFZuOSe6L0Pj7SQw4K6AO640QBob/d8VrqqJFHCb6l10amJOnXA==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-paragraph": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.1.12.tgz",
|
||||
"integrity": "sha512-hoH/uWPX+KKnNAZagudlsrr4Xu57nusGekkJWBcrb5MCDE91BS+DN2xifuhwXiTHxnwOMVFjluc0bPzQbkArsw=="
|
||||
"integrity": "sha512-hoH/uWPX+KKnNAZagudlsrr4Xu57nusGekkJWBcrb5MCDE91BS+DN2xifuhwXiTHxnwOMVFjluc0bPzQbkArsw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-placeholder": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.1.12.tgz",
|
||||
"integrity": "sha512-K52o7B1zkP4vaVy3z4ZwHn+tQy6KlXtedj1skLg+796ImwH2GYS5z6MFOTfKzBO2hLncUzLco/s0C5PLCD6SDw=="
|
||||
"integrity": "sha512-K52o7B1zkP4vaVy3z4ZwHn+tQy6KlXtedj1skLg+796ImwH2GYS5z6MFOTfKzBO2hLncUzLco/s0C5PLCD6SDw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-strike": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.1.12.tgz",
|
||||
"integrity": "sha512-HlhrzIjYUT8oCH9nYzEL2QTTn8d1ECnVhKvzAe6x41xk31PjLMHTUy8aYjeQEkWZOWZ34tiTmslV1ce6R3Dt8g=="
|
||||
"integrity": "sha512-HlhrzIjYUT8oCH9nYzEL2QTTn8d1ECnVhKvzAe6x41xk31PjLMHTUy8aYjeQEkWZOWZ34tiTmslV1ce6R3Dt8g==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-subscript": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.1.12.tgz",
|
||||
"integrity": "sha512-tb1jysEvf4SIiXwEOgDTXiyrG39RVNHvn/zsGMg5wy5t9qUp9m1k7kKYTH084ktuKDAPQonCcpn3hwc+ngTFzg=="
|
||||
"integrity": "sha512-tb1jysEvf4SIiXwEOgDTXiyrG39RVNHvn/zsGMg5wy5t9qUp9m1k7kKYTH084ktuKDAPQonCcpn3hwc+ngTFzg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-superscript": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.1.12.tgz",
|
||||
"integrity": "sha512-ek6L+DNsrjiJieArlgTvQt1VfJ56d8V19WAPW/ciRhq88YRlTEY9nSO3QuUCSUO1nGmE5OWQpgrsiW/XZbONVw=="
|
||||
"integrity": "sha512-ek6L+DNsrjiJieArlgTvQt1VfJ56d8V19WAPW/ciRhq88YRlTEY9nSO3QuUCSUO1nGmE5OWQpgrsiW/XZbONVw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-table": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.1.12.tgz",
|
||||
"integrity": "sha512-q/DuKZ4j1ycRfuFdb9rBJ3MglGNxlM2BQ1csScX/BrVIsAQI5B8sdzy1BrIlepQ6DRu4DCzHcKMI8u4/edUSWA=="
|
||||
"integrity": "sha512-q/DuKZ4j1ycRfuFdb9rBJ3MglGNxlM2BQ1csScX/BrVIsAQI5B8sdzy1BrIlepQ6DRu4DCzHcKMI8u4/edUSWA==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-table-cell": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.1.12.tgz",
|
||||
"integrity": "sha512-hextcfVTdwX8G7s8Q/V6LW2aUhGvPgu1dfV+kVVO42AFHxG+6PIkDOUuHphGajG3Nrs129bjMDWb8jphj38dUg=="
|
||||
"integrity": "sha512-hextcfVTdwX8G7s8Q/V6LW2aUhGvPgu1dfV+kVVO42AFHxG+6PIkDOUuHphGajG3Nrs129bjMDWb8jphj38dUg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-table-header": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.1.12.tgz",
|
||||
"integrity": "sha512-a4WZ5Z7gqQ/QlK8cK2d1ONYdma/J5+yH/0SNtQhkfELoS45GsLJh89OyKO0W0FnY6Mg0RoH1FsoBD+cqm0yazA=="
|
||||
"integrity": "sha512-a4WZ5Z7gqQ/QlK8cK2d1ONYdma/J5+yH/0SNtQhkfELoS45GsLJh89OyKO0W0FnY6Mg0RoH1FsoBD+cqm0yazA==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-table-row": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.1.12.tgz",
|
||||
"integrity": "sha512-0kPr+zngQC1YQRcU6+Fl3CpIW/SdJhVJ5qOLpQleXrLPdjmZQd3Z1DXvOSDphYjXCowGPCxeUa++6bo7IoEMJw=="
|
||||
"integrity": "sha512-0kPr+zngQC1YQRcU6+Fl3CpIW/SdJhVJ5qOLpQleXrLPdjmZQd3Z1DXvOSDphYjXCowGPCxeUa++6bo7IoEMJw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-task-item": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-task-item/-/extension-task-item-2.1.12.tgz",
|
||||
"integrity": "sha512-uqrDTO4JwukZUt40GQdvB6S+oDhdp4cKNPMi0sbteWziQugkSMLlkYvxU0Hfb/YeziaWWwFI7ssPu/hahyk6dQ=="
|
||||
"integrity": "sha512-uqrDTO4JwukZUt40GQdvB6S+oDhdp4cKNPMi0sbteWziQugkSMLlkYvxU0Hfb/YeziaWWwFI7ssPu/hahyk6dQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-task-list": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-task-list/-/extension-task-list-2.1.12.tgz",
|
||||
"integrity": "sha512-BUpYlEWK+Q3kw9KIiOqvhd0tUPhMcOf1+fJmCkluJok+okAxMbP1umAtCEQ3QkoCwLr+vpHJov7h3yi9+dwgeQ=="
|
||||
"integrity": "sha512-BUpYlEWK+Q3kw9KIiOqvhd0tUPhMcOf1+fJmCkluJok+okAxMbP1umAtCEQ3QkoCwLr+vpHJov7h3yi9+dwgeQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-text": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.1.12.tgz",
|
||||
"integrity": "sha512-rCNUd505p/PXwU9Jgxo4ZJv4A3cIBAyAqlx/dtcY6cjztCQuXJhuQILPhjGhBTOLEEL4kW2wQtqzCmb7O8i2jg=="
|
||||
"integrity": "sha512-rCNUd505p/PXwU9Jgxo4ZJv4A3cIBAyAqlx/dtcY6cjztCQuXJhuQILPhjGhBTOLEEL4kW2wQtqzCmb7O8i2jg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-text-align": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.1.12.tgz",
|
||||
"integrity": "sha512-siMlwrkgVrAxxgmZn8GOc75J7UZi2CVrP9vDHkUPPyKm/fjssYekXwGCEk4Vswii1BbOh2gt+MDsRkeYRGyDlQ=="
|
||||
"integrity": "sha512-siMlwrkgVrAxxgmZn8GOc75J7UZi2CVrP9vDHkUPPyKm/fjssYekXwGCEk4Vswii1BbOh2gt+MDsRkeYRGyDlQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-text-style": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.1.12.tgz",
|
||||
"integrity": "sha512-nfjWXX0JSRHLcscfiMESh+RN+Z7bG8nio/C9+8yQASM90VxU9f8oKgF8HnnSYsSrD4lLf44Q6XjmB7aMVUuikg=="
|
||||
"integrity": "sha512-nfjWXX0JSRHLcscfiMESh+RN+Z7bG8nio/C9+8yQASM90VxU9f8oKgF8HnnSYsSrD4lLf44Q6XjmB7aMVUuikg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/extension-underline": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.1.12.tgz",
|
||||
"integrity": "sha512-NwwdhFT8gDD0VUNLQx85yFBhP9a8qg8GPuxlGzAP/lPTV8Ubh3vSeQ5N9k2ZF/vHlEvnugzeVCbmYn7wf8vn1g=="
|
||||
"integrity": "sha512-NwwdhFT8gDD0VUNLQx85yFBhP9a8qg8GPuxlGzAP/lPTV8Ubh3vSeQ5N9k2ZF/vHlEvnugzeVCbmYn7wf8vn1g==",
|
||||
"requires": {}
|
||||
},
|
||||
"@tiptap/pm": {
|
||||
"version": "2.1.12",
|
||||
@@ -4968,6 +5009,14 @@
|
||||
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
|
||||
"dev": true
|
||||
},
|
||||
"async-mutex": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz",
|
||||
"integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==",
|
||||
"requires": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
@@ -5790,7 +5839,8 @@
|
||||
}
|
||||
},
|
||||
"prosemirror-codemark": {
|
||||
"version": "0.4.2"
|
||||
"version": "0.4.2",
|
||||
"requires": {}
|
||||
},
|
||||
"prosemirror-collab": {
|
||||
"version": "1.3.0",
|
||||
@@ -5938,26 +5988,26 @@
|
||||
}
|
||||
},
|
||||
"re-resizable": {
|
||||
"version": "6.9.9"
|
||||
"version": "6.9.9",
|
||||
"requires": {}
|
||||
},
|
||||
"react": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"react-colorful": {
|
||||
"version": "5.6.1"
|
||||
"version": "5.6.1",
|
||||
"requires": {}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -6044,7 +6094,6 @@
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
|
||||
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
@@ -6237,8 +6286,7 @@
|
||||
"tslib": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||
},
|
||||
"type-detect": {
|
||||
"version": "4.0.8",
|
||||
@@ -6272,7 +6320,8 @@
|
||||
}
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"version": "1.2.0"
|
||||
"version": "1.2.0",
|
||||
"requires": {}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"@tiptap/extension-underline": "2.1.12",
|
||||
"@tiptap/pm": "2.1.12",
|
||||
"@tiptap/starter-kit": "2.1.12",
|
||||
"async-mutex": "^0.4.0",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
"detect-indent": "^7.0.0",
|
||||
"entities": "^4.5.0",
|
||||
|
||||
@@ -43,7 +43,7 @@ export const AnimatedImage = motion(Image);
|
||||
|
||||
export function ImageComponent(
|
||||
props: SelectionBasedReactNodeViewProps<
|
||||
ImageAttributes & ImageAlignmentOptions
|
||||
Partial<ImageAttributes & ImageAlignmentOptions>
|
||||
>
|
||||
) {
|
||||
const { editor, node, selected } = props;
|
||||
@@ -57,7 +57,8 @@ export function ImageComponent(
|
||||
height,
|
||||
textDirection,
|
||||
hash,
|
||||
aspectRatio
|
||||
aspectRatio,
|
||||
mime
|
||||
} = node.attrs;
|
||||
const float = isMobile ? false : node.attrs.float;
|
||||
|
||||
@@ -67,6 +68,7 @@ export function ImageComponent(
|
||||
const imageRef = useRef<HTMLImageElement>(null);
|
||||
const downloadOptions = useToolbarStore((store) => store.downloadOptions);
|
||||
const isReadonly = !editor.current?.isEditable;
|
||||
const isSVG = !!mime && mime.includes("/svg");
|
||||
const relativeHeight = aspectRatio
|
||||
? editor.view.dom.clientWidth / aspectRatio
|
||||
: undefined;
|
||||
@@ -87,6 +89,7 @@ export function ImageComponent(
|
||||
? "start"
|
||||
: "end",
|
||||
position: "relative",
|
||||
mt: isSVG ? `24px` : 0,
|
||||
":hover .drag-handle, :active .drag-handle": {
|
||||
opacity: 1
|
||||
}
|
||||
@@ -139,7 +142,8 @@ export function ImageComponent(
|
||||
top: -40,
|
||||
right: 0,
|
||||
mb: 2,
|
||||
alignItems: "end"
|
||||
alignItems: "end",
|
||||
zIndex: 999
|
||||
}}
|
||||
>
|
||||
<ToolbarGroup
|
||||
@@ -184,7 +188,31 @@ export function ImageComponent(
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{isSVG ? (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: editor.isEditable ? "flex" : "none",
|
||||
position: "absolute",
|
||||
top: -24,
|
||||
height: 24,
|
||||
justifyContent: "end",
|
||||
p: "small",
|
||||
bg: editor.isEditable
|
||||
? "var(--background-secondary)"
|
||||
: "transparent",
|
||||
borderTopLeftRadius: "default",
|
||||
borderTopRightRadius: "default",
|
||||
borderColor: selected ? "border" : "var(--border-secondary)",
|
||||
cursor: "pointer",
|
||||
":hover": {
|
||||
borderColor: "border"
|
||||
}
|
||||
}}
|
||||
></Box>
|
||||
) : null}
|
||||
<AnimatedImage
|
||||
as={isSVG ? "object" : "img"}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: bloburl || src ? 1 : 0 }}
|
||||
transition={{ duration: 0.5, ease: "easeIn" }}
|
||||
@@ -192,11 +220,20 @@ export function ImageComponent(
|
||||
ref={imageRef}
|
||||
alt={alt}
|
||||
crossOrigin="anonymous"
|
||||
src={
|
||||
toBlobURL("", hash) ||
|
||||
bloburl ||
|
||||
corsify(src, downloadOptions?.corsHost)
|
||||
}
|
||||
{...(isSVG
|
||||
? {
|
||||
data:
|
||||
toBlobURL("", hash) ||
|
||||
bloburl ||
|
||||
corsify(src, downloadOptions?.corsHost),
|
||||
type: mime
|
||||
}
|
||||
: {
|
||||
src:
|
||||
toBlobURL("", hash) ||
|
||||
bloburl ||
|
||||
corsify(src, downloadOptions?.corsHost)
|
||||
})}
|
||||
title={title}
|
||||
sx={{
|
||||
objectFit: "contain",
|
||||
@@ -207,37 +244,47 @@ export function ImageComponent(
|
||||
: "2px solid transparent !important",
|
||||
borderRadius: "default"
|
||||
}}
|
||||
onDoubleClick={() =>
|
||||
editor.current?.commands.previewAttachment(node.attrs)
|
||||
}
|
||||
onLoad={async () => {
|
||||
onDoubleClick={() => {
|
||||
const { hash, filename, mime, size } = node.attrs;
|
||||
if (!!hash && !!filename && !!mime && !!size)
|
||||
editor.current?.commands.previewAttachment({
|
||||
hash,
|
||||
filename,
|
||||
mime,
|
||||
size
|
||||
});
|
||||
}}
|
||||
onLoad={async function onLoad() {
|
||||
if (!imageRef.current) return;
|
||||
const { clientHeight, clientWidth } = imageRef.current;
|
||||
|
||||
if (!isDataUrl(src) && canParse(src)) {
|
||||
const { url, size, blob, mimeType } = await downloadImage(
|
||||
src,
|
||||
downloadOptions
|
||||
);
|
||||
editor.current?.commands.updateImage(
|
||||
{ src, hash },
|
||||
{
|
||||
src: await toDataURL(blob),
|
||||
bloburl: url,
|
||||
size: size,
|
||||
mime: mimeType,
|
||||
aspectRatio:
|
||||
!height && !width && !aspectRatio
|
||||
? clientWidth / clientHeight
|
||||
: undefined
|
||||
}
|
||||
if (src && !isDataUrl(src) && canParse(src)) {
|
||||
const image = await downloadImage(src, downloadOptions);
|
||||
if (!image) return;
|
||||
const { url, size, blob, mimeType } = image;
|
||||
const dataurl = await toDataURL(blob);
|
||||
await editor.threadsafe((editor) =>
|
||||
editor.commands.updateImage(
|
||||
{ src, hash },
|
||||
{
|
||||
src: dataurl,
|
||||
bloburl: url,
|
||||
size: size,
|
||||
mime: mimeType,
|
||||
aspectRatio:
|
||||
!height && !width && !aspectRatio
|
||||
? clientWidth / clientHeight
|
||||
: undefined
|
||||
}
|
||||
)
|
||||
);
|
||||
} else if (!height && !width && !aspectRatio) {
|
||||
editor.current?.commands.updateImage(
|
||||
{ src, hash },
|
||||
{
|
||||
aspectRatio: clientWidth / clientHeight
|
||||
}
|
||||
await editor.threadsafe((editor) =>
|
||||
editor.commands.updateImage(
|
||||
{ src, hash },
|
||||
{
|
||||
aspectRatio: clientWidth / clientHeight
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -49,10 +49,9 @@ export function ImageUploadPopup(props: ImageUploadPopupProps) {
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
const { blob, size, mimeType } = await downloadImage(
|
||||
url,
|
||||
downloadOptions
|
||||
);
|
||||
const image = await downloadImage(url, downloadOptions);
|
||||
if (!image) return;
|
||||
const { blob, size, mimeType } = image;
|
||||
onInsert({ src: await toDataURL(blob), size, mime: mimeType });
|
||||
} catch (e) {
|
||||
if (e instanceof Error) setError(e.message);
|
||||
|
||||
@@ -18,10 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { UnionCommands, Editor as TiptapEditor } from "@tiptap/core";
|
||||
import { Mutex } from "async-mutex";
|
||||
|
||||
export type PermissionRequestEvent = CustomEvent<{ id: keyof UnionCommands }>;
|
||||
|
||||
export class Editor extends TiptapEditor {
|
||||
private mutex: Mutex = new Mutex();
|
||||
/**
|
||||
* Use this to get the latest instance of the editor.
|
||||
* This is required to reduce unnecessary rerenders of
|
||||
@@ -45,4 +47,15 @@ export class Editor extends TiptapEditor {
|
||||
|
||||
return this.current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs editor state changes in a thread-safe manner using a mutex
|
||||
* ensuring that all changes are applied sequentially. Use this when
|
||||
* you are getting `RangeError: Applying a mismatched transaction` errors.
|
||||
*/
|
||||
threadsafe(callback: (editor: TiptapEditor) => void) {
|
||||
return this.mutex.runExclusive(() =>
|
||||
this.current ? callback(this.current) : void 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ 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 DataURL from "@notesnook/core/dist/utils/dataurl";
|
||||
|
||||
export type DownloadOptions = {
|
||||
corsHost: string;
|
||||
};
|
||||
@@ -58,14 +60,17 @@ const UTITypes: Record<string, string> = {
|
||||
"public.heifs": "image/heif-sequence"
|
||||
};
|
||||
|
||||
export function corsify(url: string, host?: string) {
|
||||
export function corsify(url?: string, host?: string) {
|
||||
if (host && url && !url.startsWith("blob:") && !isDataUrl(url))
|
||||
return `${host}/${url}`;
|
||||
return url;
|
||||
}
|
||||
|
||||
export async function downloadImage(url: string, options?: DownloadOptions) {
|
||||
const response = await fetch(corsify(url, options?.corsHost), {
|
||||
const corsifiedURL = corsify(url, options?.corsHost);
|
||||
if (!corsifiedURL) return;
|
||||
|
||||
const response = await fetch(corsifiedURL, {
|
||||
mode: "cors",
|
||||
credentials: "omit",
|
||||
cache: "force-cache"
|
||||
@@ -119,8 +124,10 @@ export function toBlobURL(dataurl: string, id?: string) {
|
||||
if (id && OBJECT_URL_CACHE[id]) return OBJECT_URL_CACHE[id];
|
||||
if (!isDataUrl(dataurl)) return;
|
||||
|
||||
const { data, mime } = DataURL.toObject(dataurl); //.split(",");
|
||||
if (!data || !mime) return;
|
||||
const objectURL = URL.createObjectURL(
|
||||
new Blob([Buffer.from(dataurl.split(",")[1], "base64")])
|
||||
new Blob([Buffer.from(data, "base64")], { type: mime })
|
||||
);
|
||||
|
||||
if (id) OBJECT_URL_CACHE[id] = objectURL;
|
||||
|
||||
Reference in New Issue
Block a user