mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-24 13:58:50 +08:00
feat: improve native preview (#15223)
#### PR Dependency Tree * **PR #15223** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Updated Mermaid and Typst preview rendering in native/mobile apps to use a shared preview implementation. * Improved production Release builds with additional Rust release tuning and enhanced iOS Release stripping/symbol settings. * Enabled stronger release output optimization for the native package build. * **Bug Fixes** * Strengthened handling of unsupported self-hosted server versions during login preflight. * Refreshed unsupported-version messaging with localized text and a direct upgrade link. * **Tests** * Added coverage for login preflight behavior when the server version is unsupported. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
+22
-21
@@ -1,5 +1,6 @@
|
||||
import type { Server } from '@affine/core/modules/cloud';
|
||||
import { MIN_SUPPORTED_SERVER_VERSION } from '@affine/core/modules/cloud/stores/server-config';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { useLiveData } from '@toeverything/infra';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import semver from 'semver';
|
||||
@@ -7,7 +8,7 @@ import semver from 'semver';
|
||||
const rules = [
|
||||
{
|
||||
min: MIN_SUPPORTED_SERVER_VERSION,
|
||||
tip: (receivedVersion: string, requiredVersion: string) => (
|
||||
tip: (message: string) => (
|
||||
<div>
|
||||
<p
|
||||
style={{
|
||||
@@ -16,26 +17,21 @@ const rules = [
|
||||
lineHeight: '22px',
|
||||
}}
|
||||
>
|
||||
Your server version{' '}
|
||||
<b style={{ fontWeight: 600 }}>{receivedVersion}</b> is not compatible
|
||||
with current client. Please upgrade your server to{' '}
|
||||
<b style={{ fontWeight: 600 }}>{requiredVersion}</b> or higher to use
|
||||
this client.
|
||||
{message}
|
||||
</p>
|
||||
<div style={{ marginTop: '12px', color: cssVarV2.text.primary }}>
|
||||
<span style={{ fontWeight: 500 }}>Instructions:</span>
|
||||
<br />
|
||||
<a
|
||||
style={{
|
||||
whiteSpace: 'break-spaces',
|
||||
wordBreak: 'break-all',
|
||||
fontSize: 12,
|
||||
lineHeight: '16px',
|
||||
}}
|
||||
>
|
||||
https://docs.affine.pro/self-host-affine/install/upgrade
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
href="https://docs.affine.pro/self-host-affine/install/upgrade"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
style={{
|
||||
color: cssVarV2.text.primary,
|
||||
wordBreak: 'break-all',
|
||||
fontSize: 12,
|
||||
lineHeight: '16px',
|
||||
}}
|
||||
>
|
||||
https://docs.affine.pro/self-host-affine/install/upgrade
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -45,12 +41,17 @@ const rules = [
|
||||
* Return the error tip if the server version is not meet the requirement
|
||||
*/
|
||||
export const useSelfhostLoginVersionGuard = (server: Server) => {
|
||||
const t = useI18n();
|
||||
const serverVersion =
|
||||
useLiveData(server.config$.selector(c => c.version)) ?? '0.0.0';
|
||||
|
||||
for (const rule of rules) {
|
||||
if (semver.lt(serverVersion, rule.min)) {
|
||||
return rule.tip(serverVersion, rule.min);
|
||||
return rule.tip(
|
||||
t['error.UNSUPPORTED_SERVER_VERSION']({
|
||||
requiredVersion: `>=${rule.min}`,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,10 @@ function createStore({
|
||||
framework.service(GraphQLService, {} as any);
|
||||
framework.impl(GlobalState, {} as any);
|
||||
framework.service(ServerService, {
|
||||
server: { id: 'test-server' },
|
||||
server: {
|
||||
id: 'test-server',
|
||||
['config$']: { value: { version: '0.27.0' } },
|
||||
},
|
||||
} as any);
|
||||
framework.impl(AuthProvider, {} as any);
|
||||
framework.service(NbstoreService, {
|
||||
@@ -37,6 +40,36 @@ function createStore({
|
||||
}
|
||||
|
||||
describe('AuthStore', () => {
|
||||
test('validates login preflight responses', async () => {
|
||||
const validResponse = {
|
||||
registered: true,
|
||||
methods: {
|
||||
password: { available: true },
|
||||
magicLink: { available: true },
|
||||
oauth: { available: false, providers: [] },
|
||||
passkey: { available: false, discoverable: false },
|
||||
},
|
||||
};
|
||||
const fetch = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ ok: true, json: async () => validResponse })
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ registered: true, methods: {} }),
|
||||
});
|
||||
const store = createStore({ fetch, request: vi.fn() });
|
||||
|
||||
await expect(store.checkUserByEmail('user@affine.pro')).resolves.toEqual(
|
||||
validResponse
|
||||
);
|
||||
await expect(
|
||||
store.checkUserByEmail('user@affine.pro')
|
||||
).rejects.toMatchObject({
|
||||
name: 'UNSUPPORTED_SERVER_VERSION',
|
||||
data: { serverVersion: '0.27.0' },
|
||||
});
|
||||
});
|
||||
|
||||
test('loads account profile from realtime after auth session bootstrap', async () => {
|
||||
const authMethods = {
|
||||
password: { bound: true },
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from '@affine/graphql';
|
||||
import type { CurrentUserProfileSnapshot } from '@affine/realtime';
|
||||
import { Store } from '@toeverything/infra';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { GlobalState, NbstoreService } from '../../storage';
|
||||
import type { AuthSessionInfo } from '../entities/session';
|
||||
@@ -14,6 +15,23 @@ import type { AuthProvider, SignInUserInfo } from '../provider/auth';
|
||||
import type { FetchService } from '../services/fetch';
|
||||
import type { GraphQLService } from '../services/graphql';
|
||||
import type { ServerService } from '../services/server';
|
||||
import { createUnsupportedServerVersionError } from './server-config';
|
||||
|
||||
const AuthPreflightResponseSchema = z.object({
|
||||
registered: z.boolean(),
|
||||
methods: z.object({
|
||||
password: z.object({ available: z.boolean() }),
|
||||
magicLink: z.object({ available: z.boolean() }),
|
||||
oauth: z.object({
|
||||
available: z.boolean(),
|
||||
providers: z.array(z.string()),
|
||||
}),
|
||||
passkey: z.object({
|
||||
available: z.boolean(),
|
||||
discoverable: z.boolean(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export interface AccountProfile extends CurrentUserProfileSnapshot {
|
||||
authMethods?: {
|
||||
@@ -201,17 +219,14 @@ export class AuthStore extends Store {
|
||||
throw new Error(`Failed to check user by email: ${email}`);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as {
|
||||
registered: boolean;
|
||||
methods: {
|
||||
password: { available: boolean };
|
||||
magicLink: { available: boolean };
|
||||
oauth: { available: boolean; providers: string[] };
|
||||
passkey: { available: boolean; discoverable: boolean };
|
||||
};
|
||||
};
|
||||
const data = AuthPreflightResponseSchema.safeParse(await res.json());
|
||||
if (!data.success) {
|
||||
throw createUnsupportedServerVersionError(
|
||||
this.serverService.server.config$.value.version
|
||||
);
|
||||
}
|
||||
|
||||
return data;
|
||||
return data.data;
|
||||
}
|
||||
|
||||
async deleteAccount() {
|
||||
|
||||
Reference in New Issue
Block a user