922 share sheet changes (#3129)

* Allow 500 photos in image picker

* Allow 500 photos from share extension

* Basic React share sheet setup

Following the setup here: f5805e9208/SHARE_EXTENSION_VIEW.md

* Basic ShareSheet React component

* Update ShareSheet.tsx

* Update ShareSheet.tsx
This commit is contained in:
Johannes Klein
2025-10-14 01:16:29 +02:00
committed by GitHub
parent 660283863b
commit 316caf6687
7 changed files with 147 additions and 7 deletions

5
index.share.js Normal file
View File

@@ -0,0 +1,5 @@
import { AppRegistry } from "react-native";
import ShareSheet from "./src/components/ShareSheet/ShareSheet";
AppRegistry.registerComponent( "ShareMenuModuleComponent", () => ShareSheet );

View File

@@ -9,7 +9,7 @@
<!--Share View Controller-->
<scene sceneID="ceB-am-kn3">
<objects>
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="j1y-V4-xli" customClass="ReactShareViewController" customModule="iNaturalistReactNative-ShareExtension" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>

View File

@@ -13,7 +13,7 @@
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>20</integer>
<integer>500</integer>
</dict>
</dict>
<key>NSExtensionMainStoryboard</key>
@@ -21,5 +21,31 @@
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
<key>ReactShareViewBackgroundColor</key>
<dict>
<key>Red</key>
<integer>1</integer>
<key>Green</key>
<integer>1</integer>
<key>Blue</key>
<integer>1</integer>
<key>Alpha</key>
<integer>1</integer>
<key>Transparent</key>
<false/>
</dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>

View File

@@ -1,4 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTBridgeDelegate.h>
#import <React/RCTRootView.h>

View File

@@ -28,6 +28,7 @@
8F1AC6772BC1B610002F994B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 8F1AC6762BC1B610002F994B /* PrivacyInfo.xcprivacy */; };
8F1AC6792BC1B610002F994B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 8F1AC6762BC1B610002F994B /* PrivacyInfo.xcprivacy */; };
8F346E4A2CF6912700CED7B4 /* geomodel.mlmodel in Sources */ = {isa = PBXBuildFile; fileRef = 8F346E492CF6912700CED7B4 /* geomodel.mlmodel */; };
8FA933AD2E99522900179553 /* ReactShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FA933AC2E99522900179553 /* ReactShareViewController.swift */; };
A5C00A8934ED4A48A1495179 /* INatIcon.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0972395C34134C71A54A2A5D /* INatIcon.ttf */; };
AE4DC81B3A87484CB3FD6750 /* Lato-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4B0AEEF6CA584BCF9880EB35 /* Lato-Regular.ttf */; };
E5DFC1C6FBFA45739CE91C69 /* Lato-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 69DF855D92EA4ADFB73B47F1 /* Lato-MediumItalic.ttf */; };
@@ -92,6 +93,7 @@
8C2D97D72EED451C887998A8 /* Lato-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Lato-BoldItalic.ttf"; path = "../assets/fonts/Lato-BoldItalic.ttf"; sourceTree = "<group>"; };
8F1AC6762BC1B610002F994B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
8F346E492CF6912700CED7B4 /* geomodel.mlmodel */ = {isa = PBXFileReference; lastKnownFileType = file.mlmodel; path = geomodel.mlmodel; sourceTree = "<group>"; };
8FA933AC2E99522900179553 /* ReactShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ReactShareViewController.swift; path = "../node_modules/react-native-share-menu/ios/ReactShareViewController.swift"; sourceTree = SOURCE_ROOT; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F15C1390617A309CE0A194B2 /* Pods_iNaturalistReactNative_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iNaturalistReactNative_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -202,6 +204,7 @@
8B65ED2C29F575C10054CCEF /* iNaturalistReactNative-ShareExtension */ = {
isa = PBXGroup;
children = (
8FA933AC2E99522900179553 /* ReactShareViewController.swift */,
8B65ED3C29F576D00054CCEF /* iNaturalistReactNative-ShareExtension.entitlements */,
8B65ED3A29F575FE0054CCEF /* ShareViewController.swift */,
8B65ED2F29F575C10054CCEF /* MainInterface.storyboard */,
@@ -260,6 +263,7 @@
8B65ED2829F575C10054CCEF /* Frameworks */,
8B65ED2929F575C10054CCEF /* Resources */,
CBBE96E94BCFC337E2A3EDB6 /* [CP] Copy Pods Resources */,
8F0FF30C2E9AF08F005DCE05 /* Bundle React Native code and images */,
);
buildRules = (
);
@@ -397,6 +401,24 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iNaturalistReactNative/Pods-iNaturalistReactNative-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
8F0FF30C2E9AF08F005DCE05 /* Bundle React Native code and images */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Bundle React Native code and images";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export ENTRY_FILE=index.share.js\n\nset -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
};
987B044B8ED1BE214203D222 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -472,6 +494,7 @@
buildActionMask = 2147483647;
files = (
8B65ED3B29F575FE0054CCEF /* ShareViewController.swift in Sources */,
8FA933AD2E99522900179553 /* ReactShareViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -25,7 +25,7 @@ import { useLayoutPrefs } from "sharedHooks";
import useExitObservationsFlow from "sharedHooks/useExitObservationFlow";
import useStore from "stores/useStore";
const MAX_PHOTOS_ALLOWED = 20;
const MAX_PHOTOS_ALLOWED = 500;
const FROM_AICAMERA_MAX_PHOTOS_ALLOWED = 1;
const PhotoLibrary = ( ): Node => {

View File

@@ -0,0 +1,86 @@
// Fellow developers: This is the component that is rendered when the share extension is opened.
// It uses a separate React instance and entry point that is registered in index.share.js
// It is not set up to use anything from the main app, like nativewind or styled components.
import React, { useEffect, useState } from "react";
import {
Pressable, StyleSheet, Text, View
} from "react-native";
import { ShareMenuReactView } from "react-native-share-menu";
interface ButtonProps {
onPress: () => void;
title: string;
style?: object;
}
const Button = ( { onPress, title, style }: ButtonProps ) => (
<Pressable accessibilityRole="button" onPress={onPress}>
{/* eslint-disable-next-line no-use-before-define */}
<Text style={[styles.button, style]}>{title}</Text>
</Pressable>
);
const ShareSheet = () => {
const [sharedData, setSharedData] = useState( [] );
useEffect( () => {
// @ts-expect-error data has any type here, but the actual type should come from the library
ShareMenuReactView.data().then( ( { data } ) => {
setSharedData( data );
} );
}, [] );
const {
container, text, buttonGroup, destructive
// eslint-disable-next-line no-use-before-define
} = styles;
return (
<View style={container}>
<Text style={text}>
{`Share ${sharedData.length} photos with iNaturalist?`}
</Text>
<View style={buttonGroup}>
<Button
title="Yes"
onPress={( ) => {
ShareMenuReactView.continueInApp( );
}}
/>
<Button
title="No"
onPress={( ) => {
ShareMenuReactView.dismissExtension( );
}}
style={destructive}
/>
</View>
</View>
);
};
const styles = StyleSheet.create( {
button: {
fontSize: 16,
margin: 16
},
container: {
flex: 1,
backgroundColor: "white"
},
text: {
fontSize: 20,
textAlign: "center",
margin: 16
},
buttonGroup: {
flexDirection: "row",
justifyContent: "space-around",
alignItems: "center"
},
destructive: {
color: "red"
}
} );
export default ShareSheet;