Skip to content

Comments

Add loader if channel is being published and community library side p…#5523

Merged
AlexVelezLl merged 5 commits intolearningequality:unstablefrom
taoerman:issue-5452-add-loader
Nov 12, 2025
Merged

Add loader if channel is being published and community library side p…#5523
AlexVelezLl merged 5 commits intolearningequality:unstablefrom
taoerman:issue-5452-add-loader

Conversation

@taoerman
Copy link
Member

Summary

  1. made the submit panel publishing-aware: it shows a centered “Channel is being published” loader, disables form and submit while publishing, then re-enables after polling.
  2. Backend now blocks submissions if the channel is publishing (via main_tree.publishing).
  3. Added unit tests for both frontend behavior and backend validation.

References

Fixed #5452

Reviewer guidance

run frontend and backend unit tests,
test the frontend manually.

Comment on lines +57 to +61
# Prevent creating submissions while a publish is in progress
if getattr(getattr(channel, "main_tree", None), "publishing", False):
raise ValidationError(
"Cannot create a community library submission while the channel is being published."
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

Comment on lines 255 to 269
let publishPollId = null;
function startPublishPolling() {
if (publishPollId || !isPublishing.value) return;
publishPollId = setInterval(() => {
Channel.fetchModel(props.channel.id).then(ch => {
if (ch) {
isPublishing.value = Boolean(ch.publishing);
if (!isPublishing.value) {
currentChannelVersion.value = ch.version;
clearInterval(publishPollId);
publishPollId = null;
}
}
});
}, 2000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I think here we can use the liveQuery method instead? Just as you did for the draft version publishing! https://github.com/taoerman/studio/blob/781d95378f7f20dd1dc1cf2f4f10bdf15b0457a2/contentcuration/contentcuration/frontend/shared/data/resources.js#L1275

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

Copy link
Member

@AlexVelezLl AlexVelezLl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for me leading us in the wrong direction 😅. It seems there is an easier way to monitor the publishing property without a liveQuery :)

Comment on lines 281 to 343
const isPublishing = ref(Boolean(props.channel.publishing));
const currentChannelVersion = ref(props.channel.version);
const replacementConfirmed = ref(false);

// Monitor publishing completion using liveQuery to watch channel's publishing property
let publishSubscription = null;
let publishTimeout = null;

function startPublishMonitoring() {
if (publishSubscription || !isPublishing.value) return;

if (!Channel.table || !Channel.table.get) return;

const observable = liveQuery(() => {
return Channel.table.get(props.channel.id);
});

publishSubscription = observable.subscribe({
next: async channel => {
if (channel && !channel.publishing) {
stopPublishMonitoring();
isPublishing.value = false;
currentChannelVersion.value = channel.version;
await store.dispatch('channel/loadChannel', props.channel.id);
}
},
});

publishTimeout = setTimeout(() => {
stopPublishMonitoring();
}, 300000);
}

function stopPublishMonitoring() {
if (publishSubscription) {
publishSubscription.unsubscribe();
publishSubscription = null;
}
if (publishTimeout) {
clearTimeout(publishTimeout);
publishTimeout = null;
}
}

if (isPublishing.value) {
startPublishMonitoring();
}

watch(
() => currentChannel.value?.publishing,
(newPublishing, oldPublishing) => {
isPublishing.value = Boolean(newPublishing);
if (newPublishing && !oldPublishing) {
startPublishMonitoring();
} else if (!newPublishing && oldPublishing) {
stopPublishMonitoring();
}
},
);

onUnmounted(() => {
stopPublishMonitoring();
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A hundred apologies, I did not catch this earlier... 🥲, but.... since the props.channel prop is already reactive, then a simply computed property would automatically watch any changes on the publishing property:

const isPublishing = computed(() => props.channel?.publishing === true);

And we wouldn't need to set up the monitoring machinery.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, Thanks!

Comment on lines 454 to 463
() => currentChannel.value?.publishing,
async (isPublishing, wasPublishing) => {
if (wasPublishing === true && isPublishing === false && props.channel.id) {
await Channel.fetchModel(props.channel.id);
const ch = await Channel.fetchModel(props.channel.id);
if (ch) {
currentChannelVersion.value = ch.version;
}
await store.dispatch('channel/loadChannel', props.channel.id);
}
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we can rely on the reactiveness of the props.channel, we can get rid of this watcher too!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed it, Thanks!

Comment on lines 517 to 520
if (isPublishing.value) {
showSnackbar({ text: submittingSnackbar$() });
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, this should never get here, right? And even if it does, I don't think it'd be appropriate to show a "submitting" snackbar. Was there any particular reason for this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read the code again and tested the workflow manually and it would not get there. Removed, Thanks.

const countries = ref([]);
const description = ref('');
const isPublishing = ref(Boolean(props.channel.publishing));
const currentChannelVersion = ref(props.channel.version);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the definition of this currentChannelVersion can also be replaced with a computed property: const currentChannelVersion = computed(() => props.channel.version);.

I tried it, and... it seems there is an error with the reactiveness of the version, as the version never gets updated!

However, this is not a problem with the reactiveness of the props.channel property, but with how we manage the changes in Studio. The reason why the version of props.channel doesn't change is that in the backend, when we create the change that informs the publish task has finished, we don't specify the new version in that change, and therefore, when changes are synced to the frontend, we don't update the channel version property. Therefore, the fix for this would be to include the new version of the channel in the channel update event after publishing a new version here! After this, we shouldn't need to reload the channel from the backend to get the proper updates of the version 😄.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Alex! LOL, I used const isPublishing = ref(Boolean(props.channel.publishing)); const currentChannelVersion = ref(props.channel.version); They worked on my laptop, but they needed some time to load. However, your idea is looks more reasonable! I will fix the code according to your idea.

if (!publishedData.value) return undefined;

return publishedData.value[currentChannel.value?.version];
return publishedData.value[currentChannelVersion.value];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note that we may have a problem with this publishedData not being reloaded when the channel is published. Would you like to take a look and come up with a way to keep this updated smoothly? We can open another issue for this if that makes more sense 😄.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, Thanks!

@@ -376,7 +454,10 @@
() => currentChannel.value?.publishing,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not flag this earlier because I wanted to have the complete picture regarding this currentChannel prop with this PR. And now that it seems that props.channel is indeed reactive, then I think we can just always use props.channel, and avoid the definition of this currentChannel here?

The pros of using props.channel instead of currentChannel are that we make this side panel less coupled to the channel edit context. And if, for some reason, we want to show it in another place, it will be easier to implement.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed Thanks!

Copy link
Member

@AlexVelezLl AlexVelezLl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @taoerman! Just one little requirement before merging!

Comment on lines 413 to 418
// Watch for version changes and refetch publishedData
watch(currentChannelVersion, (newVersion, oldVersion) => {
if (newVersion && newVersion !== oldVersion) {
fetchPublishedData();
}
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove this and rely on the isPublishing watcher. If we keep both, we may end up making the same fetch twice, consuming time and resources unnecessarily.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Alex, I checked the code and found it truly redundant and duplicate. I've removed this watch.

Copy link
Member

@AlexVelezLl AlexVelezLl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @taoerman!!

@AlexVelezLl AlexVelezLl merged commit 9932b7d into learningequality:unstable Nov 12, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add loader if channel is being published and community library side panel is open.

2 participants