mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-10 13:38:49 +08:00
fix(ios): stabilize keyboard toolbar, image picker, and scrolling (#15182)
## Summary - keep the editor active when iOS keyboard toolbar interactions move focus into range-sync excluded widgets - use the native iOS image picker/source sheet and sync native presentation with the app theme - pin `ListViewKit` to `1.1.6` so the iOS workspace resolves with Xcode 16.3 - restore vertical scrolling in the iOS `WKWebView` by removing the global `contentOffset` reset while preserving zoom prevention ## Test plan - [x] `yarn vitest --run --config \"vitest.config.ts\" --browser.enabled=false \"src/__tests__/inline/active.unit.spec.ts\"` - [x] `LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 xcodebuild -resolvePackageDependencies -workspace \"packages/frontend/apps/ios/App/App.xcworkspace\" -scheme \"App\"` - [x] `xcodebuild -workspace \"App.xcworkspace\" -scheme \"App\" -destination \"generic/platform=iOS Simulator\" build CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES ARCHS=arm64` - [x] Xcode build validation for the updated PR branch
This commit is contained in:
@@ -54,9 +54,11 @@ import {
|
||||
MarkdownAdapter,
|
||||
titleMiddleware,
|
||||
} from '@blocksuite/affine/shared/adapters';
|
||||
import { registerNativeImageFilesPicker } from '@blocksuite/affine/shared/utils';
|
||||
import { MarkdownTransformer } from '@blocksuite/affine/widgets/linked-doc';
|
||||
import { App as CapacitorApp } from '@capacitor/app';
|
||||
import { Browser } from '@capacitor/browser';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { Haptics } from '@capacitor/haptics';
|
||||
import { Keyboard, KeyboardStyle } from '@capacitor/keyboard';
|
||||
import { Framework, FrameworkRoot, getCurrentStore } from '@toeverything/infra';
|
||||
@@ -69,8 +71,10 @@ import { RouterProvider } from 'react-router-dom';
|
||||
|
||||
import { BlocksuiteMenuConfigProvider } from './bs-menu-config';
|
||||
import { ModalConfigProvider } from './modal-config';
|
||||
import { AffineTheme } from './plugins/affine-theme';
|
||||
import { Auth } from './plugins/auth';
|
||||
import { Hashcash } from './plugins/hashcash';
|
||||
import { ImagePicker } from './plugins/image-picker';
|
||||
import { NbStoreNativeDBApis } from './plugins/nbstore';
|
||||
import { PayWall } from './plugins/paywall';
|
||||
import { Preview } from './plugins/preview';
|
||||
@@ -237,6 +241,39 @@ registerNativePreviewHandlers({
|
||||
renderMermaidSvg: request => Preview.renderMermaidSvg(request),
|
||||
renderTypstSvg: request => Preview.renderTypstSvg(request),
|
||||
});
|
||||
registerNativeImageFilesPicker(async () => {
|
||||
const result = await ImagePicker.pickImages({ multiple: true });
|
||||
if (result.canceled || result.files.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const settled = await Promise.allSettled(
|
||||
result.files.map(async file => {
|
||||
const filePath = file.path.startsWith('file://')
|
||||
? file.path
|
||||
: `file://${file.path}`;
|
||||
const response = await fetch(Capacitor.convertFileSrc(filePath));
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to read image picker file: ${file.name} (status ${response.status})`
|
||||
);
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
return new File([blob], file.name, {
|
||||
type: file.mimeType || blob.type || 'image/*',
|
||||
lastModified: file.lastModified,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
return settled
|
||||
.filter(
|
||||
(settledResult): settledResult is PromiseFulfilledResult<File> =>
|
||||
settledResult.status === 'fulfilled'
|
||||
)
|
||||
.map(settledResult => settledResult.value);
|
||||
});
|
||||
|
||||
// ------ some apis for native ------
|
||||
(window as any).getCurrentServerBaseUrl = () => {
|
||||
@@ -252,6 +289,9 @@ registerNativePreviewHandlers({
|
||||
(window as any).getCurrentI18nLocale = () => {
|
||||
return I18n.language;
|
||||
};
|
||||
(window as any).getCurrentThemeMode = () => {
|
||||
return 'system';
|
||||
};
|
||||
(window as any).getAiButtonFeatureFlag = () => {
|
||||
const featureFlagService = frameworkProvider.get(FeatureFlagService);
|
||||
return featureFlagService.flags.enable_mobile_ai_button.value;
|
||||
@@ -522,6 +562,18 @@ const KeyboardThemeProvider = () => {
|
||||
});
|
||||
}, [resolvedTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
const themeMode = resolvedTheme === 'dark' ? 'dark' : 'light';
|
||||
(window as any).getCurrentThemeMode = () => {
|
||||
return themeMode;
|
||||
};
|
||||
AffineTheme.onThemeChanged({
|
||||
themeMode,
|
||||
}).catch(e => {
|
||||
console.error(`Failed to sync app theme: ${e}`);
|
||||
});
|
||||
}, [resolvedTheme]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { NativeThemeMode } from './theme-mode';
|
||||
|
||||
export interface AffineThemePlugin {
|
||||
onThemeChanged(options: { themeMode: NativeThemeMode }): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { registerPlugin } from '@capacitor/core';
|
||||
|
||||
import type { AffineThemePlugin } from './definitions';
|
||||
|
||||
const AffineTheme = registerPlugin<AffineThemePlugin>('AffineTheme');
|
||||
|
||||
export * from './definitions';
|
||||
export * from './theme-mode';
|
||||
export { AffineTheme };
|
||||
@@ -0,0 +1 @@
|
||||
export type NativeThemeMode = 'dark' | 'light' | 'system';
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface PickedImageFile {
|
||||
path: string;
|
||||
name: string;
|
||||
mimeType: string;
|
||||
lastModified: number;
|
||||
}
|
||||
|
||||
export interface ImagePickerPlugin {
|
||||
pickImages(options?: { multiple?: boolean }): Promise<{
|
||||
files: PickedImageFile[];
|
||||
canceled?: boolean;
|
||||
}>;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { registerPlugin } from '@capacitor/core';
|
||||
|
||||
import type { ImagePickerPlugin } from './definitions';
|
||||
|
||||
const ImagePicker = registerPlugin<ImagePickerPlugin>('ImagePicker');
|
||||
|
||||
export * from './definitions';
|
||||
export { ImagePicker };
|
||||
Reference in New Issue
Block a user