mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
mobile: fix attachment downloads failing on mobile
This commit is contained in:
@@ -224,6 +224,13 @@ export default async function downloadAttachment(
|
||||
}
|
||||
|
||||
try {
|
||||
useAttachmentStore.getState().setDownloading({
|
||||
groupId: options.groupId || attachment.hash,
|
||||
current: 0,
|
||||
total: 1,
|
||||
filename: attachment.filename
|
||||
});
|
||||
|
||||
await db
|
||||
.fs()
|
||||
.downloadFile(
|
||||
@@ -231,6 +238,15 @@ export default async function downloadAttachment(
|
||||
attachment.hash,
|
||||
attachment.chunkSize
|
||||
);
|
||||
|
||||
useAttachmentStore.getState().setDownloading({
|
||||
groupId: options.groupId || attachment.hash,
|
||||
current: 1,
|
||||
total: 1,
|
||||
filename: attachment.filename,
|
||||
success: true
|
||||
});
|
||||
|
||||
if (!(await exists(attachment.hash))) {
|
||||
DatabaseLogger.log("Attachment does not exist after download.");
|
||||
return;
|
||||
@@ -301,6 +317,14 @@ export default async function downloadAttachment(
|
||||
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}_dcache`)
|
||||
.catch(console.log);
|
||||
}
|
||||
|
||||
useAttachmentStore.getState().setDownloading({
|
||||
groupId: options.groupId || attachment.hash,
|
||||
current: 0,
|
||||
total: 0,
|
||||
filename: attachment.filename,
|
||||
success: false
|
||||
});
|
||||
DatabaseLogger.error(e);
|
||||
useAttachmentStore.getState().remove(attachment.hash);
|
||||
if (options.throwError) {
|
||||
|
||||
@@ -63,9 +63,11 @@ const Actions = ({
|
||||
attachment,
|
||||
close,
|
||||
setAttachments,
|
||||
fwdRef
|
||||
fwdRef,
|
||||
context
|
||||
}: {
|
||||
attachment: Attachment;
|
||||
context: string;
|
||||
setAttachments: (attachments?: VirtualizedGrouping<Attachment>) => void;
|
||||
close?: () => void;
|
||||
fwdRef: RefObject<ActionSheetRef>;
|
||||
@@ -79,7 +81,6 @@ const Actions = ({
|
||||
const [loading, setLoading] = useState<{
|
||||
name?: string;
|
||||
}>({});
|
||||
|
||||
const actions = [
|
||||
{
|
||||
name: strings.network.download(),
|
||||
@@ -88,7 +89,7 @@ const Actions = ({
|
||||
await db.fs().cancel(attachment.hash);
|
||||
useAttachmentStore.getState().remove(attachment.hash);
|
||||
}
|
||||
downloadAttachment(attachment.hash, false);
|
||||
downloadAttachment(attachment.hash, context === "global");
|
||||
fwdRef.current?.hide();
|
||||
},
|
||||
icon: "download"
|
||||
@@ -375,6 +376,7 @@ Actions.present = (
|
||||
setAttachments={set}
|
||||
close={close}
|
||||
attachment={attachment}
|
||||
context={context || "global"}
|
||||
/>
|
||||
)
|
||||
});
|
||||
|
||||
@@ -206,7 +206,7 @@ export const AttachmentDialog = ({
|
||||
errorOnly={currentFilter === "errors"}
|
||||
attachments={attachments}
|
||||
id={index}
|
||||
context="global"
|
||||
context={!isSheet ? "global" : "attachments-list"}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -19,9 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useRef } from "react";
|
||||
import { Platform, StyleSheet, View } from "react-native";
|
||||
//@ts-ignore
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import Menu from "react-native-reanimated-material-menu";
|
||||
import { Menu } from "react-native-material-menu";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import {
|
||||
HeaderRightButton,
|
||||
@@ -97,14 +96,16 @@ export const RightMenus = ({
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
backgroundColor: contextMenuColors.primary.background,
|
||||
marginTop: -40
|
||||
marginTop: 35
|
||||
}}
|
||||
onRequestClose={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
}}
|
||||
anchor={
|
||||
<IconButton
|
||||
onPress={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.show();
|
||||
}}
|
||||
name="dots-vertical"
|
||||
@@ -116,9 +117,10 @@ export const RightMenus = ({
|
||||
{headerRightButtons.map((item) => (
|
||||
<Button
|
||||
style={{
|
||||
width: 150,
|
||||
justifyContent: "flex-start",
|
||||
borderRadius: 0
|
||||
borderRadius: 0,
|
||||
alignSelf: "flex-start",
|
||||
width: "100%"
|
||||
}}
|
||||
type="plain"
|
||||
buttonType={{
|
||||
@@ -127,6 +129,7 @@ export const RightMenus = ({
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
onPress={async () => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
if (Platform.OS === "ios") await sleep(300);
|
||||
item.onPress();
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
Platform,
|
||||
View
|
||||
} from "react-native";
|
||||
import Menu from "react-native-reanimated-material-menu/src/Menu";
|
||||
import { Menu } from "react-native-material-menu";
|
||||
import { db } from "../../common/database";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { ToastManager } from "../../services/event-manager";
|
||||
@@ -101,11 +101,14 @@ export const SelectionHeader = React.memo(
|
||||
};
|
||||
|
||||
const deleteItem = async () => {
|
||||
if (!type) return;
|
||||
presentDialog({
|
||||
title: strings.doActions.delete[type](selectedItemsList.length),
|
||||
paragraph: strings.actionConfirmations.delete[type](
|
||||
selectedItemsList.length
|
||||
),
|
||||
title: strings.doActions.delete[
|
||||
type as keyof typeof strings.doActions.delete
|
||||
](selectedItemsList.length),
|
||||
paragraph: strings.actionConfirmations.delete[
|
||||
type as keyof typeof strings.doActions.delete
|
||||
](selectedItemsList.length),
|
||||
positiveText: strings.delete(),
|
||||
negativeText: strings.cancel(),
|
||||
positivePress: async () => {
|
||||
@@ -218,14 +221,16 @@ export const SelectionHeader = React.memo(
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
backgroundColor: contextMenuColors.primary.background,
|
||||
marginTop: -20
|
||||
marginTop: 35
|
||||
}}
|
||||
onRequestClose={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
}}
|
||||
anchor={
|
||||
<IconButton
|
||||
onPress={() => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.show();
|
||||
}}
|
||||
name="dots-vertical"
|
||||
@@ -319,9 +324,10 @@ export const SelectionHeader = React.memo(
|
||||
!item.visible ? null : (
|
||||
<Button
|
||||
style={{
|
||||
width: 150,
|
||||
justifyContent: "flex-start",
|
||||
borderRadius: 0
|
||||
borderRadius: 0,
|
||||
alignSelf: "flex-start",
|
||||
width: "100%"
|
||||
}}
|
||||
type="plain"
|
||||
buttonType={{
|
||||
@@ -331,6 +337,7 @@ export const SelectionHeader = React.memo(
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
onPress={async () => {
|
||||
//@ts-ignore
|
||||
menuRef.current?.hide();
|
||||
if (Platform.OS === "ios") await sleep(300);
|
||||
item.onPress();
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"@lingui/react": "4.11.2",
|
||||
"@lingui/core": "4.11.2",
|
||||
"react-native-check-version": "^1.3.0",
|
||||
"react-native-reanimated-material-menu": "github:ammarahm-ed/react-native-reanimated-material-menu"
|
||||
"react-native-material-menu": "^2.0.0"
|
||||
},
|
||||
"sideEffects": false
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Menu, { MenuItem } from "react-native-reanimated-material-menu";
|
||||
import { Menu, MenuItem } from "react-native-material-menu";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { Dialog } from "../../../components/dialog";
|
||||
import { Pressable } from "../../../components/ui/pressable";
|
||||
@@ -146,7 +146,7 @@ export function SettingsPicker<T>({
|
||||
onChange(item);
|
||||
}
|
||||
}}
|
||||
underlayColor={colors.primary.hover}
|
||||
pressColor={colors.primary.hover}
|
||||
style={{
|
||||
backgroundColor: compareValue(currentValue, item)
|
||||
? colors.selected.background
|
||||
|
||||
@@ -123,6 +123,7 @@ export type PresentSheetOptions = {
|
||||
};
|
||||
|
||||
export function presentSheet(data: Partial<PresentSheetOptions>) {
|
||||
console.log("PRESENTING...");
|
||||
eSendEvent(eOpenSheet, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ module.exports = (env) => {
|
||||
/node_modules(.*[/\\])+@tanstack[/\\]react-query/,
|
||||
/node_modules(.*[/\\])+@trpc[/\\]react-query/,
|
||||
/node_modules(.*[/\\])+katex/,
|
||||
/node_modules(.*[/\\])+mime/,
|
||||
/node_modules(.*[/\\])+react-native-material-menu/,
|
||||
/node_modules(.*[/\\])+@notesnook[/\\]core/,
|
||||
/node_modules(.*[/\\])+whatwg-url-without-unicode/,
|
||||
/node_modules(.*[/\\])+whatwg-url/,
|
||||
|
||||
32
apps/mobile/package-lock.json
generated
32
apps/mobile/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.0.21",
|
||||
"version": "3.0.22",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.0.21",
|
||||
"version": "3.0.22",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -14,7 +14,6 @@
|
||||
"app/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0",
|
||||
"@notesnook/common": "file:../../packages/common",
|
||||
"@notesnook/core": "file:../../packages/core",
|
||||
"@notesnook/editor": "file:../../packages/editor",
|
||||
@@ -24,9 +23,9 @@
|
||||
"@notesnook/theme": "file:../../packages/theme",
|
||||
"@notesnook/themes-server": "file:../../servers/themes",
|
||||
"diffblazer": "^1.0.1",
|
||||
"mime": "^2.6.0",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.74.5",
|
||||
"react-native-actions-sheet": "^0.9.7"
|
||||
"react-native": "0.74.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"fonteditor-core": "^2.1.11",
|
||||
@@ -28912,9 +28911,9 @@
|
||||
"react-native-format-currency": "0.0.5",
|
||||
"react-native-image-zoom-viewer": "^3.0.1",
|
||||
"react-native-keyboard-aware-scroll-view": "^0.9.5",
|
||||
"react-native-material-menu": "^2.0.0",
|
||||
"react-native-progress": "^5.0.0",
|
||||
"react-native-qrcode-svg": "^6.0.6",
|
||||
"react-native-reanimated-material-menu": "github:ammarahm-ed/react-native-reanimated-material-menu",
|
||||
"react-native-reanimated-progress-bar": "1.0.1",
|
||||
"react-native-swiper-flatlist": "3.2.2",
|
||||
"react-native-wheel-color-picker": "^1.3.1",
|
||||
@@ -28997,6 +28996,7 @@
|
||||
"@ammarahmed/notifee-react-native": "7.4.7",
|
||||
"@ammarahmed/react-native-background-fetch": "^4.2.2",
|
||||
"@ammarahmed/react-native-eventsource": "1.1.0",
|
||||
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0",
|
||||
"@ammarahmed/react-native-share-extension": "^2.6.0",
|
||||
"@ammarahmed/react-native-sodium": "1.5.6",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.5",
|
||||
@@ -41679,7 +41679,8 @@
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "2.6.0",
|
||||
"license": "MIT",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
|
||||
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
@@ -43475,6 +43476,15 @@
|
||||
"version": "4.0.5",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-native-material-menu": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-material-menu/-/react-native-material-menu-2.0.0.tgz",
|
||||
"integrity": "sha512-SmO9PLE3E469EPbVWZqvdu6JGPPZIm7YjqDcWs2PPoY0k7w2V9tFo3BmmLXNzNZDCVCAi+PPSsL7h/5WkfHcSg==",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-mmkv-storage": {
|
||||
"version": "0.10.2",
|
||||
"license": "MIT",
|
||||
@@ -43590,14 +43600,6 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-reanimated-material-menu": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "git+ssh://git@github.com/ammarahm-ed/react-native-reanimated-material-menu.git#b1b19ba9e87333c76eb8abc3dc8377fe3ddd8bfc",
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.3.0",
|
||||
"react-native": ">= 0.54.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-reanimated-progress-bar": {
|
||||
"version": "1.0.1",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
"@notesnook/themes-server": "file:../../servers/themes",
|
||||
"diffblazer": "^1.0.1",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.74.5",
|
||||
"react-native-actions-sheet": "^0.9.7"
|
||||
"react-native": "0.74.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
apps/mobile/patches/react-native-material-menu+2.0.0.patch
Normal file
19
apps/mobile/patches/react-native-material-menu+2.0.0.patch
Normal file
@@ -0,0 +1,19 @@
|
||||
diff --git a/node_modules/react-native-material-menu/dist/Menu.js b/node_modules/react-native-material-menu/dist/Menu.js
|
||||
index 64fbf13..c7fcee0 100644
|
||||
--- a/node_modules/react-native-material-menu/dist/Menu.js
|
||||
+++ b/node_modules/react-native-material-menu/dist/Menu.js
|
||||
@@ -132,10 +132,13 @@ class Menu extends react_1.default.Component {
|
||||
else if (left < SCREEN_INDENT) {
|
||||
left = SCREEN_INDENT;
|
||||
}
|
||||
+ console.log(top, windowHeight - menuHeight - SCREEN_INDENT);
|
||||
// Flip by Y axis if menu hits bottom screen border
|
||||
if (top > windowHeight - menuHeight - SCREEN_INDENT) {
|
||||
+ const diff = top - (windowHeight - menuHeight - SCREEN_INDENT);
|
||||
+ const fraction = (diff / menuHeight);
|
||||
transforms.push({
|
||||
- translateY: react_native_1.Animated.multiply(menuSizeAnimation.y, -1),
|
||||
+ translateY: react_native_1.Animated.multiply(menuSizeAnimation.y, -(fraction + 1)),
|
||||
});
|
||||
top = windowHeight - SCREEN_INDENT;
|
||||
top = Math.min(windowHeight - SCREEN_INDENT, top + buttonHeight);
|
||||
@@ -1,600 +0,0 @@
|
||||
diff --git a/node_modules/react-native-reanimated-material-menu/src/Menu.js b/node_modules/react-native-reanimated-material-menu/src/Menu.js
|
||||
deleted file mode 100644
|
||||
index 69065f0..0000000
|
||||
--- a/node_modules/react-native-reanimated-material-menu/src/Menu.js
|
||||
+++ /dev/null
|
||||
@@ -1,268 +0,0 @@
|
||||
-import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
||||
-import {
|
||||
- Dimensions, I18nManager, Modal,
|
||||
- Platform,
|
||||
- StatusBar,
|
||||
- StyleSheet,
|
||||
- TouchableWithoutFeedback,
|
||||
- View
|
||||
-} from 'react-native';
|
||||
-import Animated, { Easing, useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
|
||||
-
|
||||
-const STATES = {
|
||||
- HIDDEN: 'HIDDEN',
|
||||
- ANIMATING: 'ANIMATING',
|
||||
- SHOWN: 'SHOWN',
|
||||
-};
|
||||
-
|
||||
-const EASING = Easing.bezier(0.4, 0, 0.2, 1);
|
||||
-const SCREEN_INDENT = 8;
|
||||
-
|
||||
-const Menu = ({ animationDuration = 300, ...props }, ref) => {
|
||||
- const container = useRef();
|
||||
- const [state, setState] = useState({
|
||||
- menuState: STATES.HIDDEN,
|
||||
-
|
||||
- top: 0,
|
||||
- left: 0,
|
||||
-
|
||||
- menuWidth: 0,
|
||||
- menuHeight: 0,
|
||||
-
|
||||
- buttonWidth: 0,
|
||||
- buttonHeight: 0,
|
||||
- });
|
||||
- const menuSizeXAnimation = useSharedValue(0);
|
||||
- const menuSizeYAnimation = useSharedValue(0);
|
||||
- const opacityAnimation = useSharedValue(0);
|
||||
-
|
||||
- const _onMenuLayout = e => {
|
||||
- if (state.menuState === STATES.ANIMATING) {
|
||||
- return;
|
||||
- }
|
||||
- const { width, height } = e.nativeEvent.layout;
|
||||
- setState(state => {
|
||||
- return {
|
||||
- ...state,
|
||||
- menuState: STATES.ANIMATING,
|
||||
- menuWidth: width,
|
||||
- menuHeight: height,
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- );
|
||||
- };
|
||||
-
|
||||
- useEffect(() => {
|
||||
- if (menuState === STATES.ANIMATING) {
|
||||
-
|
||||
- menuSizeXAnimation.value = withTiming(state.menuWidth, {
|
||||
- duration: animationDuration * 2,
|
||||
- easing: EASING
|
||||
- })
|
||||
- menuSizeYAnimation.value = withTiming(state.menuHeight, {
|
||||
- duration: animationDuration,
|
||||
- easing: EASING
|
||||
- })
|
||||
- opacityAnimation.value = withTiming(1, {
|
||||
- duration: animationDuration + 25,
|
||||
- easing: EASING
|
||||
- })
|
||||
- }
|
||||
- }, [state])
|
||||
-
|
||||
- const _onDismiss = () => {
|
||||
- if (props.onHidden) {
|
||||
- props.onHidden();
|
||||
- }
|
||||
- };
|
||||
- useImperativeHandle(ref, () => {
|
||||
- return {
|
||||
- show: () => {
|
||||
- container.current.measureInWindow((left, top, buttonWidth, buttonHeight) => {
|
||||
-
|
||||
- setState(state => {
|
||||
-
|
||||
- return {
|
||||
- ...state,
|
||||
- buttonHeight,
|
||||
- buttonWidth,
|
||||
- left,
|
||||
- menuState: STATES.SHOWN,
|
||||
- top,
|
||||
- }
|
||||
- });
|
||||
- });
|
||||
- },
|
||||
-
|
||||
- hide: hide
|
||||
- }
|
||||
-
|
||||
- }, [hide, container])
|
||||
-
|
||||
- const hide = onHidden => {
|
||||
- opacityAnimation.value = withTiming(0, {
|
||||
- duration: animationDuration,
|
||||
- easing: EASING
|
||||
- });
|
||||
- setTimeout(() => {
|
||||
- setState(
|
||||
- state => {
|
||||
- return {
|
||||
- ...state,
|
||||
- menuState: STATES.HIDDEN,
|
||||
- }
|
||||
- }
|
||||
- );
|
||||
- menuSizeXAnimation.value = 0;
|
||||
- menuSizeYAnimation.value = 0;
|
||||
- opacityAnimation.value = 0;
|
||||
-
|
||||
- onHidden && onHidden();
|
||||
- if (Platform.OS !== 'ios' && props.onHidden) {
|
||||
- props.onHidden();
|
||||
- }
|
||||
- }, props.animationDuration)
|
||||
-
|
||||
- };
|
||||
-
|
||||
- // @@ TODO: Rework this
|
||||
- const _hide = () => {
|
||||
- hide();
|
||||
- };
|
||||
-
|
||||
-
|
||||
- const menuSize = useAnimatedStyle(() => {
|
||||
- return {
|
||||
- width: menuSizeXAnimation.value,
|
||||
- height: menuSizeYAnimation.value,
|
||||
- }
|
||||
- });
|
||||
- const shadowMenuContainerStyle = useAnimatedStyle(() => {
|
||||
- return {
|
||||
- opacity: opacityAnimation.value,
|
||||
- };
|
||||
- }, [state])
|
||||
-
|
||||
- const { isRTL } = I18nManager;
|
||||
- const dimensions = Dimensions.get('window');
|
||||
- const { width: windowWidth } = dimensions;
|
||||
- const windowHeight = dimensions.height - (StatusBar.currentHeight || 0);
|
||||
- const {
|
||||
- menuWidth,
|
||||
- menuHeight,
|
||||
- buttonWidth,
|
||||
- buttonHeight,
|
||||
- } = state;
|
||||
-
|
||||
- // Adjust position of menu
|
||||
- let { left, top } = state;
|
||||
- const transforms = [];
|
||||
- if (
|
||||
- (isRTL && left + buttonWidth - menuWidth > SCREEN_INDENT) ||
|
||||
- (!isRTL && left + menuWidth > windowWidth - SCREEN_INDENT)
|
||||
- ) {
|
||||
- transforms.push({
|
||||
- translateX: menuSizeXAnimation.value * -1,
|
||||
- });
|
||||
-
|
||||
- left = Math.min(windowWidth - SCREEN_INDENT, left + buttonWidth);
|
||||
- } else if (left < SCREEN_INDENT) {
|
||||
- left = SCREEN_INDENT;
|
||||
- }
|
||||
-
|
||||
-
|
||||
- // Flip by Y axis if menu hits bottom screen border
|
||||
- if (top > windowHeight - menuHeight - SCREEN_INDENT) {
|
||||
- transforms.push({
|
||||
- translateY: menuSizeYAnimation.value * -1,
|
||||
- });
|
||||
-
|
||||
- top = windowHeight - SCREEN_INDENT;
|
||||
- top = Math.min(windowHeight - SCREEN_INDENT, top + buttonHeight);
|
||||
- } else if (top < SCREEN_INDENT) {
|
||||
- top = SCREEN_INDENT;
|
||||
- }
|
||||
-
|
||||
- const extraStyles = {
|
||||
- transform: transforms,
|
||||
- top,
|
||||
-
|
||||
- // Switch left to right for rtl devices
|
||||
- ...(isRTL ? { right: left } : { left }),
|
||||
- }
|
||||
-
|
||||
- const { menuState } = state;
|
||||
- const animationStarted = menuState === STATES.ANIMATING;
|
||||
- const modalVisible = menuState === STATES.SHOWN || animationStarted;
|
||||
-
|
||||
- const { testID, button, style, children } = props;
|
||||
-
|
||||
- return <View ref={container} collapsable={false} testID={testID}>
|
||||
- <View>{button}</View>
|
||||
-
|
||||
- <Modal
|
||||
- visible={modalVisible}
|
||||
- onRequestClose={_hide}
|
||||
- supportedOrientations={[
|
||||
- 'portrait',
|
||||
- 'portrait-upside-down',
|
||||
- 'landscape',
|
||||
- 'landscape-left',
|
||||
- 'landscape-right',
|
||||
- ]}
|
||||
- transparent
|
||||
- onDismiss={_onDismiss}
|
||||
- >
|
||||
- <TouchableWithoutFeedback onPress={_hide} accessible={false}>
|
||||
- <View style={StyleSheet.absoluteFill}>
|
||||
- <Animated.View
|
||||
- onLayout={_onMenuLayout}
|
||||
- style={[
|
||||
- styles.shadowMenuContainer,
|
||||
- shadowMenuContainerStyle,
|
||||
- extraStyles,
|
||||
- style,
|
||||
- ]}
|
||||
- >
|
||||
- <Animated.View
|
||||
- style={[styles.menuContainer, animationStarted && menuSize]}
|
||||
- >
|
||||
- {children}
|
||||
- </Animated.View>
|
||||
- </Animated.View>
|
||||
- </View>
|
||||
- </TouchableWithoutFeedback>
|
||||
- </Modal>
|
||||
- </View>
|
||||
-
|
||||
-}
|
||||
-
|
||||
-
|
||||
-const styles = StyleSheet.create({
|
||||
- shadowMenuContainer: {
|
||||
- position: 'absolute',
|
||||
- backgroundColor: 'white',
|
||||
- borderRadius: 4,
|
||||
- opacity: 0,
|
||||
- overflow: "hidden",
|
||||
-
|
||||
- // Shadow
|
||||
- ...Platform.select({
|
||||
- ios: {
|
||||
- shadowColor: 'black',
|
||||
- shadowOffset: { width: 0.3 * 5, height: 0.5 * 5 },
|
||||
- shadowOpacity: 0.2,
|
||||
- shadowRadius: 0.7 * 5,
|
||||
- },
|
||||
- android: {
|
||||
- elevation: 8,
|
||||
- },
|
||||
- }),
|
||||
- },
|
||||
- menuContainer: {
|
||||
- overflow: 'hidden',
|
||||
- },
|
||||
-});
|
||||
-
|
||||
-export default forwardRef(Menu);
|
||||
diff --git a/node_modules/react-native-reanimated-material-menu/src/Menu.tsx b/node_modules/react-native-reanimated-material-menu/src/Menu.tsx
|
||||
new file mode 100644
|
||||
index 0000000..c3bc1c9
|
||||
--- /dev/null
|
||||
+++ b/node_modules/react-native-reanimated-material-menu/src/Menu.tsx
|
||||
@@ -0,0 +1,303 @@
|
||||
+import React from 'react';
|
||||
+
|
||||
+import {
|
||||
+ Animated,
|
||||
+ Dimensions,
|
||||
+ Easing,
|
||||
+ I18nManager,
|
||||
+ LayoutChangeEvent,
|
||||
+ Modal,
|
||||
+ Platform,
|
||||
+ ScrollView,
|
||||
+ StatusBar,
|
||||
+ StyleSheet,
|
||||
+ TouchableWithoutFeedback,
|
||||
+ View,
|
||||
+ ViewStyle,
|
||||
+} from 'react-native';
|
||||
+
|
||||
+export interface MenuProps {
|
||||
+ children?: React.ReactNode;
|
||||
+ anchor?: React.ReactNode;
|
||||
+ style?: ViewStyle;
|
||||
+ onRequestClose?(): void;
|
||||
+ animationDuration?: number;
|
||||
+ testID?: string;
|
||||
+ visible?: boolean;
|
||||
+}
|
||||
+
|
||||
+enum States {
|
||||
+ Hidden,
|
||||
+ Animating,
|
||||
+ Shown,
|
||||
+}
|
||||
+
|
||||
+interface State {
|
||||
+ buttonHeight: number;
|
||||
+ buttonWidth: number;
|
||||
+ left: number;
|
||||
+ menuHeight: number;
|
||||
+ menuSizeAnimation: Animated.ValueXY;
|
||||
+ menuState: States;
|
||||
+ menuWidth: number;
|
||||
+ opacityAnimation: Animated.Value;
|
||||
+ top: number;
|
||||
+}
|
||||
+
|
||||
+const EASING = Easing.bezier(0.4, 0, 0.2, 1);
|
||||
+const SCREEN_INDENT = 8;
|
||||
+const SCREEN_INDENT_VERTICAL = 80;
|
||||
+
|
||||
+class Menu extends React.Component<MenuProps, State> {
|
||||
+ _container: View | null = null;
|
||||
+
|
||||
+ static defaultProps = {
|
||||
+ animationDuration: 300,
|
||||
+ };
|
||||
+
|
||||
+ constructor(props: MenuProps) {
|
||||
+ super(props);
|
||||
+
|
||||
+ this.state = {
|
||||
+ menuState: States.Hidden,
|
||||
+
|
||||
+ top: 0,
|
||||
+ left: 0,
|
||||
+
|
||||
+ menuWidth: 0,
|
||||
+ menuHeight: 0,
|
||||
+
|
||||
+ buttonWidth: 0,
|
||||
+ buttonHeight: 0,
|
||||
+
|
||||
+ menuSizeAnimation: new Animated.ValueXY({ x: 0, y: 0 }),
|
||||
+ opacityAnimation: new Animated.Value(0),
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ componentDidMount() {
|
||||
+ if (!this.props.visible) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ this.show();
|
||||
+ }
|
||||
+
|
||||
+ componentDidUpdate(prevProps: MenuProps) {
|
||||
+ if (prevProps.visible === this.props.visible) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (this.props.visible) {
|
||||
+ this.show();
|
||||
+ } else {
|
||||
+ this.hide();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private setContainerRef = (ref: View) => {
|
||||
+ this._container = ref;
|
||||
+ };
|
||||
+
|
||||
+ // Start menu animation
|
||||
+ private onMenuLayout = (e: LayoutChangeEvent) => {
|
||||
+ if (this.state.menuState === States.Animating) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const { width, height } = e.nativeEvent.layout;
|
||||
+ let timeout:any = 0;
|
||||
+ this.setState(
|
||||
+ {
|
||||
+ menuState: States.Animating,
|
||||
+ menuWidth: width,
|
||||
+ menuHeight: height,
|
||||
+ },
|
||||
+ () => {
|
||||
+ Animated.parallel([
|
||||
+ Animated.timing(this.state.menuSizeAnimation, {
|
||||
+ toValue: { x: width, y: height },
|
||||
+ duration: this.props.animationDuration,
|
||||
+ easing: EASING,
|
||||
+ useNativeDriver: false,
|
||||
+ }),
|
||||
+ Animated.timing(this.state.opacityAnimation, {
|
||||
+ toValue: 1,
|
||||
+ duration: this.props.animationDuration,
|
||||
+ easing: EASING,
|
||||
+ useNativeDriver: false,
|
||||
+ }),
|
||||
+ ]).start(({finished}) => {
|
||||
+ if (finished) {
|
||||
+ clearTimeout(timeout);
|
||||
+ timeout = setTimeout(() => {
|
||||
+ this.setState({
|
||||
+ menuState: States.Shown
|
||||
+ })
|
||||
+ },20)
|
||||
+
|
||||
+ }
|
||||
+ });
|
||||
+ },
|
||||
+ );
|
||||
+ };
|
||||
+
|
||||
+ show = () => {
|
||||
+ this._container?.measureInWindow((left, top, buttonWidth, buttonHeight) => {
|
||||
+ this.setState({
|
||||
+ buttonHeight,
|
||||
+ buttonWidth,
|
||||
+ left,
|
||||
+ menuState: States.Shown,
|
||||
+ top,
|
||||
+ });
|
||||
+ });
|
||||
+ };
|
||||
+
|
||||
+ hide = () => {
|
||||
+ Animated.timing(this.state.opacityAnimation, {
|
||||
+ toValue: 0,
|
||||
+ duration: this.props.animationDuration,
|
||||
+ easing: EASING,
|
||||
+ useNativeDriver: false,
|
||||
+ }).start(() => {
|
||||
+ // Reset state
|
||||
+ this.setState({
|
||||
+ menuState: States.Hidden,
|
||||
+ menuSizeAnimation: new Animated.ValueXY({ x: 0, y: 0 }),
|
||||
+ opacityAnimation: new Animated.Value(0),
|
||||
+ });
|
||||
+ });
|
||||
+ };
|
||||
+
|
||||
+ private onRequestClose = () => {
|
||||
+ this.props.onRequestClose?.();
|
||||
+ };
|
||||
+
|
||||
+ render() {
|
||||
+ const { isRTL } = I18nManager;
|
||||
+
|
||||
+ const dimensions = Dimensions.get('window');
|
||||
+ const { width: windowWidth } = dimensions;
|
||||
+ const windowHeight = dimensions.height - (StatusBar.currentHeight || 0);
|
||||
+
|
||||
+ const {
|
||||
+ menuSizeAnimation,
|
||||
+ menuWidth,
|
||||
+ menuHeight,
|
||||
+ buttonWidth,
|
||||
+ buttonHeight,
|
||||
+ opacityAnimation,
|
||||
+ } = this.state;
|
||||
+ const menuSize = {
|
||||
+ width: menuSizeAnimation.x,
|
||||
+ height: menuSizeAnimation.y,
|
||||
+ };
|
||||
+
|
||||
+ // Adjust position of menu
|
||||
+ let { left, top } = this.state;
|
||||
+ const transforms:any[] = [];
|
||||
+
|
||||
+ if (
|
||||
+ (isRTL && left + buttonWidth - menuWidth > SCREEN_INDENT) ||
|
||||
+ (!isRTL && left + menuWidth > windowWidth - SCREEN_INDENT)
|
||||
+ ) {
|
||||
+ transforms.push({
|
||||
+ translateX: Animated.multiply(menuSizeAnimation.x, -1),
|
||||
+ } as never);
|
||||
+
|
||||
+ left = Math.min(windowWidth - SCREEN_INDENT, left + buttonWidth);
|
||||
+ } else if (left < SCREEN_INDENT) {
|
||||
+ left = SCREEN_INDENT;
|
||||
+ }
|
||||
+
|
||||
+ // Flip by Y axis if menu hits bottom screen border
|
||||
+ if (top > windowHeight - menuHeight - SCREEN_INDENT_VERTICAL) {
|
||||
+ transforms.push({
|
||||
+ translateY: Animated.multiply(menuSizeAnimation.y, -1),
|
||||
+ } as never);
|
||||
+
|
||||
+ top = windowHeight - SCREEN_INDENT_VERTICAL;
|
||||
+ top = Math.min(windowHeight - SCREEN_INDENT_VERTICAL, top + buttonHeight);
|
||||
+ } else if (top < SCREEN_INDENT_VERTICAL) {
|
||||
+ top = SCREEN_INDENT_VERTICAL;
|
||||
+ }
|
||||
+
|
||||
+ const shadowMenuContainerStyle = {
|
||||
+ opacity: opacityAnimation,
|
||||
+ transform: transforms,
|
||||
+ maxHeight: 500,
|
||||
+ top,
|
||||
+
|
||||
+ // Switch left to right for rtl devices
|
||||
+ ...(isRTL ? { right: left } : { left }),
|
||||
+ };
|
||||
+ const { menuState } = this.state;
|
||||
+ const animationStarted = menuState === States.Animating;
|
||||
+ const modalVisible = menuState === States.Shown || animationStarted;
|
||||
+
|
||||
+ const { testID, anchor, style, children } = this.props;
|
||||
+
|
||||
+ return (
|
||||
+ <View ref={this.setContainerRef} collapsable={false} testID={testID}>
|
||||
+ {anchor}
|
||||
+
|
||||
+ <Modal
|
||||
+ visible={modalVisible}
|
||||
+ onRequestClose={this.onRequestClose}
|
||||
+ supportedOrientations={[
|
||||
+ 'portrait',
|
||||
+ 'portrait-upside-down',
|
||||
+ 'landscape',
|
||||
+ 'landscape-left',
|
||||
+ 'landscape-right',
|
||||
+ ]}
|
||||
+ transparent
|
||||
+ >
|
||||
+ <TouchableWithoutFeedback onPress={this.onRequestClose} accessible={false}>
|
||||
+ <View style={StyleSheet.absoluteFill}>
|
||||
+ <Animated.View
|
||||
+ onLayout={this.onMenuLayout}
|
||||
+ style={[styles.shadowMenuContainer, shadowMenuContainerStyle, style]}
|
||||
+ >
|
||||
+ <Animated.View style={[styles.menuContainer, animationStarted && menuSize]}>
|
||||
+ <ScrollView showsVerticalScrollIndicator={menuState !== States.Animating} >
|
||||
+ {children}
|
||||
+ </ScrollView>
|
||||
+ </Animated.View>
|
||||
+ </Animated.View>
|
||||
+ </View>
|
||||
+ </TouchableWithoutFeedback>
|
||||
+ </Modal>
|
||||
+ </View>
|
||||
+ );
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+const styles = StyleSheet.create({
|
||||
+ shadowMenuContainer: {
|
||||
+ position: 'absolute',
|
||||
+ backgroundColor: 'white',
|
||||
+ borderRadius: 4,
|
||||
+ opacity: 0,
|
||||
+
|
||||
+ // Shadow
|
||||
+ ...Platform.select({
|
||||
+ ios: {
|
||||
+ shadowColor: 'black',
|
||||
+ shadowOffset: { width: 0, height: 2 },
|
||||
+ shadowOpacity: 0.14,
|
||||
+ shadowRadius: 2,
|
||||
+ },
|
||||
+ android: {
|
||||
+ elevation: 8,
|
||||
+ },
|
||||
+ }),
|
||||
+ },
|
||||
+ menuContainer: {
|
||||
+ overflow: 'hidden',
|
||||
+ },
|
||||
+});
|
||||
+
|
||||
+
|
||||
+export default Menu
|
||||
\ No newline at end of file
|
||||
diff --git a/node_modules/react-native-reanimated-material-menu/src/MenuItem.js b/node_modules/react-native-reanimated-material-menu/src/MenuItem.js
|
||||
index 120a870..d6459c0 100644
|
||||
--- a/node_modules/react-native-reanimated-material-menu/src/MenuItem.js
|
||||
+++ b/node_modules/react-native-reanimated-material-menu/src/MenuItem.js
|
||||
@@ -14,6 +14,11 @@ const Touchable =
|
||||
? TouchableNativeFeedback
|
||||
: TouchableHighlight;
|
||||
|
||||
+/**
|
||||
+ *
|
||||
+ * @param {any} param0
|
||||
+ * @returns
|
||||
+ */
|
||||
function MenuItem({
|
||||
children,
|
||||
disabled,
|
||||
25
packages/core/package-lock.json
generated
25
packages/core/package-lock.json
generated
@@ -16,6 +16,7 @@
|
||||
"@readme/data-urls": "^3.0.0",
|
||||
"@streetwriters/kysely": "^0.27.4",
|
||||
"@streetwriters/showdown": "^3.0.9-alpha",
|
||||
"@types/mime-db": "^1.43.5",
|
||||
"async-mutex": "^0.3.2",
|
||||
"dayjs": "1.11.9",
|
||||
"dom-serializer": "^2.0.0",
|
||||
@@ -28,7 +29,7 @@
|
||||
"katex": "0.16.2",
|
||||
"linkedom": "^0.14.17",
|
||||
"liqe": "^1.13.0",
|
||||
"mime": "^4.0.4",
|
||||
"mime-db": "^1.53.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"qclone": "^1.2.0",
|
||||
"rfdc": "^1.3.0",
|
||||
@@ -2266,6 +2267,11 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/mime-db": {
|
||||
"version": "1.43.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime-db/-/mime-db-1.43.5.tgz",
|
||||
"integrity": "sha512-/bfTiIUTNPUBnwnYvUxXAre5MhD88jgagLEQiQtIASjU+bwxd8kS/ASDA4a8ufd8m0Lheu6eeMJHEUpLHoJ28A=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.9",
|
||||
"dev": true,
|
||||
@@ -3447,19 +3453,12 @@
|
||||
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz",
|
||||
"integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "bin/cli.js"
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.53.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz",
|
||||
"integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-fn": {
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"@readme/data-urls": "^3.0.0",
|
||||
"@streetwriters/kysely": "^0.27.4",
|
||||
"@streetwriters/showdown": "^3.0.9-alpha",
|
||||
"@types/mime-db": "^1.43.5",
|
||||
"async-mutex": "^0.3.2",
|
||||
"dayjs": "1.11.9",
|
||||
"dom-serializer": "^2.0.0",
|
||||
@@ -80,7 +81,7 @@
|
||||
"katex": "0.16.2",
|
||||
"linkedom": "^0.14.17",
|
||||
"liqe": "^1.13.0",
|
||||
"mime": "^4.0.4",
|
||||
"mime-db": "^1.53.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"qclone": "^1.2.0",
|
||||
"rfdc": "^1.3.0",
|
||||
|
||||
@@ -23,11 +23,11 @@ export async function getFileNameWithExtension(
|
||||
): Promise<string> {
|
||||
if (!mime || mime === "application/octet-stream") return filename;
|
||||
|
||||
const { default: mimeDB } = await import("mime");
|
||||
const { default: mimeDB } = await import("mime-db");
|
||||
|
||||
const extensions = mimeDB.getAllExtensions(mime);
|
||||
const { extensions } = mimeDB[mime] || {};
|
||||
|
||||
if (!extensions || extensions.size === 0) return filename;
|
||||
if (!extensions || extensions.length === 0) return filename;
|
||||
|
||||
for (const ext of extensions) {
|
||||
if (filename.endsWith(ext)) return filename;
|
||||
|
||||
Reference in New Issue
Block a user