stream-chat-react-native-core
Version:
The official React Native and Expo components for Stream Chat, a service for building chat applications
355 lines • 14.7 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MessageOverlayHostLayer = exports.DefaultMessageOverlayBackground = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactNativeGestureHandler = require("react-native-gesture-handler");
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
var _reactNativeTeleport = require("react-native-teleport");
var _ClosingPortalHostsLayer = require("./ClosingPortalHostsLayer");
var _useA11yLabel = require("../../a11y/hooks/useA11yLabel");
var _useAccessibilityAnnouncer = require("../../components/Accessibility/useAccessibilityAnnouncer");
var _hooks = require("../../hooks");
var _stateStore = require("../../state-store");
var _ComponentsContext = require("../componentsContext/ComponentsContext");
var _ThemeContext = require("../themeContext/ThemeContext");
var _jsxRuntime = require("react/jsx-runtime");
var _jsxFileName = "/home/runner/work/stream-chat-react-native/stream-chat-react-native/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx";
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
var DURATION = 300;
var DefaultMessageOverlayBackground = () => {
var _useTheme = (0, _ThemeContext.useTheme)(),
semantics = _useTheme.theme.semantics;
return (0, _jsxRuntime.jsx)(_reactNative.View, {
pointerEvents: "none",
style: _reactNative.StyleSheet.absoluteFill,
children: (0, _jsxRuntime.jsx)(_reactNative.View, {
pointerEvents: "none",
style: [_reactNative.StyleSheet.absoluteFill, {
backgroundColor: semantics.badgeBgOverlay
}]
})
});
};
exports.DefaultMessageOverlayBackground = DefaultMessageOverlayBackground;
var MessageOverlayHostLayer = () => {
var _useComponentsContext = (0, _ComponentsContext.useComponentsContext)(),
MessageActions = _useComponentsContext.MessageActions,
MessageOverlayBackground = _useComponentsContext.MessageOverlayBackground;
var _useOverlayController = (0, _stateStore.useOverlayController)(),
id = _useOverlayController.id,
closing = _useOverlayController.closing;
var insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
var _useWindowDimensions = (0, _reactNative.useWindowDimensions)(),
screenH = _useWindowDimensions.height;
var messageH = (0, _reactNativeReanimated.useSharedValue)(undefined);
var topH = (0, _reactNativeReanimated.useSharedValue)(undefined);
var bottomH = (0, _reactNativeReanimated.useSharedValue)(undefined);
var closeCorrectionY = (0, _reactNativeReanimated.useSharedValue)(0);
var topInset = insets.top;
var bottomInset = insets.bottom === 0 && _reactNative.Platform.OS === 'android' ? 60 : insets.bottom;
var isActive = !!id;
var announce = (0, _useAccessibilityAnnouncer.useAccessibilityAnnouncer)();
var overlayOpenHint = (0, _useA11yLabel.useA11yLabel)('a11y/Swipe right to go through different actions');
var announcedOpenRef = (0, _react.useRef)(false);
(0, _react.useEffect)(() => {
if (isActive) {
if (overlayOpenHint && !announcedOpenRef.current) {
announce(overlayOpenHint, 'polite');
announcedOpenRef.current = true;
}
} else {
announcedOpenRef.current = false;
}
}, [isActive, overlayOpenHint, announce]);
var orientation = (0, _hooks.useScreenOrientation)();
var prevOrientationRef = (0, _react.useRef)(orientation);
(0, _react.useEffect)(() => {
if (prevOrientationRef.current === orientation) {
return;
}
prevOrientationRef.current = orientation;
(0, _stateStore.finalizeCloseOverlay)();
}, [orientation]);
var padding = 8;
var minY = topInset + padding;
var maxY = screenH - bottomInset - padding;
var scrollY = (0, _reactNativeReanimated.useSharedValue)(0);
var scrollStartY = (0, _reactNativeReanimated.useSharedValue)(0);
var scrollGeom = (0, _reactNativeReanimated.useDerivedValue)(() => {
if (!messageH.value || !topH.value || !bottomH.value) {
return {
needsScroll: false
};
}
var available = maxY - minY;
var contentH = topH.value.h + messageH.value.h + bottomH.value.h;
return {
needsScroll: contentH > available
};
});
var maxScroll = (0, _reactNativeReanimated.useDerivedValue)(() => {
if (!scrollGeom.value.needsScroll || !messageH.value || !topH.value || !bottomH.value) {
return 0;
}
var available = maxY - minY;
var contentH = topH.value.h + messageH.value.h + bottomH.value.h;
return Math.max(0, contentH - available);
});
(0, _react.useEffect)(() => {
if (closing) {
(0, _reactNativeReanimated.cancelAnimation)(scrollY);
} else if (!isActive) {
scrollY.value = 0;
}
}, [closing, isActive, scrollY]);
var backdrop = (0, _reactNativeReanimated.useSharedValue)(0);
var closeCoverOpacity = (0, _reactNativeReanimated.useSharedValue)(0);
(0, _react.useEffect)(() => (0, _stateStore.registerOverlaySharedValueController)({
incrementCloseCorrectionY: deltaY => {
closeCorrectionY.value += deltaY;
},
resetCloseCorrectionY: () => {
closeCorrectionY.value = 0;
},
reset: () => {
messageH.value = undefined;
topH.value = undefined;
bottomH.value = undefined;
closeCorrectionY.value = 0;
},
setBottomH: rect => {
bottomH.value = rect;
},
setMessageH: rect => {
messageH.value = rect;
},
setTopH: rect => {
topH.value = rect;
}
}), [bottomH, closeCorrectionY, messageH, topH]);
(0, _react.useEffect)(() => {
var target = isActive && !closing ? 1 : 0;
backdrop.value = (0, _reactNativeReanimated.withSpring)(target, {
duration: DURATION + target * 100
}, finished => {
if (finished && closing) {
(0, _reactNativeReanimated.runOnJS)(_stateStore.finalizeCloseOverlay)();
}
});
closeCoverOpacity.value = (0, _reactNativeReanimated.withSpring)(closing ? 1 : 0, {
duration: DURATION
});
}, [isActive, closing, backdrop, closeCoverOpacity]);
var backdropStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => ({
opacity: backdrop.value
}));
var OverlayBackground = MessageOverlayBackground;
var messageShiftY = (0, _reactNativeReanimated.useDerivedValue)(() => {
if (!messageH.value || !topH.value || !bottomH.value) return 0;
var anchorY = messageH.value.y;
var msgH = messageH.value.h;
var minTop = minY + topH.value.h;
if (scrollGeom.value.needsScroll) {
return minTop - anchorY;
}
var maxTopWithBottom = maxY - (msgH + bottomH.value.h);
var canFitBottomWithoutOverlap = minTop <= maxTopWithBottom;
var solvedTop = canFitBottomWithoutOverlap ? (0, _reactNativeReanimated.clamp)(anchorY, minTop, Math.max(minTop, maxTopWithBottom)) : minTop;
return solvedTop - anchorY;
});
var bottomShiftY = (0, _reactNativeReanimated.useDerivedValue)(() => {
if (!messageH.value || !topH.value || !bottomH.value) return 0;
var anchorMessageTop = messageH.value.y;
var msgH = messageH.value.h;
var minMessageTop = minY + topH.value.h;
if (scrollGeom.value.needsScroll) {
return maxY - bottomH.value.h - bottomH.value.y;
}
var maxMessageTopWithBottom = maxY - (msgH + bottomH.value.h);
var canFitBottomWithoutOverlap = minMessageTop <= maxMessageTopWithBottom;
var solvedMessageTop = canFitBottomWithoutOverlap ? (0, _reactNativeReanimated.clamp)(anchorMessageTop, minMessageTop, Math.max(minMessageTop, maxMessageTopWithBottom)) : minMessageTop;
var solvedBottomTop = Math.min(solvedMessageTop + msgH, maxY - bottomH.value.h);
return solvedBottomTop - bottomH.value.y;
});
var topItemStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
if (!topH.value) return {
opacity: 0,
height: 0,
width: 0
};
var translateY = isActive ? closing ? closeCorrectionY.value : messageShiftY.value : 0;
var horizontalPosition = _reactNative.I18nManager.isRTL ? {
right: topH.value.x
} : {
left: topH.value.x
};
return {
height: topH.value.h,
opacity: 1,
position: 'absolute',
top: topH.value.y,
transform: [{
scale: backdrop.value
}, {
translateY: (0, _reactNativeReanimated.withSpring)(translateY, {
duration: DURATION
})
}],
width: topH.value.w,
...horizontalPosition
};
});
var bottomItemStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
if (!bottomH.value) return {
opacity: 0,
height: 0,
width: 0
};
var translateY = isActive ? closing ? closeCorrectionY.value : bottomShiftY.value : 0;
var horizontalPosition = _reactNative.I18nManager.isRTL ? {
right: bottomH.value.x
} : {
left: bottomH.value.x
};
return {
height: bottomH.value.h,
opacity: 1,
position: 'absolute',
top: bottomH.value.y,
transform: [{
scale: backdrop.value
}, {
translateY: (0, _reactNativeReanimated.withSpring)(translateY, {
duration: DURATION
})
}],
width: bottomH.value.w,
...horizontalPosition
};
});
var hostStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
if (!messageH.value) return {
height: 0
};
var scroll = scrollGeom.value.needsScroll ? scrollY.value : 0;
var translateY = isActive ? closing ? closeCorrectionY.value + scroll : messageShiftY.value : 0;
return {
height: messageH.value.h,
left: messageH.value.x,
position: 'absolute',
top: messageH.value.y,
transform: [{
translateY: (0, _reactNativeReanimated.withSpring)(translateY, {
duration: DURATION
})
}, {
translateY: scroll === 0 ? 0 : -scroll
}],
width: messageH.value.w
};
});
var pan = (0, _react.useMemo)(() => _reactNativeGestureHandler.Gesture.Pan().onBegin(() => {
scrollStartY.value = scrollY.value;
}).onUpdate(e => {
scrollY.value = (0, _reactNativeReanimated.clamp)(scrollStartY.value - e.translationY, 0, maxScroll.value);
}).onEnd(e => {
scrollY.value = (0, _reactNativeReanimated.withDecay)({
clamp: [0, maxScroll.value],
velocity: -e.velocityY
});
}), [maxScroll, scrollStartY, scrollY]);
var tap = (0, _react.useMemo)(() => _reactNativeGestureHandler.Gesture.Tap().onTouchesDown((e, state) => {
var t = e.allTouches[0];
if (!t || !topH || !bottomH) return;
var x = t.x;
var y = t.y;
var messageYShift = messageShiftY.value;
var bottomYShift = bottomShiftY.value;
var top = topH.value;
if (top) {
var topY = top.y + messageYShift;
if (x >= top.x && x <= top.x + top.w && y >= topY && y <= topY + top.h) {
state.fail();
return;
}
}
var bot = bottomH.value;
if (bot) {
var botY = bot.y + bottomYShift;
if (x >= bot.x && x <= bot.x + bot.w && y >= botY && y <= botY + bot.h) {
state.fail();
return;
}
}
}).onEnd(() => {
(0, _reactNativeReanimated.runOnJS)(_stateStore.closeOverlay)();
}).requireExternalGestureToFail(pan), [bottomH, bottomShiftY, messageShiftY, pan, topH]);
return (0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, {
gesture: tap,
children: (0, _jsxRuntime.jsxs)(_reactNative.View, {
accessibilityViewIsModal: isActive,
pointerEvents: "box-none",
style: _reactNative.StyleSheet.absoluteFill,
children: [isActive ? (0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
pointerEvents: "none",
style: [_reactNative.StyleSheet.absoluteFill, backdropStyle],
children: (0, _jsxRuntime.jsx)(OverlayBackground, {})
}) : null, (0, _jsxRuntime.jsxs)(_reactNative.View, {
pointerEvents: "box-none",
style: _reactNative.StyleSheet.absoluteFill,
children: [isActive ? (0, _jsxRuntime.jsx)(_reactNative.Pressable, {
onPress: _stateStore.closeOverlay,
style: _reactNative.StyleSheet.absoluteFill,
testID: "message-overlay-backdrop"
}) : null, MessageActions ? (0, _jsxRuntime.jsx)(MessageActions, {
bottomItemStyle: bottomItemStyle,
hostStyle: hostStyle,
portalHostStyle: styles.absoluteFill,
topItemStyle: topItemStyle
}) : (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
children: [(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, {
gesture: pan,
children: (0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
style: hostStyle,
testID: "message-overlay-message",
children: (0, _jsxRuntime.jsx)(_reactNativeTeleport.PortalHost, {
name: "message-overlay",
style: styles.absoluteFill
})
})
}), (0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
style: topItemStyle,
testID: "message-overlay-top",
children: (0, _jsxRuntime.jsx)(_reactNativeTeleport.PortalHost, {
name: "top-item",
style: styles.absoluteFill
})
}), (0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
style: bottomItemStyle,
testID: "message-overlay-bottom",
children: (0, _jsxRuntime.jsx)(_reactNativeTeleport.PortalHost, {
name: "bottom-item",
style: styles.absoluteFill
})
})]
})]
}), (0, _jsxRuntime.jsx)(_ClosingPortalHostsLayer.ClosingPortalHostsLayer, {
closeCoverOpacity: closeCoverOpacity
})]
})
});
};
exports.MessageOverlayHostLayer = MessageOverlayHostLayer;
var styles = _reactNative.StyleSheet.create({
absoluteFill: {
bottom: 0,
left: 0,
position: 'absolute',
right: 0,
top: 0
}
});
//# sourceMappingURL=MessageOverlayHostLayer.js.map