feat(web)!: sign in before joining a space and drop the stranger option
Opening a space link while signed out shows the sign-in page first and keeps the link in memory. The channel settings no longer offer the stranger switch, and the client supports API contract major 8.
This commit is contained in:
1 parent
c858bd2a09
commit
8233369cd4
16 files changed
+27
-30
No files matched your search
@@ -3,9 +3,9 @@ import { supportsApiContract } from './contract';
|
||||
|
||||
describe('API contract compatibility', () => {
|
||||
it('accepts compatible SemVer releases in the supported major', () => {
|
||||
expect(supportsApiContract('7.0.0')).toBe(true);
|
||||
expect(supportsApiContract('7.8.2')).toBe(true);
|
||||
expect(supportsApiContract('7.2.0-rc.1')).toBe(true);
|
||||
expect(supportsApiContract('8.0.0')).toBe(true);
|
||||
expect(supportsApiContract('8.8.2')).toBe(true);
|
||||
expect(supportsApiContract('8.2.0-rc.1')).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects a breaking major and malformed versions', () => {
|
||||
@@ -13,6 +13,7 @@ describe('API contract compatibility', () => {
|
||||
expect(supportsApiContract('4.0.0')).toBe(false);
|
||||
expect(supportsApiContract('5.0.0')).toBe(false);
|
||||
expect(supportsApiContract('6.1.0')).toBe(false);
|
||||
expect(supportsApiContract('7.0.0')).toBe(false);
|
||||
expect(supportsApiContract('01.0.0')).toBe(false);
|
||||
expect(supportsApiContract(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const SUPPORTED_API_CONTRACT_MAJOR = 7;
|
||||
export const SUPPORTED_API_CONTRACT_MAJOR = 8;
|
||||
|
||||
const semverPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@ import type { CreateOptions } from './types';
|
||||
export function creationOptions(defaults: DefaultsBody): CreateOptions {
|
||||
const settings = defaults.channel;
|
||||
return { mode: defaults.channel_mode, maxMembers: settings.max_members, ttlSeconds: settings.ttl_seconds, membersCanUpload: settings.members_can_upload,
|
||||
fileMaxDownloads: settings.file_max_downloads, joinPolicy: settings.join_policy, allowGuests: settings.allow_guests };
|
||||
fileMaxDownloads: settings.file_max_downloads, joinPolicy: settings.join_policy };
|
||||
}
|
||||
@@ -8,7 +8,6 @@ export interface ChannelInfo {
|
||||
dataSource?: 'server' | 'cache';
|
||||
creatorAccount?: string;
|
||||
joinPolicy?: 'open' | 'invite';
|
||||
allowGuests?: boolean;
|
||||
passwordRequired?: boolean;
|
||||
id: string;
|
||||
/** 服务端 channel settings 中的共享名称,加入者可见。 */
|
||||
@@ -33,7 +32,6 @@ export interface ChannelInfo {
|
||||
/** 新建空间的选项(protocol §12 设置的子集) */
|
||||
export interface CreateOptions {
|
||||
joinPolicy?: 'open' | 'invite';
|
||||
allowGuests?: boolean;
|
||||
joinPassword?: string;
|
||||
name?: string;
|
||||
mode: ChannelMode;
|
||||
@@ -51,7 +49,6 @@ export interface FileUploadOptions {
|
||||
|
||||
export interface ChannelSettingsPatch {
|
||||
joinPolicy?: 'open' | 'invite';
|
||||
allowGuests?: boolean;
|
||||
joinPassword?: string;
|
||||
name?: string;
|
||||
maxMembers?: number;
|
||||
|
||||
@@ -34,7 +34,7 @@ export async function createChannel(options: CreateOptions, captchaToken?: strin
|
||||
ttl_seconds: options.ttlSeconds,
|
||||
members_can_upload: options.membersCanUpload,
|
||||
file_max_downloads: options.fileMaxDownloads,
|
||||
join_policy: options.joinPolicy ?? defaults.join_policy, allow_guests: options.allowGuests ?? defaults.allow_guests,
|
||||
join_policy: options.joinPolicy ?? defaults.join_policy,
|
||||
},
|
||||
});
|
||||
try {
|
||||
@@ -157,7 +157,6 @@ export async function updateChannelSettings(resourceId: string, patch: ChannelSe
|
||||
if (patch.ttlSeconds !== undefined) body.ttl_seconds = patch.ttlSeconds;
|
||||
if (patch.fileMaxDownloads !== undefined) body.file_max_downloads = patch.fileMaxDownloads;
|
||||
if (patch.joinPolicy !== undefined) body.join_policy = patch.joinPolicy;
|
||||
if (patch.allowGuests !== undefined) body.allow_guests = patch.allowGuests;
|
||||
if (patch.joinPassword !== undefined) body.join_password = patch.joinPassword;
|
||||
const info = await apiFetch(`/channels/${resourceId}/settings`, {
|
||||
method: 'PATCH', headers: { ...session.authHeaders('owner'), 'Content-Type': 'application/json' },
|
||||
|
||||
@@ -8,7 +8,7 @@ describe('API contract preflight', () => {
|
||||
});
|
||||
|
||||
it('blocks API requests when the server has a newer breaking major', async () => {
|
||||
const fetchMock = vi.fn(async () => new Response(JSON.stringify({ contract_version: '8.0.0' }), { status: 200 }));
|
||||
const fetchMock = vi.fn(async () => new Response(JSON.stringify({ contract_version: '9.0.0' }), { status: 200 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
setApiBase('http://api.test/api/v1');
|
||||
|
||||
@@ -19,7 +19,7 @@ describe('API contract preflight', () => {
|
||||
|
||||
it('checks the contract once and then sends the API request', async () => {
|
||||
const fetchMock = vi.fn()
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify({ contract_version: '7.0.0' }), { status: 200 }))
|
||||
.mockResolvedValueOnce(new Response(JSON.stringify({ contract_version: '8.0.0' }), { status: 200 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 200 }))
|
||||
.mockResolvedValueOnce(new Response('{}', { status: 200 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
@@ -8,7 +8,7 @@ export function toChannelInfo(raw: ChannelWire, saved: SavedResource): ChannelIn
|
||||
return {
|
||||
id: raw.id,
|
||||
creatorAccount: raw.creator_account ?? undefined,
|
||||
joinPolicy, allowGuests: raw.settings.allow_guests, passwordRequired: raw.password_required,
|
||||
joinPolicy, passwordRequired: raw.password_required,
|
||||
name: raw.settings.name ?? undefined,
|
||||
mode: raw.mode,
|
||||
role: raw.can_manage_structure ? 'owner' : 'member',
|
||||
|
||||
Reference in new issue
Block a user