mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-31 02:58:34 +02:00
Compare commits
27 Commits
fix/sync-q
...
fix/sync-q
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57a3b73ada | ||
|
|
5f6a38c8b1 | ||
|
|
3496e6bfa9 | ||
|
|
fc5b62ba61 | ||
|
|
9d0a206240 | ||
|
|
ee6bbca18c | ||
|
|
e6d5472514 | ||
|
|
1e5f13087b | ||
|
|
abb89ecbc3 | ||
|
|
aa168bec5e | ||
|
|
f9116b22db | ||
|
|
5b957f5f0d | ||
|
|
a578aae3dc | ||
|
|
43e5d2fa2d | ||
|
|
b58d034716 | ||
|
|
3b77d79997 | ||
|
|
ef4c9af7dc | ||
|
|
8ba3cf172e | ||
|
|
3e446ce9dd | ||
|
|
394aa98713 | ||
|
|
a2ac882a05 | ||
|
|
cae36487f9 | ||
|
|
fade5eddbf | ||
|
|
66b2ffce31 | ||
|
|
13cd2d4daa | ||
|
|
8442faeeb1 | ||
|
|
6d18383b22 |
@@ -31,14 +31,6 @@ import { TipManager } from "./services/tip-manager";
|
||||
import { useUserStore } from "./stores/use-user-store";
|
||||
import { View } from "react-native";
|
||||
import { useState } from "react";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
NetInfo.configure({
|
||||
reachabilityUrl: "https://notesnook.com",
|
||||
reachabilityTest: (response) => {
|
||||
if (!response) return false;
|
||||
return response?.status >= 200 && response?.status < 300;
|
||||
}
|
||||
});
|
||||
|
||||
SettingsService.init();
|
||||
SettingsService.checkOrientation();
|
||||
|
||||
@@ -148,7 +148,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
|
||||
<Button
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
type="gray"
|
||||
onPress={() => onPress(item)}
|
||||
width={null}
|
||||
|
||||
@@ -67,7 +67,7 @@ export const Title = ({ text, style = {}, inline }) => {
|
||||
right: 0
|
||||
}}
|
||||
iconSize={24}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingVertical: 0,
|
||||
|
||||
@@ -209,7 +209,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
size={SIZE.xs + 1}
|
||||
size={SIZE.xs}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
@@ -221,7 +221,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
size={SIZE.xs + 1}
|
||||
size={SIZE.xs}
|
||||
color={colors.icon}
|
||||
>
|
||||
{formatBytes(attachment.length)}
|
||||
@@ -232,7 +232,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
size={SIZE.xs + 1}
|
||||
size={SIZE.xs}
|
||||
color={colors.icon}
|
||||
>
|
||||
{attachment.noteIds.length} note
|
||||
@@ -248,7 +248,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
|
||||
context: "local"
|
||||
});
|
||||
}}
|
||||
size={SIZE.xs + 1}
|
||||
size={SIZE.xs}
|
||||
color={colors.icon}
|
||||
>
|
||||
{attachment.metadata.hash}
|
||||
@@ -304,7 +304,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
|
||||
}}
|
||||
key={item.id}
|
||||
>
|
||||
<Paragraph size={SIZE.xs + 1}>{item.title}</Paragraph>
|
||||
<Paragraph size={SIZE.xs}>{item.title}</Paragraph>
|
||||
</PressableButton>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -22,7 +22,6 @@ import { TouchableOpacity, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
|
||||
import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { formatBytes } from "../../utils";
|
||||
import { SIZE } from "../../utils/size";
|
||||
@@ -47,9 +46,7 @@ export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
|
||||
attachment,
|
||||
encryption
|
||||
);
|
||||
const encryptionProgress = useAttachmentStore(
|
||||
(state) => state.encryptionProgress
|
||||
);
|
||||
|
||||
const onPress = () => {
|
||||
Actions.present(attachment, setAttachments, attachment.metadata.hash);
|
||||
};
|
||||
@@ -125,9 +122,7 @@ export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{currentProgress ||
|
||||
(encryptionProgress && encryptionProgress !== "0.00") ||
|
||||
encryption ? (
|
||||
{currentProgress ? (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.9}
|
||||
onPress={() => {
|
||||
@@ -144,13 +139,7 @@ export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
|
||||
>
|
||||
<ProgressCircleComponent
|
||||
size={SIZE.xxl}
|
||||
progress={
|
||||
encryptionProgress
|
||||
? encryptionProgress
|
||||
: currentProgress?.value
|
||||
? currentProgress?.value / 100
|
||||
: 0
|
||||
}
|
||||
progress={currentProgress?.value ? currentProgress?.value / 100 : 0}
|
||||
showsText
|
||||
textStyle={{
|
||||
fontSize: 10
|
||||
|
||||
@@ -86,7 +86,7 @@ export const ProFeatures = ({ count = 6 }) => {
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenPremiumDialog);
|
||||
}}
|
||||
size={SIZE.xs + 1}
|
||||
size={SIZE.xs}
|
||||
style={{
|
||||
textDecorationLine: "underline",
|
||||
color: colors.icon
|
||||
|
||||
@@ -70,6 +70,7 @@ const Launcher = React.memo(
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
const verifying = useRef(false);
|
||||
|
||||
const loadNotes = useCallback(async () => {
|
||||
if (verifyUser) {
|
||||
@@ -206,6 +207,9 @@ const Launcher = React.memo(
|
||||
setVerifyUser(false);
|
||||
enabled(false);
|
||||
password.current = null;
|
||||
setTimeout(() => {
|
||||
verifying.current = false;
|
||||
}, 1);
|
||||
}
|
||||
}, [setVerifyUser]);
|
||||
|
||||
@@ -214,7 +218,9 @@ const Launcher = React.memo(
|
||||
}, [init, verifyUser]);
|
||||
|
||||
useEffect(() => {
|
||||
if (verifying.current) return;
|
||||
if (verifyUser && appState === "active") {
|
||||
verifying.current = true;
|
||||
onUnlockBiometrics();
|
||||
}
|
||||
}, [appState, onUnlockBiometrics, verifyUser]);
|
||||
|
||||
@@ -108,7 +108,7 @@ const ReminderItem = React.memo(
|
||||
color={colors.errorText}
|
||||
/>
|
||||
<Paragraph
|
||||
size={SIZE.xs + 1}
|
||||
size={SIZE.xs}
|
||||
color={colors.icon}
|
||||
style={{ marginLeft: 5 }}
|
||||
>
|
||||
@@ -133,7 +133,7 @@ const ReminderItem = React.memo(
|
||||
>
|
||||
<Icon name="reload" size={SIZE.md} color={colors.accent} />
|
||||
<Paragraph
|
||||
size={SIZE.xs + 1}
|
||||
size={SIZE.xs}
|
||||
color={colors.icon}
|
||||
style={{ marginLeft: 5 }}
|
||||
>
|
||||
@@ -147,7 +147,7 @@ const ReminderItem = React.memo(
|
||||
<ReminderTime
|
||||
reminder={item}
|
||||
checkIsActive={false}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
borderWidth: 0,
|
||||
|
||||
@@ -143,7 +143,7 @@ export const Expiring = () => {
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenPremiumDialog, promo);
|
||||
}}
|
||||
size={SIZE.xs + 1}
|
||||
size={SIZE.xs}
|
||||
style={{
|
||||
textDecorationLine: "underline",
|
||||
color: colors.icon,
|
||||
|
||||
@@ -62,10 +62,10 @@ export const DateMeta = ({ item }) => {
|
||||
paddingVertical: 3
|
||||
}}
|
||||
>
|
||||
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
|
||||
<Paragraph size={SIZE.xs} color={colors.icon}>
|
||||
{getNameFromKey(key)}
|
||||
</Paragraph>
|
||||
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
|
||||
<Paragraph size={SIZE.xs} color={colors.icon}>
|
||||
{timeConverter(item[key])}
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
@@ -108,7 +108,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
backgroundColor: "transparent",
|
||||
paddingHorizontal: 0
|
||||
}}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
@@ -115,12 +115,12 @@ export const Items = ({ item, buttons, close }) => {
|
||||
onPress={item.func}
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
activeOpacity={1}
|
||||
customStyle={{
|
||||
alignSelf: "flex-start",
|
||||
width: topBarItemWidth,
|
||||
marginRight: isLast ? 0 : 10,
|
||||
backgroundColor: "transparent",
|
||||
paddingHorizontal: 0
|
||||
paddingHorizontal: 0,
|
||||
width: topBarItemWidth
|
||||
}}
|
||||
>
|
||||
<PressableButton
|
||||
@@ -183,7 +183,7 @@ export const Items = ({ item, buttons, close }) => {
|
||||
horizontal
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingTop: 12
|
||||
paddingVertical: 12
|
||||
}}
|
||||
>
|
||||
{topBarItems.map(renderTopBarItem)}
|
||||
|
||||
@@ -23,13 +23,18 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import Notebook from "../../screens/notebook";
|
||||
import { TopicNotes } from "../../screens/notes/topic-notes";
|
||||
import { presentSheet, ToastEvent } from "../../services/event-manager";
|
||||
import {
|
||||
eSendEvent,
|
||||
presentSheet,
|
||||
ToastEvent
|
||||
} from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useNotebookStore } from "../../stores/use-notebook-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import { eClearEditor } from "../../utils/events";
|
||||
|
||||
export default function Notebooks({ note, close, full }) {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
@@ -112,6 +117,7 @@ export default function Notebooks({ note, close, full }) {
|
||||
size={SIZE.sm}
|
||||
onPress={() => {
|
||||
navigateNotebook(item.id);
|
||||
eSendEvent(eClearEditor);
|
||||
close();
|
||||
}}
|
||||
>
|
||||
@@ -134,6 +140,7 @@ export default function Notebooks({ note, close, full }) {
|
||||
key={topic.id}
|
||||
onPress={() => {
|
||||
navigateTopic(topic.id, item.id);
|
||||
eSendEvent(eClearEditor);
|
||||
close();
|
||||
}}
|
||||
onLongPress={async () => {
|
||||
@@ -155,7 +162,7 @@ export default function Notebooks({ note, close, full }) {
|
||||
title={topic.title}
|
||||
type="gray"
|
||||
height={30}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
icon="bookmark-outline"
|
||||
style={{
|
||||
marginRight: 5,
|
||||
@@ -184,7 +191,7 @@ export default function Notebooks({ note, close, full }) {
|
||||
{noteNotebooks.length > 1 && !full ? (
|
||||
<Button
|
||||
title={`See all linked notebooks`}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
style={{
|
||||
alignSelf: "flex-end",
|
||||
marginRight: 12,
|
||||
|
||||
@@ -91,7 +91,7 @@ export const Synced = ({ item, close }) => {
|
||||
console.error(e);
|
||||
}
|
||||
}}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
title="Learn more"
|
||||
height={30}
|
||||
type="grayAccent"
|
||||
|
||||
@@ -57,7 +57,7 @@ export const Tags = ({ item, close }) => {
|
||||
icon="plus"
|
||||
iconPosition="right"
|
||||
height={30}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
style={{
|
||||
marginRight: 5,
|
||||
borderRadius: 100,
|
||||
@@ -107,7 +107,7 @@ const TagItem = ({ tag, close }) => {
|
||||
title={"#" + tag}
|
||||
type="grayBg"
|
||||
height={20}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
style={style}
|
||||
textStyle={{
|
||||
textDecorationLine: "underline"
|
||||
|
||||
@@ -39,7 +39,7 @@ export const Topics = ({ item, close }) => {
|
||||
height={30}
|
||||
onPress={() => open(topic)}
|
||||
icon="bookmark-outline"
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
style={{
|
||||
marginRight: 5,
|
||||
paddingHorizontal: 8,
|
||||
|
||||
@@ -42,6 +42,7 @@ import { SelectionProvider } from "./context";
|
||||
import { FilteredList } from "./filtered-list";
|
||||
import { ListItem } from "./list-item";
|
||||
import { useItemSelectionStore } from "./store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
|
||||
const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
@@ -263,7 +264,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
if (itemState[id] === "selected") {
|
||||
if (item.type === "notebook") {
|
||||
for (let noteId of noteIds) {
|
||||
db.relations.add(item, { id: noteId, type: "note" });
|
||||
await db.relations.add(item, { id: noteId, type: "note" });
|
||||
}
|
||||
} else {
|
||||
await db.notes.addToNotebook(
|
||||
@@ -278,7 +279,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
} else if (itemState[id] === "deselected") {
|
||||
if (item.type === "notebook") {
|
||||
for (let noteId of noteIds) {
|
||||
db.relations.unlink(item, { id: noteId, type: "note" });
|
||||
await db.relations.unlink(item, { id: noteId, type: "note" });
|
||||
}
|
||||
} else {
|
||||
await db.notes.removeFromNotebook(
|
||||
@@ -296,6 +297,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
setNotebooks();
|
||||
eSendEvent(eOnTopicSheetUpdate);
|
||||
SearchService.updateAndSearch();
|
||||
useRelationStore.getState().update();
|
||||
actionSheetRef.current?.hide();
|
||||
};
|
||||
|
||||
|
||||
@@ -158,11 +158,7 @@ export const MoveNotes = ({
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
{item.type == "note" && item.headline ? (
|
||||
<Paragraph
|
||||
numberOfLines={1}
|
||||
color={colors.icon}
|
||||
size={SIZE.xs + 1}
|
||||
>
|
||||
<Paragraph numberOfLines={1} color={colors.icon} size={SIZE.xs}>
|
||||
{item.headline}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
|
||||
@@ -43,8 +43,7 @@ import Seperator from "../../ui/seperator";
|
||||
import SheetWrapper from "../../ui/sheet";
|
||||
import { QRCode } from "../../ui/svg/lazy";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
|
||||
let RNFetchBlob;
|
||||
import RNFetchBlob from "rn-fetch-blob";
|
||||
|
||||
class RecoveryKeySheet extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -115,7 +114,6 @@ class RecoveryKeySheet extends React.Component {
|
||||
this.svg.current?.toDataURL(async (data) => {
|
||||
try {
|
||||
let path;
|
||||
RNFetchBlob = (await import("rn-fetch-blob")).default;
|
||||
let fileName = "nn_" + this.user.email + "_recovery_key_qrcode";
|
||||
fileName = sanitizeFilename(fileName, { replacement: "_" });
|
||||
fileName = fileName + ".png";
|
||||
@@ -151,7 +149,6 @@ class RecoveryKeySheet extends React.Component {
|
||||
fileName = sanitizeFilename(fileName, { replacement: "_" });
|
||||
fileName = fileName + ".txt";
|
||||
|
||||
RNFetchBlob = (await import("rn-fetch-blob")).default;
|
||||
if (Platform.OS === "android") {
|
||||
let file = await ScopedStorage.createDocument(
|
||||
fileName,
|
||||
|
||||
@@ -119,7 +119,7 @@ export default function ReminderNotify({
|
||||
marginTop: 10
|
||||
}}
|
||||
>
|
||||
<Paragraph size={SIZE.xs + 1}>Remind me in:</Paragraph>
|
||||
<Paragraph size={SIZE.xs}>Remind me in:</Paragraph>
|
||||
{QuickActions.map((item) => {
|
||||
return (
|
||||
<Button
|
||||
@@ -127,7 +127,7 @@ export default function ReminderNotify({
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
height={30}
|
||||
fontSize={SIZE.xs + 1}
|
||||
fontSize={SIZE.xs}
|
||||
style={{ marginLeft: 10, borderRadius: 100 }}
|
||||
onPress={() => onSnooze(item.time)}
|
||||
/>
|
||||
@@ -150,7 +150,7 @@ export default function ReminderNotify({
|
||||
<Paragraph
|
||||
style={{
|
||||
color: colors.icon,
|
||||
fontSize: SIZE.xs + 1,
|
||||
fontSize: SIZE.xs,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -550,7 +550,7 @@ export default function ReminderSheet({
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
|
||||
<Paragraph size={SIZE.xs} color={colors.icon}>
|
||||
{recurringMode === RecurringModes.Daily
|
||||
? "Repeats daily " + `at ${dayjs(date).format("hh:mm A")}.`
|
||||
: selectedDays.length === 7 &&
|
||||
|
||||
@@ -44,7 +44,8 @@ import { Button } from "../../ui/button";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import SheetWrapper from "../../ui/sheet";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
let RNFetchBlob;
|
||||
import RNFetchBlob from "rn-fetch-blob";
|
||||
|
||||
const RestoreDataSheet = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [restoring, setRestoring] = useState(false);
|
||||
@@ -213,7 +214,6 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
RNFetchBlob = (await import("rn-fetch-blob")).default;
|
||||
let path = await storage.checkAndCreateDir("/backups/");
|
||||
files = await RNFetchBlob.fs.lstat(path);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ import { getElevation } from "../../../utils";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { IconButton } from "../icon-button";
|
||||
import Paragraph from "../typography/paragraph";
|
||||
import phone from "phone";
|
||||
|
||||
interface InputProps extends TextInputProps {
|
||||
fwdRef?: RefObject<TextInput>;
|
||||
validationType?:
|
||||
@@ -149,7 +151,6 @@ const Input = ({
|
||||
isError = customValidator && value === customValidator();
|
||||
break;
|
||||
case "phonenumber": {
|
||||
const { default: phone } = await import("phone");
|
||||
const result = phone(value, {
|
||||
strictDetection: true,
|
||||
validateMobilePrefix: true
|
||||
|
||||
@@ -65,7 +65,7 @@ export const Notice = ({
|
||||
flexShrink: 1
|
||||
}}
|
||||
selectable={selectable}
|
||||
size={isSmall ? SIZE.xs + 1 : SIZE.sm}
|
||||
size={isSmall ? SIZE.xs : SIZE.sm}
|
||||
>
|
||||
{text}
|
||||
</Paragraph>
|
||||
|
||||
@@ -19,9 +19,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { FeatureType } from "./components/sheets/new-feature";
|
||||
|
||||
export const features: FeatureType[] = [
|
||||
{
|
||||
title: "Default font size & font family",
|
||||
body: "Now you can set default font size and font family in editor that will be used across all your new and old notes."
|
||||
}
|
||||
];
|
||||
export const features: FeatureType[] = [];
|
||||
|
||||
@@ -520,7 +520,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
}
|
||||
|
||||
async function showAttachments() {
|
||||
AttachmentDialog.present();
|
||||
AttachmentDialog.present(item);
|
||||
}
|
||||
|
||||
async function exportNote() {
|
||||
@@ -613,7 +613,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
},
|
||||
{
|
||||
id: "pin",
|
||||
title: "Pin",
|
||||
title: item.pinned ? "Unpin" : "Pin",
|
||||
icon: item.pinned ? "pin-off-outline" : "pin-outline",
|
||||
func: pinItem,
|
||||
close: false,
|
||||
@@ -623,7 +623,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
},
|
||||
{
|
||||
id: "favorite",
|
||||
title: "Favorite",
|
||||
title: item.favorite ? "Unfavorite" : "Favorite",
|
||||
icon: item.favorite ? "star-off" : "star-outline",
|
||||
func: addToFavorites,
|
||||
close: false,
|
||||
@@ -701,7 +701,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
},
|
||||
{
|
||||
id: "read-only",
|
||||
title: "Read only",
|
||||
title: "Readonly",
|
||||
icon: "pencil-lock",
|
||||
func: toggleReadyOnlyMode,
|
||||
on: item.readonly
|
||||
|
||||
@@ -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 { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useAttachmentStore } from "../stores/use-attachment-store";
|
||||
|
||||
type AttachmentProgress = {
|
||||
@@ -41,22 +41,24 @@ export const useAttachmentProgress = (
|
||||
: undefined
|
||||
);
|
||||
|
||||
const attachmentProgress = progress?.[attachment.metadata.hash];
|
||||
if (attachmentProgress) {
|
||||
const type = attachmentProgress.type;
|
||||
const loaded =
|
||||
attachmentProgress.type === "download"
|
||||
? attachmentProgress.recieved
|
||||
: attachmentProgress.sent;
|
||||
const value = loaded / attachmentProgress.total;
|
||||
setCurrentProgress({
|
||||
value: value * 100,
|
||||
percent: (value * 100).toFixed(0) + "%",
|
||||
type: type
|
||||
});
|
||||
} else {
|
||||
setCurrentProgress(undefined);
|
||||
}
|
||||
useEffect(() => {
|
||||
const attachmentProgress = progress?.[attachment.metadata.hash];
|
||||
if (attachmentProgress) {
|
||||
const type = attachmentProgress.type;
|
||||
const loaded =
|
||||
attachmentProgress.type === "download"
|
||||
? attachmentProgress.recieved
|
||||
: attachmentProgress.sent;
|
||||
const value = loaded / attachmentProgress.total;
|
||||
setCurrentProgress({
|
||||
value: value * 100,
|
||||
percent: (value * 100).toFixed(0) + "%",
|
||||
type: type
|
||||
});
|
||||
} else {
|
||||
setCurrentProgress(undefined);
|
||||
}
|
||||
}, [attachment.metadata.hash, progress]);
|
||||
|
||||
return [currentProgress, setCurrentProgress];
|
||||
};
|
||||
|
||||
@@ -128,7 +128,6 @@ const Editor = React.memo(
|
||||
renderKey.current =
|
||||
renderKey.current === `editor-0` ? `editor-1` : `editor-0`;
|
||||
editor.setLoading(true);
|
||||
setTimeout(() => editor.setLoading(false), 10);
|
||||
}, [editor]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -167,6 +166,7 @@ const Editor = React.memo(
|
||||
injectedJavaScript={`globalThis.sessionId="${editor.sessionId}";`}
|
||||
javaScriptEnabled={true}
|
||||
focusable={true}
|
||||
onContentProcessDidTerminate={onError}
|
||||
setSupportMultipleWindows={false}
|
||||
overScrollMode="never"
|
||||
scrollEnabled={false}
|
||||
|
||||
@@ -101,8 +101,9 @@ const EditorOverlay = ({ editorId = "", editor }) => {
|
||||
setTimeout(() => {
|
||||
if (!loadingState.current.startTime) {
|
||||
translateValue.value = 6000;
|
||||
opacity.value = 0;
|
||||
}
|
||||
}, 1000);
|
||||
}, 3000);
|
||||
eSubscribeEvent("loadingNote" + editorId, load);
|
||||
return () => {
|
||||
clearTimers();
|
||||
|
||||
@@ -80,8 +80,8 @@ export const useEditor = (
|
||||
const lockedSessionId = useRef<string>();
|
||||
|
||||
const postMessage = useCallback(
|
||||
async <T>(type: string, data: T) =>
|
||||
await post(editorRef, sessionIdRef.current, type, data),
|
||||
async <T>(type: string, data: T, waitFor = 300) =>
|
||||
await post(editorRef, sessionIdRef.current, type, data, waitFor),
|
||||
[sessionIdRef]
|
||||
);
|
||||
|
||||
@@ -120,9 +120,13 @@ export const useEditor = (
|
||||
[editorId]
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
setLoading(false);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
setLoading(false);
|
||||
} else {
|
||||
state.current.ready = false;
|
||||
}
|
||||
}, [loading]);
|
||||
|
||||
const withTimer = useCallback(
|
||||
(id: string, fn: () => void, duration: number) => {
|
||||
@@ -336,7 +340,6 @@ export const useEditor = (
|
||||
) => {
|
||||
state.current.currentlyEditing = true;
|
||||
const editorState = useEditorStore.getState();
|
||||
|
||||
if (item && item.type === "new") {
|
||||
currentNote.current && (await reset());
|
||||
const nextSessionId = makeSessionId(item as NoteType);
|
||||
@@ -344,7 +347,7 @@ export const useEditor = (
|
||||
sessionIdRef.current = nextSessionId;
|
||||
sessionHistoryId.current = Date.now();
|
||||
await commands.setSessionId(nextSessionId);
|
||||
await commands.focus();
|
||||
if (state.current?.ready) await commands.focus();
|
||||
lastContentChangeTime.current = 0;
|
||||
useEditorStore.getState().setReadonly(false);
|
||||
} else {
|
||||
@@ -358,7 +361,7 @@ export const useEditor = (
|
||||
!currentContent.current?.data ||
|
||||
currentContent.current?.data.length < 50000
|
||||
) {
|
||||
overlay(false);
|
||||
if (state.current.ready) overlay(false);
|
||||
} else {
|
||||
overlay(true);
|
||||
}
|
||||
@@ -372,7 +375,11 @@ export const useEditor = (
|
||||
currentNote.current = item as NoteType;
|
||||
await commands.setStatus(timeConverter(item.dateEdited), "Saved");
|
||||
await postMessage(EditorEvents.title, item.title);
|
||||
await postMessage(EditorEvents.html, currentContent.current?.data);
|
||||
await postMessage(
|
||||
EditorEvents.html,
|
||||
currentContent.current?.data,
|
||||
10000
|
||||
);
|
||||
useEditorStore.getState().setReadonly(item.readonly);
|
||||
await commands.setTags(currentNote.current);
|
||||
commands.setSettings();
|
||||
@@ -578,6 +585,10 @@ export const useEditor = (
|
||||
lastContentChangeTime.current = Date.now();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
state.current.saveCount = 0;
|
||||
}, [sessionId, loading]);
|
||||
|
||||
const onReady = useCallback(async () => {
|
||||
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
|
||||
eSendEvent("webview_reset");
|
||||
@@ -586,30 +597,24 @@ export const useEditor = (
|
||||
}
|
||||
}, [isDefaultEditor, restoreEditorState]);
|
||||
|
||||
useEffect(() => {
|
||||
state.current.saveCount = 0;
|
||||
(async () => {
|
||||
await commands.setSessionId(sessionIdRef.current);
|
||||
if (sessionIdRef.current) {
|
||||
if (!state.current?.ready) return;
|
||||
await onReady();
|
||||
}
|
||||
})();
|
||||
}, [sessionId, loading, commands, onReady]);
|
||||
|
||||
const onLoad = useCallback(async () => {
|
||||
state.current.ready = true;
|
||||
onReady();
|
||||
postMessage(EditorEvents.theme, theme || useThemeStore.getState().colors);
|
||||
commands.setInsets(
|
||||
isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 }
|
||||
);
|
||||
if (currentNote.current) {
|
||||
loadNote({ ...currentNote.current, forced: true });
|
||||
} else {
|
||||
await commands.setPlaceholder(placeholderTip.current);
|
||||
}
|
||||
commands.setSettings();
|
||||
if (currentNote.current) overlay(true);
|
||||
clearTimeout(timers.current["editor:loaded"]);
|
||||
timers.current["editor:loaded"] = setTimeout(async () => {
|
||||
postMessage(EditorEvents.theme, theme || useThemeStore.getState().colors);
|
||||
commands.setInsets(
|
||||
isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 }
|
||||
);
|
||||
await commands.setSessionId(sessionIdRef.current);
|
||||
await onReady();
|
||||
await commands.setSettings();
|
||||
if (currentNote.current) {
|
||||
loadNote({ ...currentNote.current, forced: true });
|
||||
} else {
|
||||
await commands.setPlaceholder(placeholderTip.current);
|
||||
}
|
||||
state.current.ready = true;
|
||||
}, 300);
|
||||
}, [
|
||||
onReady,
|
||||
postMessage,
|
||||
@@ -617,7 +622,8 @@ export const useEditor = (
|
||||
commands,
|
||||
isDefaultEditor,
|
||||
insets,
|
||||
loadNote
|
||||
loadNote,
|
||||
overlay
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -73,7 +73,8 @@ export async function post<T>(
|
||||
ref: RefObject<WebView>,
|
||||
sessionId: string,
|
||||
type: string,
|
||||
value: T | null = null
|
||||
value: T | null = null,
|
||||
waitFor = 300
|
||||
) {
|
||||
if (!sessionId) {
|
||||
console.warn("post called without sessionId of type:", type);
|
||||
@@ -85,7 +86,7 @@ export async function post<T>(
|
||||
sessionId: sessionId
|
||||
};
|
||||
setImmediate(() => ref.current?.postMessage(JSON.stringify(message)));
|
||||
const response = await getResponse(type);
|
||||
const response = await getResponse(type, waitFor);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -97,7 +98,8 @@ type WebviewResponseData = {
|
||||
};
|
||||
|
||||
export const getResponse = async (
|
||||
type: string
|
||||
type: string,
|
||||
waitFor = 300
|
||||
): Promise<WebviewResponseData | false> => {
|
||||
return new Promise((resolve) => {
|
||||
const callback = (data: WebviewResponseData) => {
|
||||
@@ -107,7 +109,7 @@ export const getResponse = async (
|
||||
eSubscribeEvent(type, callback);
|
||||
setTimeout(() => {
|
||||
resolve(false);
|
||||
}, 5000);
|
||||
}, waitFor);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
get: () => {
|
||||
const notebook = db.notebooks?.notebook(params?.current?.item?.id)
|
||||
?.data as NotebookType;
|
||||
if (!notebook) return [];
|
||||
return db.relations?.from(notebook, "note");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -109,7 +109,7 @@ const SettingsUserSection = ({ item }) => {
|
||||
flexGrow: 1
|
||||
}}
|
||||
>
|
||||
<Heading color={colors.accent} size={SIZE.xs + 1}>
|
||||
<Heading color={colors.accent} size={SIZE.xs}>
|
||||
{SUBSCRIPTION_STATUS_STRINGS[
|
||||
user.subscription?.type
|
||||
]?.toUpperCase() || "Basic"}
|
||||
|
||||
@@ -45,6 +45,7 @@ import { sleep } from "../utils/time";
|
||||
import { useRelationStore } from "../stores/use-relation-store";
|
||||
import { useReminderStore } from "../stores/use-reminder-store";
|
||||
import { presentDialog } from "../components/dialog/functions";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
|
||||
export type Reminder = {
|
||||
id: string;
|
||||
@@ -193,7 +194,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
case "Hide":
|
||||
unpinQuickNote();
|
||||
break;
|
||||
case "ReplyInput":
|
||||
case "ReplyInput": {
|
||||
displayNotification({
|
||||
title: "Quick note",
|
||||
message: 'Tap on "Take note" to add a note.',
|
||||
@@ -203,16 +204,25 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
reply_button_text: "Take note",
|
||||
reply_placeholder_text: "Write something..."
|
||||
});
|
||||
await initDatabase(false);
|
||||
if (!db.isInitialized) await db.init();
|
||||
await db.notes?.init();
|
||||
await db.notes?.add({
|
||||
content: {
|
||||
type: "tiptap",
|
||||
data: `<p>${input} </p>`
|
||||
}
|
||||
});
|
||||
await db.notes?.init();
|
||||
const status = await NetInfo.fetch();
|
||||
if (status.isInternetReachable) {
|
||||
try {
|
||||
await db.sync(false, false);
|
||||
} catch (e) {
|
||||
console.log(e, (e as Error).stack);
|
||||
}
|
||||
}
|
||||
useNoteStore.getState().setNotes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -164,7 +164,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 2044
|
||||
versionCode 2046
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
- Added default font size & font family settings
|
||||
- Redesign reminder/notebook sheets to improve UX
|
||||
- Fixed an issue causing sync to fail
|
||||
- Fix tag suggestions in share extension
|
||||
- Fix app font sizes too small in some places
|
||||
- Improved mobile editor performance on low-end devices
|
||||
- Fix pin to notifications not working
|
||||
- Bug fixes and performance improvements
|
||||
- Fix a crash when tryin to upload attachments
|
||||
- Fix editor not rendered when enter foreground after a long time on iOS
|
||||
- Increased hit area for outline list to make toggling outline list items easier
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -7,6 +7,15 @@ import Config from 'react-native-config';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import appJson from './app.json';
|
||||
import Notifications from "../app/services/notifications";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
NetInfo.configure({
|
||||
reachabilityUrl: "https://notesnook.com",
|
||||
reachabilityTest: (response) => {
|
||||
if (!response) return false;
|
||||
return response?.status >= 200 && response?.status < 300;
|
||||
}
|
||||
});
|
||||
|
||||
Notifications.init();
|
||||
|
||||
const appName = appJson.name;
|
||||
|
||||
@@ -993,7 +993,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
CURRENT_PROJECT_VERSION = 2038;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1067,7 +1067,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
MARKETING_VERSION = 2.4.15;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1097,7 +1097,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
CURRENT_PROJECT_VERSION = 2038;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
@@ -1170,7 +1170,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
MARKETING_VERSION = 2.4.15;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1328,7 +1328,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
CURRENT_PROJECT_VERSION = 2038;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1340,7 +1340,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
MARKETING_VERSION = 2.4.15;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1370,7 +1370,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
CURRENT_PROJECT_VERSION = 2038;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1382,7 +1382,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
MARKETING_VERSION = 2.4.15;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1411,7 +1411,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
CURRENT_PROJECT_VERSION = 2038;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1485,7 +1485,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
MARKETING_VERSION = 2.4.15;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1515,7 +1515,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2036;
|
||||
CURRENT_PROJECT_VERSION = 2038;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1589,7 +1589,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.13;
|
||||
MARKETING_VERSION = 2.4.15;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
- Added default font size & font family settings
|
||||
- Redesign reminder/notebook sheets to improve UX
|
||||
- Fixed an issue causing sync to fail
|
||||
- Fix tag suggestions in share extension
|
||||
- Fix app font sizes too small in some places
|
||||
- Improved mobile editor performance on low-end devices
|
||||
- Fix pin to notifications not working
|
||||
- Bug fixes and performance improvements
|
||||
- Fix a crash when tryin to upload attachments
|
||||
- Fix editor not rendered when enter foreground after a long time on iOS
|
||||
- Increased hit area for outline list to make toggling outline list items easier
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -168,6 +168,7 @@ module.exports = (env) => {
|
||||
/node_modules(.*[/\\])+@microsoft/,
|
||||
/node_modules(.*[/\\])+@msgpack/,
|
||||
/node_modules(.*[/\\])+liqe/,
|
||||
/node_modules(.*[/\\])+leac/,
|
||||
/node_modules(.*[/\\])+selderee/,
|
||||
/node_modules(.*[/\\])+html-to-text/,
|
||||
/node_modules(.*[/\\])+buffer/,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.4.13",
|
||||
"version": "2.4.15",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
diff --git a/node_modules/leac/lib/leac.cjs b/node_modules/leac/lib/leac.cjs
|
||||
index 13123da..9d490e2 100644
|
||||
--- a/node_modules/leac/lib/leac.cjs
|
||||
+++ b/node_modules/leac/lib/leac.cjs
|
||||
@@ -1 +1,83 @@
|
||||
-"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=/\n/g;function t(t){const o=[...t.matchAll(e)].map((e=>e.index||0));o.unshift(-1);const s=n(o,0,o.length);return e=>r(s,e)}function n(e,t,r){if(r-t==1)return{offset:e[t],index:t+1};const o=Math.ceil((t+r)/2),s=n(e,t,o),l=n(e,o,r);return{offset:s.offset,low:s,high:l}}function r(e,t){return function(e){return Object.prototype.hasOwnProperty.call(e,"index")}(e)?{line:e.index,column:t-e.offset}:r(e.high.offset<t?e.high:e.low,t)}function o(e,t){return{...e,regex:s(e,t)}}function s(e,t){if(0===e.name.length)throw new Error(`Rule #${t} has empty name, which is not allowed.`);if(function(e){return Object.prototype.hasOwnProperty.call(e,"regex")}(e))return function(e){if(e.global)throw new Error(`Regular expression /${e.source}/${e.flags} contains the global flag, which is not allowed.`);return e.sticky?e:new RegExp(e.source,e.flags+"y")}(e.regex);if(function(e){return Object.prototype.hasOwnProperty.call(e,"str")}(e)){if(0===e.str.length)throw new Error(`Rule #${t} ("${e.name}") has empty "str" property, which is not allowed.`);return new RegExp(l(e.str),"y")}return new RegExp(l(e.name),"y")}function l(e){return e.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g,"\\$&")}exports.createLexer=function(e,n="",r={}){const s="string"!=typeof n?n:r,l="string"==typeof n?n:"",c=e.map(o),i=!!s.lineNumbers;return function(e,n=0){const r=i?t(e):()=>({line:0,column:0});let o=n;const s=[];e:for(;o<e.length;){let t=!1;for(const n of c){n.regex.lastIndex=o;const c=n.regex.exec(e);if(c&&c[0].length>0){if(!n.discard){const e=r(o),t="string"==typeof n.replace?c[0].replace(new RegExp(n.regex.source,n.regex.flags),n.replace):c[0];s.push({state:l,name:n.name,text:t,offset:o,len:c[0].length,line:e.line,column:e.column})}if(o=n.regex.lastIndex,t=!0,n.push){const t=n.push(e,o);s.push(...t.tokens),o=t.offset}if(n.pop)break e;break}}if(!t)break}return{tokens:s,offset:o,complete:e.length<=o}}};
|
||||
+"use strict";
|
||||
+Object.defineProperty(exports, "__esModule", { value: !0 });
|
||||
+const e = /\n/g;
|
||||
+function t(t) {
|
||||
+ const o = [...t.matchAll(e)].map((e) => e.index || 0);
|
||||
+ o.unshift(-1);
|
||||
+ const s = n(o, 0, o.length);
|
||||
+ return (e) => r(s, e);
|
||||
+}
|
||||
+function n(e, t, r) {
|
||||
+ if (r - t == 1) return { offset: e[t], index: t + 1 };
|
||||
+ const o = Math.ceil((t + r) / 2),
|
||||
+ s = n(e, t, o),
|
||||
+ l = n(e, o, r);
|
||||
+ return { offset: s.offset, low: s, high: l };
|
||||
+}
|
||||
+function r(e, t) {
|
||||
+ return (function (e) {
|
||||
+ return Object.prototype.hasOwnProperty.call(e, "index");
|
||||
+ })(e)
|
||||
+ ? { line: e.index, column: t - e.offset }
|
||||
+ : r(e.high.offset < t ? e.high : e.low, t);
|
||||
+}
|
||||
+function _o(e, t) {
|
||||
+ return { ...e, regex: s(e, t) };
|
||||
+}
|
||||
+function s(e, t) {
|
||||
+ if (0 === e.name.length) throw new Error(`Rule #${t} has empty name, which is not allowed.`);
|
||||
+ if (
|
||||
+ (function (e) {
|
||||
+ return Object.prototype.hasOwnProperty.call(e, "regex");
|
||||
+ })(e)
|
||||
+ )
|
||||
+ return (function (e) {
|
||||
+ if (e.global) throw new Error(`Regular expression /${e.source}/${e.flags} contains the global flag, which is not allowed.`);
|
||||
+ return e.sticky ? e : new RegExp(e.source, e.flags + "y");
|
||||
+ })(e.regex);
|
||||
+ if (
|
||||
+ (function (e) {
|
||||
+ return Object.prototype.hasOwnProperty.call(e, "str");
|
||||
+ })(e)
|
||||
+ ) {
|
||||
+ if (0 === e.str.length) throw new Error(`Rule #${t} ("${e.name}") has empty "str" property, which is not allowed.`);
|
||||
+ return new RegExp(l(e.str), "y");
|
||||
+ }
|
||||
+ return new RegExp(l(e.name), "y");
|
||||
+}
|
||||
+function l(e) {
|
||||
+ return e.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g, "\\$&");
|
||||
+}
|
||||
+exports.createLexer = function (_e, n = "", r = {}) {
|
||||
+ const s = "string" != typeof n ? n : r,
|
||||
+ l = "string" == typeof n ? n : "",
|
||||
+ i = !!s.lineNumbers;
|
||||
+ return function (e, n = 0) {
|
||||
+ const r = i ? t(e) : () => ({ line: 0, column: 0 });
|
||||
+ let o = n;
|
||||
+ const s = [];
|
||||
+ e: for (; o < e.length; ) {
|
||||
+ let t = !1;
|
||||
+ let z = _e.map(_o);
|
||||
+ for (const n of z) {
|
||||
+ n.regex.lastIndex = o;
|
||||
+ const c = n.regex.exec(e);
|
||||
+ if (c && c[0].length > 0) {
|
||||
+ if (!n.discard) {
|
||||
+ const e = r(o),
|
||||
+ t = "string" == typeof n.replace ? c[0].replace(new RegExp(n.regex.source, n.regex.flags), n.replace) : c[0];
|
||||
+ s.push({ state: l, name: n.name, text: t, offset: o, len: c[0].length, line: e.line, column: e.column });
|
||||
+ }
|
||||
+ if (((o = n.regex.lastIndex), (t = !0), n.push)) {
|
||||
+ const t = n.push(e, o);
|
||||
+ s.push(...t.tokens), (o = t.offset);
|
||||
+ }
|
||||
+ if (n.pop) break e;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (!t) break;
|
||||
+ }
|
||||
+ return { tokens: s, offset: o, complete: true };
|
||||
+ };
|
||||
+};
|
||||
@@ -26,12 +26,12 @@ import {
|
||||
Keyboard,
|
||||
Platform,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
ScrollView
|
||||
useWindowDimensions
|
||||
} from "react-native";
|
||||
import {
|
||||
SafeAreaProvider,
|
||||
@@ -45,10 +45,10 @@ import Storage from "../app/common/database/storage";
|
||||
import { eSendEvent } from "../app/services/event-manager";
|
||||
import { getElevation } from "../app/utils";
|
||||
import { eOnLoadNote } from "../app/utils/events";
|
||||
import { sleep } from "../app/utils/time";
|
||||
import { Editor } from "./editor";
|
||||
import { Search } from "./search";
|
||||
import { initDatabase, useShareStore } from "./store";
|
||||
import { Editor } from "./editor";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
const getLinkPreview = (url) => {
|
||||
return getPreviewData(url, 5000);
|
||||
};
|
||||
@@ -63,16 +63,15 @@ async function sanitizeHtml(site) {
|
||||
}
|
||||
|
||||
function makeHtmlFromUrl(url) {
|
||||
return `<a style="overflow-wrap:anywhere;white-space:pre-wrap" href='${url}' target='_blank'>${url}</a>`;
|
||||
return `<a href='${url}' target='_blank'>${url}</a>`;
|
||||
}
|
||||
|
||||
function makeHtmlFromPlainText(text) {
|
||||
if (!text) return "";
|
||||
|
||||
return `<p style="overflow-wrap:anywhere;white-space:pre-wrap" >${text.replace(
|
||||
/(?:\r\n|\r|\n)/g,
|
||||
"<br>"
|
||||
)}</p>`;
|
||||
return `<p>${text
|
||||
.replace(/[\n]+/g, "\n")
|
||||
.replace(/(?:\r\n|\r|\n)/g, "</p><p>")}</p>`;
|
||||
}
|
||||
|
||||
function getBaseUrl(site) {
|
||||
@@ -315,7 +314,6 @@ const ShareView = ({ quicknote = false }) => {
|
||||
const onPress = async () => {
|
||||
setLoading(true);
|
||||
await initDatabase();
|
||||
await sleep(1500);
|
||||
if (!noteContent.current) return;
|
||||
if (appendNote && !db.notes.note(appendNote.id)) {
|
||||
useShareStore.getState().setAppendNote(null);
|
||||
@@ -358,6 +356,14 @@ const ShareView = ({ quicknote = false }) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
const status = await NetInfo.fetch();
|
||||
if (status.isInternetReachable) {
|
||||
try {
|
||||
await db.sync(false, false);
|
||||
} catch (e) {
|
||||
console.log(e, e.stack);
|
||||
}
|
||||
}
|
||||
await Storage.write("notesAddedFromIntent", "added");
|
||||
close();
|
||||
setLoading(false);
|
||||
|
||||
@@ -29,8 +29,7 @@ import { db } from "../app/common/database";
|
||||
|
||||
export async function initDatabase() {
|
||||
if (!db.isInitialized) {
|
||||
// Only load collections in database.
|
||||
await db.initCollections();
|
||||
await db.init();
|
||||
}
|
||||
await db.notes.init();
|
||||
}
|
||||
|
||||
4
apps/web/desktop/package-lock.json
generated
4
apps/web/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.4.8",
|
||||
"version": "2.4.10",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.4.8",
|
||||
"version": "2.4.10",
|
||||
"dependencies": {
|
||||
"diary": "^0.3.1",
|
||||
"electron-updater": "^5.3.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.4.8",
|
||||
"version": "2.4.10",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./build/electron.js",
|
||||
|
||||
@@ -22,8 +22,8 @@ import { isDevelopment, getPath } from "./utils";
|
||||
import { createReadStream } from "fs";
|
||||
import { extname, normalize } from "path";
|
||||
import { logger } from "./logger";
|
||||
import { Blob } from "buffer";
|
||||
import { URL } from "url";
|
||||
import { Blob } from "buffer";
|
||||
|
||||
const FILE_NOT_FOUND = -6;
|
||||
const BASE_PATH = isDevelopment() ? "../public" : "";
|
||||
@@ -75,9 +75,10 @@ function registerProtocol() {
|
||||
...request,
|
||||
body,
|
||||
headers: {
|
||||
...request.headers,
|
||||
origin: `${PROTOCOL}://${HOSTNAME}/`
|
||||
...request.headers
|
||||
// origin: `${PROTOCOL}://${HOSTNAME}/`
|
||||
},
|
||||
referrer: request.referrer,
|
||||
redirect: "manual"
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -103,7 +104,7 @@ function registerProtocol() {
|
||||
);
|
||||
}
|
||||
|
||||
const bypassedRoutes = ["/notes/index_v14.json", "/notes/welcome-web"];
|
||||
const bypassedRoutes = [];
|
||||
function shouldInterceptRequest(url) {
|
||||
let shouldIntercept = url.hostname === HOSTNAME;
|
||||
return shouldIntercept && !bypassedRoutes.includes(url.pathname);
|
||||
@@ -117,14 +118,14 @@ async function getBody(request) {
|
||||
/**
|
||||
* @type {Electron.Session}
|
||||
*/
|
||||
const session = globalThis.window.webContents.session;
|
||||
const session = globalThis?.window?.webContents?.session;
|
||||
|
||||
const blobParts = [];
|
||||
if (!request.uploadData || !request.uploadData.length) return null;
|
||||
for (let data of request.uploadData) {
|
||||
if (data.type === "rawData") {
|
||||
blobParts.push(new Uint8Array(data.bytes));
|
||||
} else if (data.type === "blob") {
|
||||
} else if (session && data.type === "blob") {
|
||||
const buffer = await session.getBlobData(data.blobUUID);
|
||||
blobParts.push(new Uint8Array(buffer));
|
||||
}
|
||||
|
||||
4
apps/web/package-lock.json
generated
4
apps/web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.4.8",
|
||||
"version": "2.4.10",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.4.8",
|
||||
"version": "2.4.10",
|
||||
"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.4.8",
|
||||
"version": "2.4.10",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
|
||||
@@ -41,6 +41,8 @@ import { useCheckoutStore } from "./store";
|
||||
import { getCurrencySymbol } from "./helpers";
|
||||
import { Theme } from "@notesnook/theme";
|
||||
import { isMacStoreApp } from "../../../utils/platform";
|
||||
import { isUserSubscribed } from "../../../hooks/use-is-user-premium";
|
||||
import { SUBSCRIPTION_STATUS } from "../../../common/constants";
|
||||
|
||||
type BuyDialogProps = {
|
||||
couponCode?: string;
|
||||
@@ -52,7 +54,7 @@ export function BuyDialog(props: BuyDialogProps) {
|
||||
const { onClose, couponCode, plan } = props;
|
||||
const theme = useTheme() as Theme;
|
||||
|
||||
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
|
||||
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
useCheckoutStore.getState().reset();
|
||||
@@ -156,13 +158,13 @@ type SideBarProps = {
|
||||
};
|
||||
function SideBar(props: SideBarProps) {
|
||||
const { initialPlan, onClose } = props;
|
||||
const [showPlans, setShowPlans] = useState(!!initialPlan);
|
||||
const onPlanSelected = useCheckoutStore((state) => state.onPlanSelected);
|
||||
const [showPlans, setShowPlans] = useState(false);
|
||||
const onPlanSelected = useCheckoutStore((state) => state.selectPlan);
|
||||
const selectedPlan = useCheckoutStore((state) => state.selectedPlan);
|
||||
const pricingInfo = useCheckoutStore((state) => state.pricingInfo);
|
||||
const user = useUserStore((store) => store.user);
|
||||
const couponCode = useCheckoutStore((store) => store.couponCode);
|
||||
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
|
||||
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
|
||||
|
||||
if (user && selectedPlan)
|
||||
return (
|
||||
@@ -176,7 +178,13 @@ function SideBar(props: SideBarProps) {
|
||||
/>
|
||||
);
|
||||
|
||||
if (user && showPlans)
|
||||
if (user && !showPlans && isUserSubscribed(user)) {
|
||||
return (
|
||||
<AlreadyPremium user={user} onShowPlans={() => setShowPlans(true)} />
|
||||
);
|
||||
}
|
||||
|
||||
if (user && (showPlans || !!initialPlan))
|
||||
return (
|
||||
<PlansList
|
||||
onPlansLoaded={(plans) => {
|
||||
@@ -221,7 +229,7 @@ function SideBar(props: SideBarProps) {
|
||||
function Details() {
|
||||
const user = useUserStore((store) => store.user);
|
||||
const selectedPlan = useCheckoutStore((state) => state.selectedPlan);
|
||||
const onPriceUpdated = useCheckoutStore((state) => state.onPriceUpdated);
|
||||
const onPriceUpdated = useCheckoutStore((state) => state.updatePrice);
|
||||
const couponCode = useCheckoutStore((store) => store.couponCode);
|
||||
const setIsApplyingCoupon = useCheckoutStore(
|
||||
(store) => store.setIsApplyingCoupon
|
||||
@@ -347,6 +355,47 @@ function TrialOrUpgrade(props: TrialOrUpgradeProps) {
|
||||
);
|
||||
}
|
||||
|
||||
type AlreadyPremiumProps = {
|
||||
user: User | undefined;
|
||||
onShowPlans: () => void;
|
||||
};
|
||||
function AlreadyPremium(props: AlreadyPremiumProps) {
|
||||
const { user, onShowPlans } = props;
|
||||
|
||||
const isCanceled =
|
||||
user?.subscription?.type === SUBSCRIPTION_STATUS.PREMIUM_CANCELED;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Rocket width={200} />
|
||||
<Text variant="heading" mt={4} sx={{ textAlign: "center" }}>
|
||||
Notesnook Pro
|
||||
</Text>
|
||||
{isCanceled ? (
|
||||
<>
|
||||
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
|
||||
Resubscribing to Notesnook Pro will replace your existing
|
||||
subscription.
|
||||
</Text>
|
||||
<Button
|
||||
variant="primary"
|
||||
mt={2}
|
||||
sx={{ borderRadius: 100, px: 6 }}
|
||||
onClick={onShowPlans}
|
||||
data-test-id="see-all-plans"
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
|
||||
You are already subscribed to Notesnook Pro.
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type SelectedPlanProps = {
|
||||
plan: Plan;
|
||||
pricingInfo: PricingInfo | undefined;
|
||||
@@ -360,7 +409,7 @@ function SelectedPlan(props: SelectedPlanProps) {
|
||||
store.setIsApplyingCoupon
|
||||
]);
|
||||
|
||||
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
|
||||
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
|
||||
const couponInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const applyCoupon = useCallback(() => {
|
||||
|
||||
@@ -23,13 +23,13 @@ import produce from "immer";
|
||||
|
||||
interface ICheckoutStore {
|
||||
selectedPlan?: Plan;
|
||||
onPlanSelected: (plan?: Plan) => void;
|
||||
selectPlan: (plan?: Plan) => void;
|
||||
pricingInfo?: PricingInfo;
|
||||
onPriceUpdated: (pricingInfo?: PricingInfo) => void;
|
||||
updatePrice: (pricingInfo?: PricingInfo) => void;
|
||||
isApplyingCoupon: boolean;
|
||||
setIsApplyingCoupon: (isApplyingCoupon: boolean) => void;
|
||||
couponCode?: string;
|
||||
onApplyCoupon: (couponCode?: string) => void;
|
||||
applyCoupon: (couponCode?: string) => void;
|
||||
reset: () => void;
|
||||
}
|
||||
export const useCheckoutStore = create<ICheckoutStore>((set) => ({
|
||||
@@ -37,20 +37,20 @@ export const useCheckoutStore = create<ICheckoutStore>((set) => ({
|
||||
pricingInfo: undefined,
|
||||
couponCode: undefined,
|
||||
isApplyingCoupon: false,
|
||||
onPlanSelected: (plan) =>
|
||||
selectPlan: (plan) =>
|
||||
set(
|
||||
produce((state: ICheckoutStore) => {
|
||||
state.selectedPlan = plan;
|
||||
state.pricingInfo = undefined;
|
||||
})
|
||||
),
|
||||
onPriceUpdated: (pricingInfo) =>
|
||||
updatePrice: (pricingInfo) =>
|
||||
set(
|
||||
produce((state: ICheckoutStore) => {
|
||||
state.pricingInfo = pricingInfo;
|
||||
})
|
||||
),
|
||||
onApplyCoupon: (couponCode) =>
|
||||
applyCoupon: (couponCode) =>
|
||||
set(
|
||||
produce((state: ICheckoutStore) => {
|
||||
state.couponCode = couponCode;
|
||||
|
||||
@@ -90,13 +90,7 @@ const features: Record<FeatureKeys, Feature> = {
|
||||
}
|
||||
]
|
||||
: isDesktop()
|
||||
? [
|
||||
{
|
||||
title: "Default font size & font family",
|
||||
subtitle:
|
||||
"You can now change the default font family and size used by the editor across all your older & newer notes. Go into Settings > Editor settings to change these settings."
|
||||
}
|
||||
]
|
||||
? []
|
||||
: [],
|
||||
cta: {
|
||||
title: "Got it",
|
||||
|
||||
@@ -41,3 +41,13 @@ export function isUserPremium(user?: User) {
|
||||
subStatus === SUBSCRIPTION_STATUS.TRIAL
|
||||
);
|
||||
}
|
||||
|
||||
export function isUserSubscribed(user?: User) {
|
||||
if (!user) user = userstore.get().user;
|
||||
|
||||
const subStatus = user?.subscription?.type;
|
||||
return (
|
||||
subStatus === SUBSCRIPTION_STATUS.PREMIUM ||
|
||||
subStatus === SUBSCRIPTION_STATUS.PREMIUM_CANCELED
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ import { EV, EVENTS, SYNC_CHECK_IDS } from "@notesnook/core/common";
|
||||
import { logger } from "../utils/logger";
|
||||
import Config from "../utils/config";
|
||||
import { onPageVisibilityChanged } from "../utils/page-visibility";
|
||||
import { NetworkCheck } from "../utils/network-check";
|
||||
|
||||
const networkCheck = new NetworkCheck();
|
||||
var syncStatusTimeout = 0;
|
||||
const BATCH_SIZE = 50;
|
||||
class AppStore extends BaseStore {
|
||||
@@ -128,7 +130,7 @@ class AppStore extends BaseStore {
|
||||
if (type === "online") {
|
||||
// a slight delay to make sure sockets are open and can be connected
|
||||
// to. Otherwise, this fails miserably.
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
await networkCheck.waitForInternet();
|
||||
}
|
||||
await db.connectSSE({ force: type === "online" }).catch(logger.error);
|
||||
} else if (type === "offline") {
|
||||
|
||||
38
apps/web/src/utils/network-check.ts
Normal file
38
apps/web/src/utils/network-check.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import Worker from "worker-loader?filename=static/workers/network-check.worker.[contenthash].js!./network-check.worker";
|
||||
import type { NetworkCheck as NetworkWorker } from "./network-check.worker";
|
||||
import { wrap, Remote } from "comlink";
|
||||
|
||||
export class NetworkCheck {
|
||||
private worker!: globalThis.Worker;
|
||||
private network!: Remote<NetworkWorker>;
|
||||
|
||||
constructor() {
|
||||
this.worker = new Worker();
|
||||
this.network = wrap<NetworkWorker>(this.worker);
|
||||
}
|
||||
|
||||
waitForInternet() {
|
||||
return this.network.waitForInternet();
|
||||
}
|
||||
}
|
||||
40
apps/web/src/utils/network-check.worker.ts
Normal file
40
apps/web/src/utils/network-check.worker.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { expose } from "comlink";
|
||||
|
||||
const module = {
|
||||
async waitForInternet() {
|
||||
let retries = 10;
|
||||
while (retries-- > 0) {
|
||||
try {
|
||||
const response = await fetch("https://api.notesnook.com/health");
|
||||
if (response.ok) return true;
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// wait a bit before trying again.
|
||||
await new Promise((resolve) => setTimeout(resolve, 2500));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
expose(module);
|
||||
export type NetworkCheck = typeof module;
|
||||
@@ -35,7 +35,7 @@ function isDocumentHidden() {
|
||||
|
||||
export function onPageVisibilityChanged(
|
||||
handler: (
|
||||
status: "online" | "offline" | "visibilitychange",
|
||||
status: "online" | "offline" | "visibilitychange" | "focus",
|
||||
bool: boolean
|
||||
) => void
|
||||
) {
|
||||
@@ -46,6 +46,8 @@ export function onPageVisibilityChanged(
|
||||
document.addEventListener(visibilityChange(), () =>
|
||||
handler("visibilitychange", isDocumentHidden())
|
||||
);
|
||||
|
||||
window.addEventListener("focus", () => handler("focus", false));
|
||||
}
|
||||
|
||||
function onDeviceOnline(handler: () => void) {
|
||||
|
||||
@@ -154,7 +154,7 @@ export const OutlineListItem = Node.create<ListItemOptions>({
|
||||
const clientY =
|
||||
e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
|
||||
|
||||
const hitArea = { width: 26, height: 24 };
|
||||
const hitArea = { width: 40, height: 40 };
|
||||
|
||||
const selection = editor.state.selection;
|
||||
const parent = findParentNode((node) => !!node.attrs.textDirection)(
|
||||
|
||||
@@ -18,10 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Flex } from "@theme-ui/components";
|
||||
import {
|
||||
ReactNodeView,
|
||||
ReactNodeViewProps,
|
||||
} from "../react";
|
||||
import { ReactNodeView, ReactNodeViewProps } from "../react";
|
||||
import { Node as ProsemirrorNode } from "prosemirror-model";
|
||||
import { Editor } from "../../types";
|
||||
import { Editor as TiptapEditor } from "@tiptap/core";
|
||||
@@ -263,7 +260,7 @@ function TableColumnToolbar(props: TableToolbarProps) {
|
||||
>
|
||||
<TableProperties
|
||||
editor={editor}
|
||||
title="tableProperties"
|
||||
title="Table properties"
|
||||
icon="more"
|
||||
variant={"small"}
|
||||
/>
|
||||
|
||||
@@ -54,13 +54,12 @@ const clean: ThemeUIStyleObject = {
|
||||
|
||||
const error: ThemeUIStyleObject = {
|
||||
variant: "forms.input",
|
||||
boxShadow: "0px 0px 0px 1px var(--error) inset",
|
||||
outline: "none",
|
||||
outline: "1.5px solid var(--error)",
|
||||
":focus": {
|
||||
boxShadow: "0px 0px 0px 1.5px var(--error) inset"
|
||||
outline: "2px solid var(--error)"
|
||||
},
|
||||
":hover:not(:focus)": {
|
||||
boxShadow: "0px 0px 0px 1px var(--error) inset"
|
||||
outline: "1.5px solid var(--error)"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user