Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,11 @@ export class EdgelessRootBlockComponent extends BlockComponent<
}
// pan
else {
const simulateHorizontalScroll = IS_WINDOWS && e.shiftKey;
// Enable Shift+wheel horizontal scroll on Windows (no native deltaX),
// and on any platform when using a mouse without horizontal scroll
// (deltaX === 0 means no native horizontal input, e.g. external mouse on macOS/Linux).
const simulateHorizontalScroll =
e.shiftKey && (IS_WINDOWS || e.deltaX === 0);
const dx = simulateHorizontalScroll
? e.deltaY / viewport.zoom
: e.deltaX / viewport.zoom;
Expand Down
5 changes: 3 additions & 2 deletions packages/frontend/apps/ios/App/xc-universal-binary.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ fi
FFI_TARGET=${1}
# path to source code root
SRC_ROOT=${2}
# Keep Cargo artifacts in a stable location that the rest of this script can reference.
export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$SRC_ROOT/../../../target}"
# Keep Cargo artifacts in a stable repo-local location so Xcode does not inherit
# a sandbox-specific CARGO_TARGET_DIR from the parent shell.
export CARGO_TARGET_DIR="$SRC_ROOT/../../../target"
# buildvariant from our xcconfigs
BUILDVARIANT=$(echo "${3}" | tr '[:upper:]' '[:lower:]')

Expand Down
54 changes: 54 additions & 0 deletions packages/frontend/core/src/mobile/dialogs/deleted-account.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ConfirmModal } from '@affine/component';
import {
RouteLogic,
useNavigateHelper,
} from '@affine/core/components/hooks/use-navigate-helper';
import type {
DialogComponentProps,
GLOBAL_DIALOG_SCHEMA,
} from '@affine/core/modules/dialogs';
import { useI18n } from '@affine/i18n';
import { useCallback } from 'react';

export const DeletedAccountDialog = ({
close,
}: DialogComponentProps<GLOBAL_DIALOG_SCHEMA['deleted-account']>) => {
const t = useI18n();
const { jumpToIndex } = useNavigateHelper();

const handleDone = useCallback(() => {
close();
jumpToIndex(RouteLogic.REPLACE);
}, [close, jumpToIndex]);

return (
<ConfirmModal
open
persistent
title={t['com.affine.setting.account.delete.success-title']()}
description={
<>
<span>
{t['com.affine.setting.account.delete.success-description-1']()}
</span>
<br />
<br />
<span>
{t['com.affine.setting.account.delete.success-description-2']()}
</span>
</>
}
confirmText={t['Done']()}
onOpenChange={handleDone}
onConfirm={handleDone}
confirmButtonOptions={{
variant: 'primary',
}}
cancelButtonOptions={{
style: {
display: 'none',
},
}}
/>
);
};
2 changes: 2 additions & 0 deletions packages/frontend/core/src/mobile/dialogs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import type { WORKSPACE_DIALOG_SCHEMA } from '@affine/core/modules/dialogs/constant';
import { useLiveData, useService } from '@toeverything/infra';

import { DeletedAccountDialog } from './deleted-account';
import { CollectionSelectorDialog } from './selectors/collection-selector';
import { DateSelectorDialog } from './selectors/date-selector';
import { DocSelectorDialog } from './selectors/doc-selector';
Expand All @@ -16,6 +17,7 @@ import { SignInDialog } from './sign-in';

const GLOBAL_DIALOGS = {
'sign-in': SignInDialog,
'deleted-account': DeletedAccountDialog,
} satisfies {
[key in keyof GLOBAL_DIALOG_SCHEMA]?: React.FC<
DialogComponentProps<GLOBAL_DIALOG_SCHEMA[key]>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type DeviceAuthSession,
} from '@affine/core/modules/cloud';
import { useI18n } from '@affine/i18n';
import { useService } from '@toeverything/infra';
import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useEffect, useRef, useState } from 'react';

import { SettingGroup } from '../group';
Expand All @@ -15,10 +15,16 @@ const loadFailedToastId = 'mobile-settings-devices-load-failed';
export const DevicesGroup = () => {
const t = useI18n();
const auth = useService(AuthService);
const loginStatus = useLiveData(auth.session.status$);
const [sessions, setSessions] = useState<DeviceAuthSession[]>([]);
const dismissTimer = useRef<number | undefined>(undefined);

const reload = useCallback(() => {
if (loginStatus !== 'authenticated') {
setSessions([]);
return;
}

void auth
.listDeviceSessions()
.then(setSessions)
Expand All @@ -36,7 +42,7 @@ export const DevicesGroup = () => {
5000
);
});
}, [auth, t]);
}, [auth, loginStatus, t]);

useEffect(reload, [reload]);
useEffect(
Expand Down Expand Up @@ -71,6 +77,10 @@ export const DevicesGroup = () => {
[auth, reload, t]
);

if (loginStatus !== 'authenticated' || sessions.length === 0) {
return null;
}

return (
<SettingGroup title={t['com.affine.settings.devices.title']()}>
{sessions.map(session => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,17 @@ import * as styles from './styles.css';

export const ExperimentalFeatureSetting = () => {
const [open, setOpen] = useState(false);
const t = useI18n();
const title = t['com.affine.mobile.setting.experimental.features']();

return (
<>
<SettingGroup title="Experimental">
<RowLayout
label={'Experimental Features'}
onClick={() => setOpen(true)}
>
<SettingGroup title={t['com.affine.mobile.setting.experimental.title']()}>
<RowLayout label={title} onClick={() => setOpen(true)}>
<ArrowRightSmallIcon fontSize={22} />
</RowLayout>
</SettingGroup>
<SwipeDialog
open={open}
onOpenChange={setOpen}
title="Experimental Features"
>
<SwipeDialog open={open} onOpenChange={setOpen} title={title}>
<ExperimentalFeatureList />
</SwipeDialog>
</>
Expand Down
14 changes: 14 additions & 0 deletions packages/frontend/core/src/mobile/dialogs/setting/group.css.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { cssVarV2 } from '@toeverything/theme/v2';
import { style } from '@vanilla-extract/css';

export const group = style({
Expand All @@ -6,3 +7,16 @@ export const group = style({
gap: 4,
width: '100%',
});

export const groupTitle = style({
color: cssVarV2('text/tertiary'),
fontSize: 14,
lineHeight: '18px',
padding: 4,
});

export const groupContent = style({
gap: 0,
padding: 0,
overflow: 'hidden',
});
6 changes: 4 additions & 2 deletions packages/frontend/core/src/mobile/dialogs/setting/group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ export const SettingGroup = forwardRef<HTMLDivElement, SettingGroupProps>(
<ConfigModal.RowGroup
{...attrs}
ref={ref}
title={title}
title={
title ? <div className={styles.groupTitle}>{title}</div> : undefined
}
className={clsx(styles.group, className)}
contentClassName={contentClassName}
contentClassName={clsx(styles.groupContent, contentClassName)}
contentStyle={contentStyle}
>
{children}
Expand Down
142 changes: 126 additions & 16 deletions packages/frontend/core/src/mobile/dialogs/setting/index.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,159 @@
import { notify } from '@affine/component';
import { AuthService } from '@affine/core/modules/cloud';
import type {
DialogComponentProps,
WORKSPACE_DIALOG_SCHEMA,
} from '@affine/core/modules/dialogs';
import { UrlService } from '@affine/core/modules/url';
import { copyTextToClipboard } from '@affine/core/utils/clipboard';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { useEffect } from 'react';
import { useCallback, useEffect } from 'react';

import { AboutGroup } from './about';
import { AppearanceGroup } from './appearance';
import teamPeople from './assets/team-people.png';
import { DevicesGroup } from './devices';
import { ExperimentalFeatureSetting } from './experimental';
import { SettingGroup } from './group';
import { OthersGroup } from './others';
import { DeleteAccount } from './others/delete-account';
import { RowLayout } from './row.layout';
import * as styles from './style.css';
import { UserSubscription } from './subscription';
import { SwipeDialog } from './swipe-dialog';
import { UserProfile } from './user-profile';
import { UserUsage } from './user-usage';

const MobileSetting = () => {
const AFFINE_MOBILE_STORE_URL = BUILD_CONFIG.isIOS
? 'https://apps.apple.com/app/notes-whiteboard-ai-affine/id6736937980'
: BUILD_CONFIG.isAndroid
? 'https://play.google.com/store/apps/details?id=app.affine.pro'
: undefined;
const AFFINE_DOWNLOAD_URL = 'https://affine.pro/download';
const AFFINE_TEAM_URL = 'https://affine.pro/teamhub';

const SupportGroup = () => {
const t = useI18n();
const urlService = useService(UrlService);

const shareApp = useCallback(async () => {
const shareData = {
title: 'AFFiNE',
text: t['com.affine.mobile.setting.support.invite-message'](),
url: AFFINE_DOWNLOAD_URL,
};

if ('share' in navigator && typeof navigator.share === 'function') {
try {
await navigator.share(shareData);
return;
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
return;
}
}
}

const copied = await copyTextToClipboard(AFFINE_DOWNLOAD_URL);
if (copied) {
notify.success({ title: t['Copied link to clipboard']() });
return;
}

urlService.openExternal(AFFINE_DOWNLOAD_URL);
}, [t, urlService]);

return (
<SettingGroup title={t['com.affine.mobile.setting.support.title']()}>
{AFFINE_MOBILE_STORE_URL ? (
<RowLayout
label={t['com.affine.mobile.setting.support.rate']()}
onClick={() => urlService.openExternal(AFFINE_MOBILE_STORE_URL)}
/>
) : null}
<RowLayout
label={t['com.affine.mobile.setting.support.invite']()}
onClick={() => void shareApp()}
/>
</SettingGroup>
);
};

const TeamPromotionCard = () => {
const t = useI18n();
const urlService = useService(UrlService);

return (
<button
type="button"
className={styles.promoCard}
onClick={() => urlService.openExternal(AFFINE_TEAM_URL)}
>
<span className={styles.promoCardContent}>
<span className={styles.promoCardTitle}>
{t['com.affine.mobile.setting.promo.title']()}
</span>
<span className={styles.promoCardDescription}>
{t['com.affine.mobile.setting.promo.description']()}
</span>
</span>
<img className={styles.promoCardArt} src={teamPeople} alt="" />
</button>
);
};

const DangerZoneGroup = ({
onDeleteFinished,
}: {
onDeleteFinished?: () => void;
}) => {
const t = useI18n();
const authService = useService(AuthService);
const account = useLiveData(authService.session.account$);

if (!account) {
return null;
}

return (
<SettingGroup
title={
<span className={styles.dangerZoneTitle}>
{t['com.affine.mobile.setting.danger-zone.title']()}
</span>
}
>
<DeleteAccount onDeleteFinished={onDeleteFinished} />
</SettingGroup>
);
};

const MobileSetting = ({
onDeleteFinished,
}: {
onDeleteFinished?: () => void;
}) => {
const session = useService(AuthService).session;
const status = useLiveData(session.status$);
useEffect(() => session.revalidate(), [session]);

useEffect(() => {
session.revalidate();
}, [session]);

return (
<div className={styles.root}>
<UserProfile />
<UserSubscription />
<UserProfile />
<UserUsage />
{status === 'authenticated' ? <DevicesGroup /> : null}
<AppearanceGroup />
<AboutGroup />
<ExperimentalFeatureSetting />
<TeamPromotionCard />
<SupportGroup />
<OthersGroup />
<DangerZoneGroup onDeleteFinished={onDeleteFinished} />
</div>
);
};
Expand All @@ -48,18 +169,7 @@ export const SettingDialog = ({
open
onOpenChange={() => close()}
>
<MobileSetting />
<MobileSetting onDeleteFinished={close} />
</SwipeDialog>
);

// return (
// <ConfigModal
// title={t['com.affine.mobile.setting.header-title']()}
// open
// onOpenChange={() => close()}
// onBack={close}
// >
// <MobileSetting />
// </ConfigModal>
// );
};
Loading
Loading