mirror of
https://github.com/colanode/colanode.git
synced 2026-05-18 13:15:12 +02:00
Mobile: fix page editor keyboard and interaction issues (#346)
* Mobile: add prebuild hook and submit config for EAS builds Add prebuildCommand to run copy-editor.js during EAS builds so the editor.html asset is available for Metro bundling. Also restore the App Store Connect submit credentials (appleId, appleTeamId, ascAppId) so builds auto-submit to TestFlight. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Mobile: use eas-build-post-install hook for editor asset prebuildCommand replaces expo prebuild entirely, causing build failures. Use eas-build-post-install lifecycle hook instead to run copy-editor.js after npm install but before the native build. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Mobile: fix page editor keyboard and interaction issues - Disable native WebView scrolling and use CSS overflow to prevent scroll-to-top when keyboard opens - Fix slash command click-through by preventing ProseMirror from processing mousedown events immediately after command selection - Remove keyboard from auto-opening on page load and auto-focusing cursor; keyboard now only appears on user tap - Add eas-build-post-install hook to build editor HTML asset during EAS builds - Improve editor.html iframe sandbox and CSP settings Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,6 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
AppState,
|
||||
AppStateStatus,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
@@ -143,10 +141,7 @@ export default function PageScreen() {
|
||||
)}
|
||||
<View style={styles.headerSpacer} />
|
||||
</View>
|
||||
<KeyboardAvoidingView
|
||||
style={styles.content}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
>
|
||||
<View style={styles.content}>
|
||||
<PageWebView
|
||||
nodeId={pageId!}
|
||||
userId={userId}
|
||||
@@ -159,7 +154,7 @@ export default function PageScreen() {
|
||||
updates={docUpdates ?? []}
|
||||
onNavigateNode={handleNavigateNode}
|
||||
/>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
{page && (
|
||||
<RenameNodeSheet
|
||||
visible={showRename}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"scripts": {
|
||||
"compile": "tsc --noEmit -p tsconfig.json",
|
||||
"lint": "eslint app src --ext .ts,.tsx --max-warnings 0",
|
||||
"eas-build-post-install": "node scripts/copy-editor.js",
|
||||
"prestart": "node scripts/copy-editor.js",
|
||||
"preios": "node scripts/copy-editor.js",
|
||||
"preandroid": "node scripts/copy-editor.js",
|
||||
|
||||
@@ -337,6 +337,25 @@ export const PageWebView = ({
|
||||
payload: { message: 'Unhandled rejection: ' + (e.reason && e.reason.message || e.reason || 'unknown') }
|
||||
}));
|
||||
});
|
||||
|
||||
// Prevent scroll-to-top on focus: save scroll container position and restore it
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var sc = document.getElementById('scroll-container');
|
||||
if (!sc) return;
|
||||
var saved = 0;
|
||||
sc.addEventListener('scroll', function() { saved = sc.scrollTop; }, { passive: true });
|
||||
document.addEventListener('focusin', function() {
|
||||
var s = saved;
|
||||
// Restore after browser/ProseMirror auto-scroll
|
||||
requestAnimationFrame(function() {
|
||||
requestAnimationFrame(function() {
|
||||
if (Math.abs(sc.scrollTop - s) > 50) {
|
||||
sc.scrollTop = s;
|
||||
}
|
||||
});
|
||||
});
|
||||
}, true);
|
||||
});
|
||||
true;
|
||||
`}
|
||||
style={[
|
||||
@@ -346,9 +365,8 @@ export const PageWebView = ({
|
||||
]}
|
||||
javaScriptEnabled
|
||||
domStorageEnabled
|
||||
scrollEnabled
|
||||
scrollEnabled={false}
|
||||
bounces={false}
|
||||
keyboardDisplayRequiresUserAction={false}
|
||||
hideKeyboardAccessoryView
|
||||
automaticallyAdjustContentInsets={false}
|
||||
contentMode="mobile"
|
||||
|
||||
@@ -56,16 +56,24 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
#scroll-container {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="editor-root"></div>
|
||||
<div id="scroll-container">
|
||||
<div id="editor-root"></div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -76,6 +76,7 @@ import {
|
||||
TableCommand,
|
||||
TodoCommand,
|
||||
} from '@colanode/ui/editor/commands';
|
||||
import { isSlashCommandActive } from '@colanode/ui/editor/extensions/commander';
|
||||
import { MobilePageNode } from './extensions/page';
|
||||
import { MobileFolderNode } from './extensions/folder';
|
||||
import { MobileFileNode } from './extensions/file';
|
||||
@@ -235,6 +236,15 @@ export const DocumentEditor = ({
|
||||
'prose-lg prose-stone dark:prose-invert prose-headings:font-title font-default focus:outline-none max-w-full text-foreground',
|
||||
spellCheck: 'true',
|
||||
},
|
||||
handleDOMEvents: {
|
||||
mousedown: (_view, event) => {
|
||||
if (isSlashCommandActive()) {
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
content: buildEditorContent(
|
||||
node.id,
|
||||
|
||||
@@ -180,7 +180,7 @@ export function MobileEditorApp() {
|
||||
style={{
|
||||
padding: '0 16px',
|
||||
paddingBottom: 'env(safe-area-inset-bottom, 20px)',
|
||||
minHeight: '100vh',
|
||||
minHeight: '100%',
|
||||
}}
|
||||
>
|
||||
<DocumentEditor
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
/* Mobile-specific editor styles */
|
||||
.ProseMirror {
|
||||
min-height: 60vh;
|
||||
min-height: 70vh;
|
||||
padding-bottom: 40vh;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,15 @@ interface CommanderOptions {
|
||||
|
||||
const navigationKeys = ['ArrowUp', 'ArrowDown', 'Enter'];
|
||||
|
||||
// Flag to prevent ProseMirror from handling the mousedown that follows
|
||||
// after a slash command item is tapped (pointerdown destroys the popup,
|
||||
// then mousedown lands on the editor underneath).
|
||||
let slashCommandJustSelected = false;
|
||||
|
||||
export function isSlashCommandActive() {
|
||||
return slashCommandJustSelected;
|
||||
}
|
||||
|
||||
const filterCommands = ({
|
||||
query,
|
||||
commands,
|
||||
@@ -167,6 +176,8 @@ const CommandList = ({
|
||||
// Added this event handler because the onClick handler was not working
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
slashCommandJustSelected = true;
|
||||
setTimeout(() => { slashCommandJustSelected = false; }, 300);
|
||||
selectItem(index);
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user