diff --git a/src/features/comment/reply/CommentReply.tsx b/src/features/comment/reply/CommentReply.tsx
index 5e54f9ff0b..b499920fdd 100644
--- a/src/features/comment/reply/CommentReply.tsx
+++ b/src/features/comment/reply/CommentReply.tsx
@@ -23,24 +23,25 @@ import { Centered, Spinner } from "../../auth/Login";
import { handleSelector, jwtSelector } from "../../auth/authSlice";
import { css } from "@emotion/react";
import { preventPhotoswipeGalleryFocusTrap } from "../../gallery/GalleryImg";
+import TextareaAutosizedForOnScreenKeyboard from "../../shared/TextareaAutosizedForOnScreenKeyboard";
export const Container = styled.div`
- position: absolute;
- inset: 0;
+ min-height: 100%;
display: flex;
flex-direction: column;
`;
-export const Textarea = styled.textarea`
+export const Textarea = styled(TextareaAutosizedForOnScreenKeyboard)`
border: 0;
background: none;
resize: none;
outline: 0;
padding: 1rem;
- flex: 1 0 0;
- min-height: 7rem;
+ min-height: 200px;
+
+ flex: 1 0 auto;
${({ theme }) =>
!theme.dark &&
diff --git a/src/features/post/new/NewPostText.tsx b/src/features/post/new/NewPostText.tsx
index 92a831f8bc..d0c770ab1b 100644
--- a/src/features/post/new/NewPostText.tsx
+++ b/src/features/post/new/NewPostText.tsx
@@ -12,23 +12,23 @@ import {
import { useState } from "react";
import { Centered, Spinner } from "../../auth/Login";
import { css } from "@emotion/react";
+import TextareaAutosizedForOnScreenKeyboard from "../../shared/TextareaAutosizedForOnScreenKeyboard";
const Container = styled.div`
- position: absolute;
- inset: 0;
+ min-height: 100%;
display: flex;
flex-direction: column;
`;
-const Textarea = styled.textarea`
+const Textarea = styled(TextareaAutosizedForOnScreenKeyboard)`
border: 0;
background: none;
resize: none;
outline: 0;
padding: 1rem;
- flex: 1 0 0;
+ flex: 1 0 auto;
min-height: 7rem;
${({ theme }) =>
diff --git a/src/features/shared/DynamicDismissableModal.tsx b/src/features/shared/DynamicDismissableModal.tsx
index 2545d73e00..a904322886 100644
--- a/src/features/shared/DynamicDismissableModal.tsx
+++ b/src/features/shared/DynamicDismissableModal.tsx
@@ -5,9 +5,10 @@ import React, {
useRef,
useState,
} from "react";
-import { IonModal, useIonActionSheet } from "@ionic/react";
+import { useIonActionSheet } from "@ionic/react";
import { PageContext } from "../auth/PageContext";
import { Prompt, useLocation } from "react-router";
+import IonModalAutosizedForOnScreenKeyboard from "./IonModalAutosizedForOnScreenKeyboard";
export interface DismissableProps {
dismiss: () => void;
@@ -82,7 +83,7 @@ export function DynamicDismissableModal({
when={!canDismiss}
message="Are you sure you want to discard your work?"
/>
- setIsOpen(false)}
@@ -99,7 +100,7 @@ export function DynamicDismissableModal({
onDismissAttemptCb();
},
})}
-
+
>
);
}
diff --git a/src/features/shared/IonModalAutosizedForOnScreenKeyboard.tsx b/src/features/shared/IonModalAutosizedForOnScreenKeyboard.tsx
new file mode 100644
index 0000000000..89795a8f39
--- /dev/null
+++ b/src/features/shared/IonModalAutosizedForOnScreenKeyboard.tsx
@@ -0,0 +1,108 @@
+import { IonModal } from "@ionic/react";
+import React, { useCallback, useEffect, useRef, useState } from "react";
+import usePageVisibility from "../../helpers/usePageVisibility";
+import styled from "@emotion/styled";
+
+// TODO it's a bit buggy trying to compute this
+// in realtime with the new post dialog + comment dialogs
+// So hardcode for now
+const FIXED_HEADER_HEIGHT = 56;
+
+const StyledIonModal = styled(IonModal)<{ viewportHeight: number }>`
+ ion-content::part(scroll) {
+ max-height: ${({ viewportHeight }) => viewportHeight}px;
+ }
+`;
+
+export default function IonModalAutosizedForOnScreenKeyboard(
+ props: React.ComponentProps
+) {
+ const [viewportHeight, setViewportHeight] = useState(
+ document.documentElement.clientHeight
+ );
+ const isVisible = usePageVisibility();
+ // eslint-disable-next-line no-undef
+ const modalRef = useRef(null);
+
+ const updateViewport = useCallback(() => {
+ if (!props.isOpen) return;
+
+ // For the rare legacy browsers that don't support it
+ if (!window.visualViewport) {
+ return;
+ }
+
+ const page = modalRef.current?.querySelector(
+ ".ion-page:not(.ion-page-hidden)"
+ );
+
+ setViewportHeight(
+ window.visualViewport.height -
+ (page instanceof HTMLElement ? cumulativeOffset(page).top : 0) -
+ FIXED_HEADER_HEIGHT
+ );
+ }, [props.isOpen]);
+
+ const onScroll = useCallback(() => {
+ setTimeout(() => {
+ window.scrollTo(0, 0);
+ }, 100);
+ }, []);
+
+ // Turning iPhone on/off can mess up the scrolling to top again
+ useEffect(() => {
+ if (!props.isOpen) return;
+
+ updateViewport();
+ }, [isVisible, updateViewport, props.isOpen]);
+
+ useEffect(() => {
+ if (!props.isOpen) return;
+
+ document.addEventListener("scroll", onScroll);
+
+ return () => {
+ document.removeEventListener("scroll", onScroll);
+ };
+ }, [onScroll, props.isOpen]);
+
+ useEffect(() => {
+ if (!props.isOpen) return;
+
+ const onResize = () => {
+ updateViewport();
+ };
+
+ window.visualViewport?.addEventListener("resize", onResize);
+
+ return () => {
+ window.visualViewport?.removeEventListener("resize", onResize);
+ };
+ }, [updateViewport, props.isOpen]);
+
+ return (
+ {
+ window.scrollTo(0, 0);
+ }}
+ {...props}
+ />
+ );
+}
+
+function cumulativeOffset(element: HTMLElement) {
+ let top = 0,
+ left = 0;
+ do {
+ top += element.offsetTop || 0;
+ left += element.offsetLeft || 0;
+ element = element.offsetParent as HTMLElement;
+ } while (element instanceof HTMLElement);
+
+ return {
+ top: top,
+ left: left,
+ };
+}
diff --git a/src/features/shared/TextareaAutosizedForOnScreenKeyboard.tsx b/src/features/shared/TextareaAutosizedForOnScreenKeyboard.tsx
new file mode 100644
index 0000000000..ffc8e480b9
--- /dev/null
+++ b/src/features/shared/TextareaAutosizedForOnScreenKeyboard.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+import { isAppleDeviceInstalledToHomescreen } from "../../helpers/device";
+import { fixSafariAutoscroll } from "../../helpers/safari";
+import TextareaAutosize, {
+ TextareaAutosizeProps,
+} from "react-textarea-autosize";
+
+export default function TextareaAutosizedForOnScreenKeyboard(
+ props: Omit<
+ TextareaAutosizeProps & React.RefAttributes,
+ "onFocus"
+ >
+) {
+ return (
+ {
+ if (!isAppleDeviceInstalledToHomescreen()) return;
+
+ // https://stackoverflow.com/a/74902393/1319878
+ const target = e.currentTarget;
+ target.style.opacity = "0";
+ setTimeout(() => (target.style.opacity = "1"));
+
+ fixSafariAutoscroll();
+ }}
+ {...props}
+ />
+ );
+}
diff --git a/src/helpers/safari.ts b/src/helpers/safari.ts
new file mode 100644
index 0000000000..16bd4be759
--- /dev/null
+++ b/src/helpers/safari.ts
@@ -0,0 +1,13 @@
+export function fixSafariAutoscroll() {
+ let checkAttempts = 0;
+
+ const interval = setInterval(() => {
+ window.scrollTo(0, 0);
+
+ if (window.scrollY === 0) {
+ checkAttempts++;
+
+ if (checkAttempts > 10) clearInterval(interval);
+ }
+ }, 100);
+}