Skip to content

Commit

Permalink
feat(mobile): disable swipe back gesture when there is no back in header
Browse files Browse the repository at this point in the history
  • Loading branch information
CatsJuice committed Nov 22, 2024
1 parent 2624e26 commit 454363d
Show file tree
Hide file tree
Showing 19 changed files with 176 additions and 15 deletions.
13 changes: 12 additions & 1 deletion packages/frontend/apps/ios/App/App.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
9D90BE2D2CCB9876006677DB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9D90BE222CCB9876006677DB /* Main.storyboard */; };
9D90BE2E2CCB9876006677DB /* public in Resources */ = {isa = PBXBuildFile; fileRef = 9D90BE232CCB9876006677DB /* public */; };
C4C413792CBE705D00337889 /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
E93B276C2CED92B1001409B8 /* NavigationGesturePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93B276B2CED92B1001409B8 /* NavigationGesturePlugin.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -35,6 +36,7 @@
9D90BE232CCB9876006677DB /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
E93B276B2CED92B1001409B8 /* NavigationGesturePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationGesturePlugin.swift; sourceTree = "<group>"; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -65,7 +67,6 @@
504EC3051FED79650016851F /* Products */,
7F8756D8B27F46E3366F6CEA /* Pods */,
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
9D6A85312CCF6D6B00DAB35F /* Recovered References */,
);
indentWidth = 2;
sourceTree = "<group>";
Expand Down Expand Up @@ -101,6 +102,7 @@
9D90BE1A2CCB9876006677DB /* plugins */ = {
isa = PBXGroup;
children = (
E93B276A2CED9298001409B8 /* NavigationGesture */,
9D90BE192CCB9876006677DB /* Cookie */,
);
path = plugins;
Expand All @@ -122,6 +124,14 @@
path = App;
sourceTree = "<group>";
};
E93B276A2CED9298001409B8 /* NavigationGesture */ = {
isa = PBXGroup;
children = (
E93B276B2CED92B1001409B8 /* NavigationGesturePlugin.swift */,
);
path = NavigationGesture;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -234,6 +244,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E93B276C2CED92B1001409B8 /* NavigationGesturePlugin.swift in Sources */,
9D90BE252CCB9876006677DB /* CookieManager.swift in Sources */,
9D90BE262CCB9876006677DB /* CookiePlugin.swift in Sources */,
9D6A85332CCF6DA700DAB35F /* HashcashPlugin.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ class AFFiNEViewController: CAPBridgeViewController {
override func capacitorDidLoad() {
bridge?.registerPluginInstance(CookiePlugin())
bridge?.registerPluginInstance(HashcashPlugin())
bridge?.registerPluginInstance(NavigationGesturePlugin())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Foundation
import Capacitor

@objc(NavigationGesturePlugin)
public class NavigationGesturePlugin: CAPPlugin, CAPBridgedPlugin {
public let identifier = "NavigationGesturePlugin"
public let jsName = "NavigationGesture"
public let pluginMethods: [CAPPluginMethod] = [
CAPPluginMethod(name: "isEnabled", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "enable", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "disable", returnType: CAPPluginReturnPromise)
]

@objc func isEnabled(_ call: CAPPluginCall) {
let enabled = self.bridge?.webView?.allowsBackForwardNavigationGestures ?? true
call.resolve(["value": enabled])
}

@objc func enable(_ call: CAPPluginCall) {
DispatchQueue.main.sync {
self.bridge?.webView?.allowsBackForwardNavigationGestures = true
call.resolve([:])
}
}

@objc func disable(_ call: CAPPluginCall) {
DispatchQueue.main.sync {
self.bridge?.webView?.allowsBackForwardNavigationGestures = false
call.resolve([:])
}
}
}
7 changes: 7 additions & 0 deletions packages/frontend/apps/ios/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AffineContext } from '@affine/core/components/context';
import { AppContainer } from '@affine/core/desktop/components/app-container';
import { configureMobileModules } from '@affine/core/mobile/modules';
import { NavigationGestureProvider } from '@affine/core/mobile/modules/navigation-gesture';
import { VirtualKeyboardProvider } from '@affine/core/mobile/modules/virtual-keyboard';
import { router } from '@affine/core/mobile/router';
import { configureCommonModules } from '@affine/core/modules';
Expand Down Expand Up @@ -34,6 +35,7 @@ import { RouterProvider } from 'react-router-dom';
import { configureFetchProvider } from './fetch';
import { Cookie } from './plugins/cookie';
import { Hashcash } from './plugins/hashcash';
import { NavigationGesture } from './plugins/navigation-gesture';

const future = {
v7_startTransition: true,
Expand Down Expand Up @@ -86,6 +88,11 @@ framework.impl(VirtualKeyboardProvider, {
Keyboard.removeAllListeners();
},
});
framework.impl(NavigationGestureProvider, {
isEnabled: () => NavigationGesture.isEnabled(),
enable: () => NavigationGesture.enable(),
disable: () => NavigationGesture.disable(),
});
const frameworkProvider = framework.provider();

// setup application lifecycle events, and emit application start event
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface NavigationGesturePlugin {
isEnabled: () => Promise<boolean>;
enable: () => Promise<void>;
disable: () => Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { registerPlugin } from '@capacitor/core';

import type { NavigationGesturePlugin } from './definitions';

const NavigationGesture =
registerPlugin<NavigationGesturePlugin>('NavigationGesture');

export * from './definitions';
export { NavigationGesture };
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Button, Modal } from '@affine/component';
import { PageHeader } from '@affine/core/mobile/components';
import { useI18n } from '@affine/i18n';
import clsx from 'clsx';
import {
Expand All @@ -8,7 +9,6 @@ import {
type ReactNode,
} from 'react';

import { PageHeader } from '../page-header';
import * as styles from './styles.css';

interface ConfigModalProps {
Expand Down
1 change: 0 additions & 1 deletion packages/frontend/core/src/components/mobile/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from './config-modal';
export * from './page-header';
1 change: 1 addition & 0 deletions packages/frontend/core/src/mobile/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './app-tabs';
export * from './doc-card';
export * from './page-header';
export * from './rename';
export * from './search-input';
export * from './search-result';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { IconButton, SafeArea } from '@affine/component';
import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc';
import { useService } from '@toeverything/infra';
import clsx from 'clsx';
import {
forwardRef,
type HtmlHTMLAttributes,
type ReactNode,
useCallback,
useEffect,
} from 'react';

import { NavigationGestureService } from '../../modules/navigation-gesture';
import * as styles from './styles.css';

export interface PageHeaderProps
Expand Down Expand Up @@ -60,6 +63,17 @@ export const PageHeader = forwardRef<HTMLDivElement, PageHeaderProps>(
},
ref
) {
const navigationGesture = useService(NavigationGestureService);

useEffect(() => {
const prev = navigationGesture.enabled$.value;
navigationGesture.setEnabled(!!back);

return () => {
navigationGesture.setEnabled(prev);
};
}, [back, navigationGesture]);

const handleRouteBack = useCallback(() => {
backAction ? backAction() : history.back();
}, [backAction]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
Scrollable,
useThemeColorMeta,
} from '@affine/component';
import { PageHeader } from '@affine/core/components/mobile';
import { PageHeader } from '@affine/core/mobile/components';
import { useI18n } from '@affine/i18n';
import { ArrowRightSmallIcon } from '@blocksuite/icons/rc';
import { cssVarV2 } from '@toeverything/theme/v2';
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend/core/src/mobile/modules/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { Framework } from '@toeverything/infra';

import { configureMobileNavigationGestureModule } from './navigation-gesture';
import { configureMobileSearchModule } from './search';
import { configureMobileVirtualKeyboardModule } from './virtual-keyboard';

export function configureMobileModules(framework: Framework) {
configureMobileSearchModule(framework);
configureMobileVirtualKeyboardModule(framework);
configureMobileNavigationGestureModule(framework);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Framework } from '@toeverything/infra';

import { NavigationGestureProvider } from './providers/navigation-gesture';
import { NavigationGestureService } from './services/navigation-gesture';

export { NavigationGestureProvider, NavigationGestureService };

export function configureMobileNavigationGestureModule(framework: Framework) {
framework.service(
NavigationGestureService,
f => new NavigationGestureService(f.getOptional(NavigationGestureProvider))
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createIdentifier } from '@toeverything/infra';

export interface NavigationGestureProvider {
isEnabled: () => Promise<boolean>;
enable: () => Promise<void>;
disable: () => Promise<void>;
}

export const NavigationGestureProvider =
createIdentifier<NavigationGestureProvider>('NavigationGestureProvider');
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
effect,
exhaustMapWithTrailing,
fromPromise,
LiveData,
Service,
} from '@toeverything/infra';
import { catchError, distinctUntilChanged, EMPTY, mergeMap } from 'rxjs';

import type { NavigationGestureProvider } from '../providers/navigation-gesture';

export class NavigationGestureService extends Service {
public enabled$ = new LiveData(false);

constructor(
private readonly navigationGestureProvider?: NavigationGestureProvider
) {
super();
this.disable().catch(console.error);
}

setEnabled = effect(
distinctUntilChanged<boolean>(),
exhaustMapWithTrailing((enable: boolean) => {
return fromPromise(async () => {
if (!this.navigationGestureProvider) {
return;
}
if (enable) {
await this.navigationGestureProvider.enable();
} else {
await this.navigationGestureProvider.disable();
}
return;
}).pipe(
mergeMap(() => EMPTY),
catchError(err => {
console.error('navigationGestureProvider error', err);
return EMPTY;
})
);
})
);

async enable() {
this.enabled$.next(true);
return this.navigationGestureProvider?.enable();
}

async disable() {
this.enabled$.next(false);
return this.navigationGestureProvider?.disable();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { useDocMetaHelper } from '@affine/core/components/hooks/use-block-suite-
import { usePageDocumentTitle } from '@affine/core/components/hooks/use-global-state';
import { useJournalRouteHelper } from '@affine/core/components/hooks/use-journal';
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
import { PageHeader } from '@affine/core/components/mobile';
import { PageDetailEditor } from '@affine/core/components/page-detail-editor';
import { DetailPageWrapper } from '@affine/core/desktop/pages/workspace/detail-page/detail-page-wrapper';
import { PageHeader } from '@affine/core/mobile/components';
import { EditorService } from '@affine/core/modules/editor';
import { JournalService } from '@affine/core/modules/journal';
import { WorkbenchService } from '@affine/core/modules/workbench';
Expand Down Expand Up @@ -202,19 +202,22 @@ const DetailPageImpl = () => {
);
};

const skeleton = (
const getSkeleton = (back: boolean) => (
<>
<PageHeader back className={styles.header} />
<PageHeader back={back} className={styles.header} />
<PageDetailSkeleton />
</>
);

const notFound = (
const getNotFound = (back: boolean) => (
<>
<PageHeader back className={styles.header} />
<PageHeader back={back} className={styles.header} />
Page Not Found (TODO)
</>
);
const skeleton = getSkeleton(false);
const skeletonWithBack = getSkeleton(true);
const notFound = getNotFound(false);
const notFoundWithBack = getNotFound(true);

const MobileDetailPage = ({
pageId,
Expand All @@ -237,12 +240,12 @@ const MobileDetailPage = ({
return (
<div className={styles.root}>
<DetailPageWrapper
skeleton={skeleton}
notFound={notFound}
skeleton={date ? skeleton : skeletonWithBack}
notFound={date ? notFound : notFoundWithBack}
pageId={pageId}
>
<PageHeader
back
back={!date}
className={styles.header}
suffix={
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IconButton, MobileMenu } from '@affine/component';
import { EmptyCollectionDetail } from '@affine/core/components/affine/empty';
import { PageHeader } from '@affine/core/components/mobile';
import { isEmptyCollection } from '@affine/core/desktop/pages/workspace/collection';
import { PageHeader } from '@affine/core/mobile/components';
import type { Collection } from '@affine/env/filter';
import { MoreHorizontalIcon, ViewLayersIcon } from '@blocksuite/icons/rc';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IconButton, MobileMenu } from '@affine/component';
import { PageHeader } from '@affine/core/components/mobile';
import { PageHeader } from '@affine/core/mobile/components';
import type { Tag } from '@affine/core/modules/tag';
import { MoreHorizontalIcon } from '@blocksuite/icons/rc';
import { useLiveData } from '@toeverything/infra';
Expand Down

0 comments on commit 454363d

Please sign in to comment.