mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(desktop): make starter channels best-effort for community joiners
Joining an established community failed onboarding with 'starter channels created but metadata not yet available' whenever the community's starter channels (general / welcome-everyone) no longer matched the pristine spec — renamed, made private (their kind:39000 metadata is invisible to a non-member joiner), archived, or converted to a forum. Every new member hit the error deterministically on the Meet-your-starter-team screen. Starter-channel setup is a real requirement only for the community founder creating them; for a joiner, a customized starter set is normal community evolution, not a failure. Downgrade both layers to best-effort: - ensure_starter_channels (Rust) logs and proceeds instead of erroring when the full starter set is not observable; membership joins still happen for whatever starter channels do match. - ensureStarterChannels (TS) returns nullable generalChannel/welcomeChannel instead of throwing when the ensured list lacks a full match. No callers consumed those fields outside tests. The private Welcome channel flow is unchanged and was already independent of this failure. Co-authored-by: Thomas Petersen <thomasp@squareup.com> Signed-off-by: Thomas Petersen <thomasp@squareup.com>
This commit is contained in:
@@ -674,8 +674,21 @@ pub async fn ensure_starter_channels(
|
||||
existing_channels = get_channels(state.clone()).await?;
|
||||
}
|
||||
|
||||
// Best-effort from here: a spec that still has no match is normal
|
||||
// community evolution, not a failure. In an established community the
|
||||
// starter channels may have been renamed, made private (their kind:39000
|
||||
// is then invisible to a non-member joiner), archived, or converted to a
|
||||
// forum — none of which should block a new member's onboarding.
|
||||
// The freshly-created case needs no rescue either: the relay grants the
|
||||
// creator owner membership at create time, so a slow kind:39000 only
|
||||
// delays sidebar visibility. `ensure_starter_channel_memberships` skips
|
||||
// unmatched specs, joining only the starter channels that are actually
|
||||
// observable.
|
||||
if !has_all_starter_channels(&existing_channels) {
|
||||
return Err("starter channels created but metadata not yet available".to_string());
|
||||
eprintln!(
|
||||
"buzz-desktop: ensure_starter_channels: proceeding without full starter set \
|
||||
(metadata missing or channels customized)"
|
||||
);
|
||||
}
|
||||
|
||||
ensure_starter_channel_memberships(&state, &creator_keys, &mut existing_channels).await?;
|
||||
|
||||
@@ -332,6 +332,31 @@ test("ensureStarterChannels resumes when one starter channel is missing", async
|
||||
assert.equal(ensureCalls, 1);
|
||||
});
|
||||
|
||||
test("ensureStarterChannels succeeds without a full starter set (customized community)", async () => {
|
||||
// An established community renamed/privatized its starter channels: the
|
||||
// ensure command returns whatever this member can see, and a partial (or
|
||||
// empty) starter match must not throw — joiner onboarding is best-effort.
|
||||
const renamedGeneral = makeChannel({
|
||||
id: "general-channel",
|
||||
name: "town-square",
|
||||
visibility: "open",
|
||||
});
|
||||
let ensureCalls = 0;
|
||||
|
||||
const result = await ensureStarterChannels({
|
||||
getChannels: async () => [renamedGeneral],
|
||||
ensureStarterChannels: async () => {
|
||||
ensureCalls += 1;
|
||||
return [renamedGeneral];
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.generalChannel, null);
|
||||
assert.equal(result.welcomeChannel, null);
|
||||
assert.deepEqual(result.channels, [renamedGeneral]);
|
||||
assert.equal(ensureCalls, 1);
|
||||
});
|
||||
|
||||
test("isWelcomeExperienceChannel matches legacy Welcome and starter welcome-everyone", () => {
|
||||
assert.equal(isWelcomeExperienceChannel(makeChannel()), true);
|
||||
assert.equal(
|
||||
|
||||
@@ -41,8 +41,13 @@ type StarterChannelsClient = {
|
||||
|
||||
export type StarterChannelsResult = {
|
||||
channels: Channel[];
|
||||
generalChannel: Channel;
|
||||
welcomeChannel: Channel;
|
||||
/**
|
||||
* Null when the community's starter channel no longer matches the spec
|
||||
* (renamed, private, archived, or forum). Starter channels are best-effort
|
||||
* for joiners of established communities — a missing match is not an error.
|
||||
*/
|
||||
generalChannel: Channel | null;
|
||||
welcomeChannel: Channel | null;
|
||||
};
|
||||
|
||||
type WelcomeChannelOptions = {
|
||||
@@ -86,21 +91,17 @@ function findStarterChannel(channels: Channel[], name: string) {
|
||||
|
||||
export function findStarterChannels(
|
||||
channels: Channel[],
|
||||
): Omit<StarterChannelsResult, "channels"> | null {
|
||||
const generalChannel = findStarterChannel(
|
||||
channels,
|
||||
STARTER_GENERAL_CHANNEL_NAME,
|
||||
);
|
||||
const welcomeChannel = findStarterChannel(
|
||||
channels,
|
||||
STARTER_WELCOME_CHANNEL_NAME,
|
||||
);
|
||||
): Omit<StarterChannelsResult, "channels"> {
|
||||
return {
|
||||
generalChannel: findStarterChannel(channels, STARTER_GENERAL_CHANNEL_NAME),
|
||||
welcomeChannel: findStarterChannel(channels, STARTER_WELCOME_CHANNEL_NAME),
|
||||
};
|
||||
}
|
||||
|
||||
if (!generalChannel || !welcomeChannel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { generalChannel, welcomeChannel };
|
||||
function hasAllStarterChannels(
|
||||
starters: Omit<StarterChannelsResult, "channels">,
|
||||
) {
|
||||
return starters.generalChannel !== null && starters.welcomeChannel !== null;
|
||||
}
|
||||
|
||||
function hasOnlyCurrentOrAllowedMembers(
|
||||
@@ -262,22 +263,22 @@ export async function ensureStarterChannels(
|
||||
): Promise<StarterChannelsResult> {
|
||||
const existingChannels = await client.getChannels();
|
||||
const existingStarters = findStarterChannels(existingChannels);
|
||||
if (existingStarters) {
|
||||
if (hasAllStarterChannels(existingStarters)) {
|
||||
return {
|
||||
channels: existingChannels,
|
||||
...existingStarters,
|
||||
};
|
||||
}
|
||||
|
||||
// Best-effort: in an established community the starter channels may have
|
||||
// been renamed, made private, archived, or converted — the ensure command
|
||||
// creates whatever is genuinely missing and returns what this member can
|
||||
// see. A partial (or empty) starter set is fine; it must not fail a
|
||||
// joiner's onboarding.
|
||||
const ensuredChannels = await client.ensureStarterChannels();
|
||||
const ensuredStarters = findStarterChannels(ensuredChannels);
|
||||
if (!ensuredStarters) {
|
||||
throw new Error("Starter channels were not available after setup");
|
||||
}
|
||||
|
||||
return {
|
||||
channels: ensuredChannels,
|
||||
...ensuredStarters,
|
||||
...findStarterChannels(ensuredChannels),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user