diff --git a/README.md b/README.md
index df839be..8448ffd 100644
--- a/README.md
+++ b/README.md
@@ -148,13 +148,14 @@ export default const App = () => {
const { shareIntent } = useShareIntent();
```
-| attribute | description | example |
-| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `shareIntent.text` | raw text from text/weburl (ios) and text/\* (android) | "`some text`", "`http://example.com`", "`Hey, Click on my link : http://example.com/nickname`" |
-| `shareIntent.webUrl` | link extracted from raw text | `null`, "`http://example.com`", "`http://example.com/nickname`" |
-| `shareIntent.files` | image / movies / audio / files with name, path, mimetype, size (in octets) and image/video dimensions (width/height/duration) | `[{ path: "file:///local/path/filename", mimeType: "image/jpeg", fileName: "originalFilename.jpg", size: 2567402, width: 800, height: 600 }, { path: "file:///local/path/filename", mimeType: "video/mp4", fileName: "originalFilename.mp4", size: 2567402, width: 800, height: 600, duration: 20000 }]` |
-| `shareIntent.meta` | meta object which contains extra information about the share intent | `{ title: "My cool blog article" }` |
-| `shareIntent.meta.title` | optional title property sent by other app. Currently only filled on Android | `My cool blog article` |
+| attribute | description | example |
+| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `shareIntent.text` | raw text from text/weburl (ios) and text/\* (android) | "`some text`", "`http://example.com`", "`Hey, Click on my link : http://example.com/nickname`" |
+| `shareIntent.webUrl` | link extracted from raw text | `null`, "`http://example.com`", "`http://example.com/nickname`" |
+| `shareIntent.files` | image / movies / audio / files with name, path, mimetype, size (in octets) and image/video dimensions (width/height/duration) | `[{ path: "file:///local/path/filename", mimeType: "image/jpeg", fileName: "originalFilename.jpg", size: 2567402, width: 800, height: 600 }, { path: "file:///local/path/filename", mimeType: "video/mp4", fileName: "originalFilename.mp4", size: 2567402, width: 800, height: 600, duration: 20000 }]` |
+| `shareIntent.meta` | meta object which contains extra information about the share intent | `{ title: "My cool blog article", "og:image": "https://.../image.png" }` |
+| `shareIntent.meta.title` | optional title property sent by other app (available on Android and when `NSExtensionActivationSupportsWebPageWithMaxCount` is enabled on iOS) | `My cool blog article` |
+| `shareIntent.meta.xxx` | list all webpage metadata available in meta tags `` (iOS only, available with `NSExtensionActivationSupportsWebPageWithMaxCount`) | |
#### Customize Content Types in `app.json`
@@ -177,16 +178,17 @@ Simply choose content types you need :
],
```
-| Option | Values |
-| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Option | Values |
+| ----------------------------- | ------------------- |
| iosActivationRules | Allow **text** sharing with `"NSExtensionActivationSupportsText": true`
**Url** sharing with `"NSExtensionActivationSupportsWebURLWithMaxCount": 1` and `"NSExtensionActivationSupportsWebPageWithMaxCount": 1`
**Images** sharing with `"NSExtensionActivationSupportsImageWithMaxCount": 1`
**Videos** sharing with `"NSExtensionActivationSupportsMovieWithMaxCount": 1`
**Files and audio** sharing with `"NSExtensionActivationSupportsFileWithMaxCount": 1`
_default value_: `{ "NSExtensionActivationSupportsWebURLWithMaxCount": 1, "NSExtensionActivationSupportsWebPageWithMaxCount": 1 }"`
_More info in apple developper doc [here](https://developer.apple.com/documentation/bundleresources/information_property_list/nsextension/nsextensionattributes/nsextensionactivationrule)_
you can also provide a custom query (ex: `"iosActivationRules": "SUBQUERY (...)"`) |
-| iosShareExtensionName | override `CFBundleDisplayName` the extension `info.plist`, also used as extension name for xcode target (ex: `ExpoShareIntent Example Extension`, folder: `ExpoShareIntentExampleExtension`) |
-| iosAppGroupIdentifier | custom application group identifier for `com.apple.security.application-groups` (ex: `group.custom.exposhareintent.example`) cf [#94](https://github.com/achorein/expo-share-intent/issues/94) |
-| androidIntentFilters | **one file sharing** array of MIME types :`"text/*"` / `"image/*"` / `"video/*"` / `"*/*"`
_default value_: `["text/*"]` (text and url) |
-| androidMultiIntentFilters | **multiple files sharing** array of MIME types : `"image/*"` / `"video/*"` / `"audio/*`/ `"*/*"`
_default value_: `[]` |
-| androidMainActivityAttributes | _default value_: `{ "android:launchMode": "singleTask" }` |
-| disableAndroid | Disable the android share intent. Useful if you want to use a custom implementation. _default value_: `false` |
-| disableIOS | Disable the ios share extension. Useful if you want to use a custom implementation (ex: [iOS Custom View](#ios-custom-view-)). _default value_: `false` |
+| iosShareExtensionName | override `CFBundleDisplayName` the extension `info.plist`, also used as extension name for xcode target (ex: `ExpoShareIntent Example Extension`, folder: `ExpoShareIntentExampleExtension`) |
+| iosAppGroupIdentifier | custom application group identifier for `com.apple.security.application-groups` (ex: `group.custom.exposhareintent.example`) cf [#94](https://github.com/achorein/expo-share-intent/issues/94) |
+| androidIntentFilters | **one file sharing** array of MIME types :`"text/*"` / `"image/*"` / `"video/*"` / `"*/*"`
_default value_: `["text/*"]` (text and url) |
+| androidMultiIntentFilters | **multiple files sharing** array of MIME types : `"image/*"` / `"video/*"` / `"audio/*`/ `"*/*"`
_default value_: `[]` |
+| androidMainActivityAttributes | _default value_: `{ "android:launchMode": "singleTask" }` |
+| preprocessorInjectJS | Add javascript to webpage preprocessor before the share extension is called (cf [Accessing a Webpage](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW12)).
Example: preprocessorInjectJS: "metas['og\\:image'] = metas['og\\:image'] || document.querySelector('img#seo-image')?.getAttribute('src')"
|
+| disableAndroid | Disable the android share intent. Useful if you want to use a custom implementation. _default value_: `false` |
+| disableIOS | Disable the ios share extension. Useful if you want to use a custom implementation (ex: [iOS Custom View](#ios-custom-view-)). _default value_: `false` |
### Expo Router
diff --git a/example/basic/App.tsx b/example/basic/App.tsx
index 6893930..100af85 100644
--- a/example/basic/App.tsx
+++ b/example/basic/App.tsx
@@ -20,12 +20,14 @@ const WebUrlComponent = ({ shareIntent }: { shareIntent: ShareIntent }) => {
source={
shareIntent.meta?.["og:image"]
? { uri: shareIntent.meta?.["og:image"] }
- : require("./assets/icon.png")
+ : undefined
}
style={[styles.icon, styles.gap, { borderRadius: 5 }]}
/>
- {shareIntent.meta?.title || ""}
+
+ {shareIntent.meta?.title || ""}
+
{shareIntent.webUrl}
diff --git a/example/basic/app.json b/example/basic/app.json
index 93d4135..c9ac493 100644
--- a/example/basic/app.json
+++ b/example/basic/app.json
@@ -26,11 +26,9 @@
"NSExtensionActivationSupportsFileWithMaxCount": 1
},
"iosShareExtensionName": "ExpoShareIntent Example Extension",
- "iosAppGroupIdentifier": "customgroup.expo.modules.exposhareintent.example",
"androidIntentFilters": ["text/*", "image/*", "video/*"],
"androidMultiIntentFilters": ["image/*"],
- "disableIOS": false,
- "disableAndroid": false
+ "preprocessorInjectJS": "metas['og\\:image'] = metas['og\\:image'] || document.querySelector('img#main-image')?.getAttribute('src') || document.querySelector('img[data-old-hires]')?.getAttribute('data-old-hires');"
}
],
["expo-updates"]
diff --git a/example/expo-router/app/shareintent.tsx b/example/expo-router/app/shareintent.tsx
index 44e6b34..78c6fc9 100644
--- a/example/expo-router/app/shareintent.tsx
+++ b/example/expo-router/app/shareintent.tsx
@@ -19,12 +19,14 @@ const WebUrlComponent = ({ shareIntent }: { shareIntent: ShareIntentType }) => {
source={
shareIntent.meta?.["og:image"]
? { uri: shareIntent.meta?.["og:image"] }
- : require("../assets/icon.png")
+ : undefined
}
style={[styles.icon, styles.gap, { borderRadius: 5 }]}
/>
- {shareIntent.meta?.title || ""}
+
+ {shareIntent.meta?.title || ""}
+
{shareIntent.webUrl}
diff --git a/example/react-navigation/app/ShareIntentScreen.tsx b/example/react-navigation/app/ShareIntentScreen.tsx
index 12d9f38..c33d53d 100644
--- a/example/react-navigation/app/ShareIntentScreen.tsx
+++ b/example/react-navigation/app/ShareIntentScreen.tsx
@@ -21,12 +21,14 @@ const WebUrlComponent = ({ shareIntent }: { shareIntent: ShareIntent }) => {
source={
shareIntent.meta?.["og:image"]
? { uri: shareIntent.meta?.["og:image"] }
- : require("../assets/icon.png")
+ : undefined
}
style={[styles.icon, styles.gap, { borderRadius: 5 }]}
/>
- {shareIntent.meta?.title || ""}
+
+ {shareIntent.meta?.title || ""}
+
{shareIntent.webUrl}
diff --git a/plugin/src/ios/writeIosShareExtensionFiles.ts b/plugin/src/ios/writeIosShareExtensionFiles.ts
index e62b45e..654a7f3 100644
--- a/plugin/src/ios/writeIosShareExtensionFiles.ts
+++ b/plugin/src/ios/writeIosShareExtensionFiles.ts
@@ -77,7 +77,7 @@ export async function writeShareExtensionFiles(
platformProjectRoot,
parameters,
);
- const preprocessorContent = getPreprocessorContent();
+ const preprocessorContent = getPreprocessorContent(parameters);
await fs.promises.writeFile(preprocessorFilePath, preprocessorContent);
}
@@ -241,7 +241,8 @@ export function getPreprocessorFilePath(
);
}
-export function getPreprocessorContent() {
+export function getPreprocessorContent(parameters: Parameters) {
+ const injection = parameters.preprocessorInjectJS || "";
return `class ShareExtensionPreprocessor {
run({ completionFunction }) {
// Extract meta tags and image sources from the document
@@ -260,23 +261,7 @@ export function getPreprocessorContent() {
}
}
- if (!metas["og:image"]) {
- const mainImage = document.querySelector("img#main-image");
- if (mainImage) {
- const src = mainImage.getAttribute("src");
- if (src) {
- metas["og:image"] = src;
- }
- }
-
- const oldHiresImage = document.querySelector("img[data-old-hires]");
- if (oldHiresImage) {
- const oldHires = oldHiresImage.getAttribute("data-old-hires");
- if (oldHires) {
- metas["og:image"] = oldHires;
- }
- }
- }
+ ${injection}
// Call the completion function with the extracted data
completionFunction({
diff --git a/plugin/src/types.ts b/plugin/src/types.ts
index 445b3f5..9e329c4 100644
--- a/plugin/src/types.ts
+++ b/plugin/src/types.ts
@@ -9,6 +9,7 @@ export type Parameters = {
androidIntentFilters?: ("text/*" | "image/*" | "video/*" | "*/*")[];
androidMultiIntentFilters?: ("image/*" | "video/*" | "*/*")[];
disableExperimental?: boolean;
+ preprocessorInjectJS?: string;
disableAndroid?: boolean;
disableIOS?: boolean;
};
diff --git a/src/ExpoShareIntentModule.types.ts b/src/ExpoShareIntentModule.types.ts
index ebff141..5f7dd07 100644
--- a/src/ExpoShareIntentModule.types.ts
+++ b/src/ExpoShareIntentModule.types.ts
@@ -40,7 +40,7 @@ export type ShareIntentOptions = {
onResetShareIntent?: () => void;
};
-export type ShareIntentMeta = Record & {
+export type ShareIntentMeta = Record & {
title?: string;
};
@@ -48,7 +48,7 @@ export type ShareIntentMeta = Record & {
* Base type for what shared content is common between both platforms.
*/
interface BaseShareIntent {
- meta?: ShareIntentMeta;
+ meta?: ShareIntentMeta | null;
text?: string | null;
}
@@ -74,6 +74,7 @@ export interface AndroidShareIntent extends BaseShareIntent {
*/
export interface IosShareIntent extends BaseShareIntent {
files?: IosShareIntentFile[];
+ weburls?: { url: string; meta: string }[];
type: "media" | "file" | "text" | "weburl";
}
diff --git a/src/utils.ts b/src/utils.ts
index 9ba64ad..aaa33b3 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -56,21 +56,33 @@ export const getShareExtensionKey = (options?: ShareIntentOptions) => {
// 3: "file",
// };
+export function parseJson(
+ value: string,
+ defaultValue: T | null = null,
+): T | null {
+ try {
+ return JSON.parse(value) as T;
+ } catch (e) {
+ console.debug(e);
+ return defaultValue;
+ }
+}
+
export const parseShareIntent = (
value: string | AndroidShareIntent,
options: ShareIntentOptions,
): ShareIntent => {
let result = SHAREINTENT_DEFAULTVALUE;
if (!value) return result;
- let shareIntent;
+ let shareIntent: IosShareIntent | AndroidShareIntent | null;
// ios native module send a raw string of the json, try to parse it
if (typeof value === "string") {
- shareIntent = JSON.parse(value) as IosShareIntent; // iOS
+ shareIntent = parseJson(value); // iOS
} else {
shareIntent = value; // Android
}
- if (shareIntent.text) {
+ if (shareIntent?.text) {
// Try to find the webURL in the SharedIntent text
const webUrl =
shareIntent.text.match(
@@ -86,13 +98,13 @@ export const parseShareIntent = (
title: shareIntent.meta?.title ?? undefined,
},
};
- } else if (shareIntent.weburls?.length) {
- const weburl = shareIntent.weburls[0];
+ } else if ((shareIntent as IosShareIntent)?.weburls?.length) {
+ const weburl = (shareIntent as IosShareIntent).weburls![0];
result = {
...SHAREINTENT_DEFAULTVALUE,
type: "weburl",
webUrl: weburl.url,
- meta: JSON.parse(weburl.meta),
+ meta: parseJson>(weburl.meta, {}),
};
} else {
// Ensure we got a valid file. some array value are emply