achorein / expo-share-intent

🚀 Simple share intent in an Expo Native Module
MIT License
128 stars 13 forks source link

Example to use it with expo go #23

Closed linonetwo closed 4 months ago

linonetwo commented 4 months ago

Sometimes we just want to use expo go to debut other parts of app, here is how to do it

/* eslint-disable @typescript-eslint/strict-boolean-expressions */
import type { useShareIntent as IUseShareIntent } from 'expo-share-intent';
import { useEffect } from 'react';
import { useRegisterProxy } from 'react-native-postmessage-cat';
import { nativeService } from '.';
import { NativeServiceIPCDescriptor } from './descriptor';

export function useNativeService() {
  const [webViewReference, onMessageReference] = useRegisterProxy(nativeService, NativeServiceIPCDescriptor);
  return [webViewReference, onMessageReference] as const;
}

export function useRequestNativePermissions() {
  useEffect(() => {
    void (async () => {
      await nativeService.requestCameraPermission();
      await nativeService.requestMicrophonePermission();
    })();
  }, []);
}

export function useRegisterReceivingShareIntent() {
  /** If you get error on development:
   * ```
   *  Error: Cannot find native module 'ExpoShareIntentModule', js engine: hermes
   * Invariant Violation: "main" has not been registered. This can happen if:
   * Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
   * A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called., js engine: hermes
   * ```
   *
   * Comment out this import will work.
   *
   * Also comment out all code inside `useRegisterReceivingShareIntent`.
   */
  if (process.env.NODE_ENV === 'development') {
    return;
  }
  const { useShareIntent } = require('expo-share-intent') as { useShareIntent: typeof IUseShareIntent };
  /* eslint-disable react-hooks/rules-of-hooks */
  const { hasShareIntent, shareIntent, resetShareIntent, error } = useShareIntent({
    debug: true,
  });

  useEffect(() => {
    if (error !== undefined) {
      console.log(
        `Failed to get ShareIntent, This is normal if you are using Expo Go for dev. To debug sharing feature, create a dev build "pnpm start:devClient" instead. ${error}`,
      );
    }
    void (async () => {
      try {
        if (hasShareIntent) {
          await nativeService.receivingShareIntent(shareIntent);
          resetShareIntent();
        }
      } catch (error) {
        console.log(
          `Failed to registerReceivingShareIntent, This is normal if you are using Expo Go for dev. To debug sharing feature, create a dev build "pnpm start:devClient" instead. ${
            (error as Error).message
          }`,
        );
      }
    })();
  }, [hasShareIntent, shareIntent, resetShareIntent, error]);
}

and start it with

"android": "cross-env NODE_ENV=development expo start --go --android",

It was easier when this lib also export non-hook api, but I find it is possible to workaround it today, so share with anyone with same problem.

achorein commented 4 months ago

wow ! thanks for sharing your example, sounds a bit complex but it's a good start :)

wondering if i can add something like that in a futur version :

useShareIntent({
    disabled: true,
});
linonetwo commented 4 months ago

Notice that my previous code that use expo-share-intent was:

import { ShareIntent } from 'expo-share-intent';

/// ...

      if (process.env.NODE_ENV === 'development') {
        return;
      }
      ReceiveSharingIntent.getReceivedFiles(
        async (
          files: ISharedFile[],
        ) => {
// ...

If I early return, just before actually calling the imperactive api, it was safe.

But in this version it still throw error when import the hook

import { useShareIntent } from 'expo-share-intent';

// ...
  if (process.env.NODE_ENV === 'development') {
    return;
  }
  /* eslint-disable react-hooks/rules-of-hooks */
  const { hasShareIntent, shareIntent, resetShareIntent, error } = useShareIntent({
    debug: true,
  });

Still throw the error Error: Cannot find native module 'ExpoShareIntentModule', js engine: hermes! So my workaround above use require inline.

linonetwo commented 4 months ago

Anyway this workaround does work, just for people googled here, closing this issue.

I haven't get sharing work yet, because expo's dev client is not working, and they don't want to fix it https://github.com/expo/expo/issues/27536

achorein commented 4 months ago

🤔 By the way, ReceiveSharingIntent.getReceivedFiles should not be used with this package, make sure you removed expo-config-plugin-ios-share-extension and react-native-receive-sharing-intent.

linonetwo commented 4 months ago

Thanks, I know that, I was just post old code to illustrate that old package can import and then conditionally block execution. But the new package is different.

achorein commented 4 months ago

You can now simply disabled share intent to run it into expo go (v1.2.0 and v0.4.0)

For example :

const { shareIntent } = useShareIntent({ disabled: process.env.NODE_ENV === 'development'})
<ShareIntentProvider options={{ disabled: process.env.NODE_ENV === 'development'}}>
  <App />
</ShareIntentProvider>
linonetwo commented 4 months ago

Many thanks, this is convenient.

Episodex commented 2 months ago

Hi, I'm trying to use this new disabled option, but I can't get it to work. I downloaded the repo and modified expo-router example by just adding disabled: true in _layout.tsx:

export default function Layout() {
  const router = useRouter();

  return (
    <ShareIntentProvider
      options={{
        disabled: true,
        debug: true,
        resetOnBackground: true,
        onResetShareIntent: () =>
          // used when app going in background and when the reset button is pressed
          router.replace({
            pathname: "/",
          }),
      }}
    >
      <Slot />
    </ShareIntentProvider>
  );
}

When running the code using expo start I get:

 ERROR  TypeError: Cannot read property 'useState' of null

This error is located at:
    in ShareIntentProvider (created by Layout)
    in Layout
    in Unknown (created by Route())
    in Suspense (created by Route())
    in Route (created by Route())
    in Route() (created by ContextNavigator)
    in RNCSafeAreaProvider (created by SafeAreaProvider)
    in SafeAreaProvider (created by wrapper)
    in wrapper (created by ContextNavigator)
    in EnsureSingleNavigator
    in BaseNavigationContainer
    in ThemeProvider
    in NavigationContainerInner (created by ContextNavigator)
    in ContextNavigator (created by ExpoRoot)
    in ExpoRoot (created by App)
    in App (created by ErrorOverlay)
    in ErrorToastContainer (created by ErrorOverlay)
    in ErrorOverlay (created by withDevTools(ErrorOverlay))
    in withDevTools(ErrorOverlay)
    in RCTView (created by View)
    in View (created by AppContainer)
    in RCTView (created by View)
    in View (created by AppContainer)
    in AppContainer
    in main(RootComponent), js engine: hermes

Also it complains about the rule of hooks violation.

I tried to use the package in my project with disabled: true but it also doesn't work. That's why I tried with the example.

Is this example correct at the moment? Do I do something wrong?

Btw, thank you very much for this package and updates to it!