Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add a ios native module to calculate inset top and bottom #511

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions docs/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
yarn add react-native-toast-message
# or
npm install --save react-native-toast-message
cd ios && pod install # for iOS
```

## Usage
Expand Down
5 changes: 5 additions & 0 deletions ios/SafeAreaModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import <React/RCTBridgeModule.h>

@interface RNSafeAreaModule : NSObject <RCTBridgeModule>

@end
27 changes: 27 additions & 0 deletions ios/SafeAreaModule.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface SafeAreaModule : NSObject <RCTBridgeModule>
@end

@implementation SafeAreaModule
RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(getSafeAreaInsets:(RCTResponseSenderBlock)callback) {
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;

if (@available(iOS 11.0, *)) {
safeAreaInsets = rootViewController.view.safeAreaInsets;
}

NSDictionary *result = @{
@"top": @(safeAreaInsets.top),
@"bottom": @(safeAreaInsets.bottom),
@"left": @(safeAreaInsets.left),
@"right": @(safeAreaInsets.right)
};

callback(@[[NSNull null], result]);
}

@end
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"files": [
"/lib"
"/lib",
"ios",
"*.podspec"
],
"repository": {
"type": "git",
Expand Down
41 changes: 41 additions & 0 deletions react-native-toast-message.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require "json"

package = JSON.parse(File.read(File.join(__dir__, "package.json")))
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'

Pod::Spec.new do |s|
s.name = "react-native-toast-message"
s.version = package["version"]
s.summary = package["description"]
s.homepage = "https://github.com/calintamas/react-native-toast-message"
s.license = package["license"]
s.authors = package["author"]

s.platforms = { :ios => "9.0" }
s.source = { :git => "https://github.com/calintamas/react-native-toast-message.git", :tag => "#{s.version}" }

s.source_files = "ios/**/*.{h,m,mm,swift}"

# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
if respond_to?(:install_modules_dependencies, true)
install_modules_dependencies(s)
else
s.dependency "React-Core"

# Don't install the dependencies when we run `pod install` in the old architecture.
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
s.pod_target_xcconfig = {
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
}
s.dependency "React-Codegen"
s.dependency "RCT-Folly"
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core"
end
end
end
4 changes: 2 additions & 2 deletions src/__tests__/useToast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ describe('test useToast hook', () => {
position: 'bottom',
autoHide: false,
visibilityTime: 20,
topOffset: 120,
bottomOffset: 130,
topOffset: 40,
bottomOffset: 40,
keyboardOffset: 5,
onShow: jest.fn(),
onHide: jest.fn(),
Expand Down
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './useSlideAnimation';
export * from './useTimeout';
export * from './usePanResponder';
export * from './useKeyboard';
export * from './useSafeArea';
25 changes: 25 additions & 0 deletions src/hooks/useSafeArea.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {NativeModules, StatusBar} from 'react-native'
import {isIOS} from "../utils/platform";
import {useEffect, useState} from "react";
import {TSafeArea} from "../types";
import {SCREEN_HEIGHT, WINDOW_HEIGHT} from "../utils/dimension";
export const useSafeArea = () => {
const [safeAreaInsets, setSafeAreaInsets] = useState<TSafeArea | null>(null);
useEffect(() => {
if(isIOS()){
NativeModules?.SafeAreaModule?.getSafeAreaInsets((error : Error, result: TSafeArea) => {
if (error) {
console.error(error);
} else {
setSafeAreaInsets(result)
}
});
}else {
const statusBarHeight = StatusBar.currentHeight ?? 0
const bottomInset = SCREEN_HEIGHT - WINDOW_HEIGHT - statusBarHeight;
setSafeAreaInsets({top: statusBarHeight, bottom: bottomInset, left: 0, right: 0})
}
},[])

return {safeAreaInsets}
}
seyedmostafahasani marked this conversation as resolved.
Show resolved Hide resolved
18 changes: 18 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,21 @@ export type ToastProps = {
*/
onPress?: () => void;
};

export type TSafeArea = {
top: number
bottom: number
left: number
right: number
}

export type TNativeModulesSafeArea = {
SafeAreaInsetsModule: {
getSafeAreaInsets(p: (error: Error, result: TSafeArea) => void): Promise<{
top: number;
bottom: number;
left: number;
right: number
}>;
};
seyedmostafahasani marked this conversation as resolved.
Show resolved Hide resolved
}
7 changes: 4 additions & 3 deletions src/useToast.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

import { useLogger } from './contexts';
import { useTimeout } from './hooks';
import {useSafeArea, useTimeout} from './hooks';
import { ToastData, ToastOptions, ToastProps, ToastShowParams } from './types';
import { noop } from './utils/func';
import { mergeIfDefined } from './utils/obj';
Expand Down Expand Up @@ -34,6 +34,7 @@ export function useToast({ defaultOptions }: UseToastParams) {

const [isVisible, setIsVisible] = React.useState(false);
const [data, setData] = React.useState<ToastData>(DEFAULT_DATA);
const {safeAreaInsets} = useSafeArea()

const initialOptions = mergeIfDefined(
DEFAULT_OPTIONS,
Expand Down Expand Up @@ -62,15 +63,15 @@ export function useToast({ defaultOptions }: UseToastParams) {
const show = React.useCallback(
(params: ToastShowParams) => {
log(`Showing with params: ${JSON.stringify(params)}`);
const topOffset = safeAreaInsets && safeAreaInsets.top > 0 ? safeAreaInsets.top : initialOptions.topOffset
const bottomOffset = safeAreaInsets && safeAreaInsets.bottom > 0 ? safeAreaInsets.bottom : initialOptions.bottomOffset
const {
text1 = DEFAULT_DATA.text1,
text2 = DEFAULT_DATA.text2,
type = initialOptions.type,
position = initialOptions.position,
autoHide = initialOptions.autoHide,
visibilityTime = initialOptions.visibilityTime,
topOffset = initialOptions.topOffset,
bottomOffset = initialOptions.bottomOffset,
keyboardOffset = initialOptions.keyboardOffset,
onShow = initialOptions.onShow,
onHide = initialOptions.onHide,
Expand Down
4 changes: 4 additions & 0 deletions src/utils/dimension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {Dimensions} from "react-native";

export const {height: WINDOW_HEIGHT} = Dimensions.get('window')
export const {height: SCREEN_HEIGHT} = Dimensions.get('screen')
Loading