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
51 changes: 28 additions & 23 deletions ceres/src/application/api_service/mono/cl/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use jupiter::model::cl_dto::CLDetails;
use crate::{
application::{
api_service::mono::ClApplicationService,
member_identity::display_label_for_actor,
webhook::{WebhookEvent, dispatch_cl_webhook},
},
model::change_list::{CLDetailRes, Condition, MergeBoxRes, UpdateClStatusPayload},
Expand Down Expand Up @@ -66,7 +67,7 @@ impl ClApplicationService {
Ok(detail)
}

pub async fn reopen_cl(&self, link: &str, username: &str) -> Result<(), MegaError> {
pub async fn reopen_cl(&self, link: &str, actor: &str) -> Result<(), MegaError> {
let cl_storage = self.storage().cl_service.cl_store();
let model = cl_storage
.get_cl(link)
Expand All @@ -79,13 +80,14 @@ impl ClApplicationService {

let link = model.link.clone();
cl_storage.reopen_cl(model.clone()).await?;
let display = display_label_for_actor(self.storage(), actor).await;
self.storage()
.cl_service
.conversation_store()
.add_conversation(
&link,
username,
Some(format!("{username} reopen this")),
actor,
Some(format!("{display} reopen this")),
ConvTypeEnum::Reopen,
)
.await?;
Expand All @@ -96,7 +98,7 @@ impl ClApplicationService {
Ok(())
}

pub async fn close_cl(&self, link: &str, username: &str) -> Result<(), MegaError> {
pub async fn close_cl(&self, link: &str, actor: &str) -> Result<(), MegaError> {
let cl_storage = self.storage().cl_service.cl_store();
let model = cl_storage
.get_cl(link)
Expand All @@ -109,13 +111,14 @@ impl ClApplicationService {

let link = model.link.clone();
cl_storage.close_cl(model.clone()).await?;
let display = display_label_for_actor(self.storage(), actor).await;
self.storage()
.cl_service
.conversation_store()
.add_conversation(
&link,
username,
Some(format!("{username} closed this")),
actor,
Some(format!("{display} closed this")),
ConvTypeEnum::Closed,
)
.await?;
Expand All @@ -126,7 +129,7 @@ impl ClApplicationService {
Ok(())
}

pub async fn merge_open_cl(&self, username: &str, link: &str) -> Result<(), MegaError> {
pub async fn merge_open_cl(&self, actor: &str, link: &str) -> Result<(), MegaError> {
let cl_storage = self.storage().cl_service.cl_store();
let model = cl_storage
.get_cl(link)
Expand All @@ -138,7 +141,7 @@ impl ClApplicationService {
}

if model.status == MergeStatusEnum::Open {
self.merge_cl(username, model.clone()).await?;
self.merge_cl(actor, model.clone()).await?;
if let Some(updated_model) = cl_storage.get_cl(link).await? {
dispatch_cl_webhook(self.storage(), WebhookEvent::ClMerged, &updated_model);
}
Expand Down Expand Up @@ -199,13 +202,13 @@ impl ClApplicationService {
pub async fn save_cl_comment(
&self,
link: &str,
username: &str,
actor: &str,
content: &str,
) -> Result<(), MegaError> {
let conv_type = if self
.storage()
.reviewer_storage()
.is_reviewer(link, username)
.is_reviewer(link, actor)
.await?
{
ConvTypeEnum::Review
Expand All @@ -216,10 +219,10 @@ impl ClApplicationService {
self.storage()
.cl_service
.conversation_store()
.add_conversation(link, username, Some(content.to_string()), conv_type)
.add_conversation(link, actor, Some(content.to_string()), conv_type)
.await?;

if let Err(e) = enqueue_cl_comment_notifications(self, username, link, content).await {
if let Err(e) = enqueue_cl_comment_notifications(self, actor, link, content).await {
tracing::warn!("failed to enqueue cl comment notifications: {e}");
}

Expand All @@ -244,7 +247,7 @@ impl ClApplicationService {
pub async fn update_cl_status(
&self,
link: &str,
username: &str,
actor: &str,
payload: &UpdateClStatusPayload,
) -> Result<(), MegaError> {
let cl_storage = self.storage().cl_service.cl_store();
Expand All @@ -268,13 +271,14 @@ impl ClApplicationService {
cl_storage
.update_cl_status(model.clone(), new_status.clone())
.await?;
let display = display_label_for_actor(self.storage(), actor).await;
self.storage()
.cl_service
.conversation_store()
.add_conversation(
link,
username,
Some(format!("{username} marked this as ready for review")),
actor,
Some(format!("{display} marked this as ready for review")),
ConvTypeEnum::Review,
)
.await?;
Expand All @@ -286,13 +290,14 @@ impl ClApplicationService {
cl_storage
.update_cl_status(model.clone(), new_status.clone())
.await?;
let display = display_label_for_actor(self.storage(), actor).await;
self.storage()
.cl_service
.conversation_store()
.add_conversation(
link,
username,
Some(format!("{username} marked this as draft")),
actor,
Some(format!("{display} marked this as draft")),
ConvTypeEnum::Draft,
)
.await?;
Expand All @@ -311,10 +316,10 @@ impl ClApplicationService {

pub async fn update_branch_with_webhook(
&self,
username: &str,
actor: &str,
link: &str,
) -> Result<String, MegaError> {
let new_head = self.update_branch(username, link).await?;
let new_head = self.update_branch(actor, link).await?;
if let Some(cl_model) = self.storage().cl_service.cl_store().get_cl(link).await? {
dispatch_cl_webhook(self.storage(), WebhookEvent::ClUpdated, &cl_model);
}
Expand All @@ -326,7 +331,7 @@ const EVENT_CL_COMMENT_CREATED: &str = "cl.comment.created";

async fn enqueue_cl_comment_notifications(
service: &ClApplicationService,
actor_username: &str,
actor: &str,
cl_link: &str,
comment_text: &str,
) -> Result<(), MegaError> {
Expand All @@ -350,7 +355,7 @@ async fn enqueue_cl_comment_notifications(
for r in reviewers {
recipients.insert(r.campsite_user_id);
}
recipients.remove(actor_username);
recipients.remove(actor);

for username in recipients {
if !notif_stg
Expand All @@ -366,9 +371,9 @@ async fn enqueue_cl_comment_notifications(
};

let subject = format!("New comment on CL {cl_link}");
let body_text = format!("{actor_username} commented on {cl_link}: {comment_text}");
let body_text = format!("{actor} commented on {cl_link}: {comment_text}");
let body_html = format!(
"<p><b>{actor_username}</b> commented on <b>{cl_link}</b>:</p><p>{}</p>",
"<p><b>{actor}</b> commented on <b>{cl_link}</b>:</p><p>{}</p>",
escape_html(comment_text)
);

Expand Down
31 changes: 21 additions & 10 deletions ceres/src/application/api_service/mono/conversation.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use common::errors::MegaError;

use super::context::ConversationApplicationService;
use crate::model::{
change_list::MergeStatus,
conversation::{ConvType, ReferenceType},
use crate::{
application::member_identity::display_label_for_actor,
model::{
change_list::MergeStatus,
conversation::{ConvType, ReferenceType},
},
};

impl ConversationApplicationService {
Expand All @@ -22,6 +25,19 @@ impl ConversationApplicationService {
.await
}

/// System timeline event: persist actor as campsite public id, comment uses display label.
pub async fn add_system_event(
&self,
link: &str,
actor: &str,
phrase: &str,
conv_type: ConvType,
) -> Result<i64, MegaError> {
let display = display_label_for_actor(self.ctx.storage(), actor).await;
self.add_conversation(link, actor, Some(format!("{display} {phrase}")), conv_type)
.await
}

pub async fn add_issue_mention_reference(
&self,
source_link: &str,
Expand All @@ -34,13 +50,8 @@ impl ConversationApplicationService {
.issue_store()
.add_reference(source_link, ref_link, ReferenceType::Mention.into())
.await?;
self.add_conversation(
ref_link,
username,
Some(format!("{username} mentioned this on")),
ConvType::Mention,
)
.await?;
self.add_system_event(ref_link, username, "mentioned this on", ConvType::Mention)
.await?;
Ok(())
}

Expand Down
13 changes: 9 additions & 4 deletions ceres/src/application/api_service/mono/label_assignee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use common::errors::MegaError;
use jupiter::model::common::LabelAssigneeParams;

use super::context::IssueApplicationService;
use crate::application::member_identity::display_label_for_actor;

impl IssueApplicationService {
pub async fn update_item_labels(
Expand Down Expand Up @@ -33,6 +34,8 @@ impl IssueApplicationService {
.modify_labels(to_add.clone(), to_remove.clone(), params)
.await?;

let display = display_label_for_actor(self.ctx.storage(), username).await;

if !to_remove.is_empty() {
self.ctx
.storage()
Expand All @@ -41,7 +44,7 @@ impl IssueApplicationService {
.add_conversation(
link,
username,
Some(format!("{username} removed {to_remove:?}")),
Some(format!("{display} removed {to_remove:?}")),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the action token in label events

When display_label_for_actor falls back to a real display name with spaces, this writes comments like Jane Doe removed [1], but the existing label/assignee renderers derive the action from conv.comment?.split(' ')[1] (LabelItem and AssigneeItem), so the timeline shows Doe instead of removed/added/assigned. This affects any actor whose resolved display label is not a single token; keep the persisted prefix parse-compatible or update those renderers to strip the actor/display prefix before reading the verb.

Useful? React with 👍 / 👎.

ConvTypeEnum::Label,
)
.await?;
Expand All @@ -55,7 +58,7 @@ impl IssueApplicationService {
.add_conversation(
link,
username,
Some(format!("{username} added {to_add:?}")),
Some(format!("{display} added {to_add:?}")),
ConvTypeEnum::Label,
)
.await?;
Expand Down Expand Up @@ -93,6 +96,8 @@ impl IssueApplicationService {
.modify_assignees(to_add.clone(), to_remove.clone(), params)
.await?;

let display = display_label_for_actor(self.ctx.storage(), username).await;

if !to_remove.is_empty() {
self.ctx
.storage()
Expand All @@ -101,7 +106,7 @@ impl IssueApplicationService {
.add_conversation(
link,
username,
Some(format!("{username} unassigned {to_remove:?}")),
Some(format!("{display} unassigned {to_remove:?}")),
ConvTypeEnum::Assignee,
)
.await?;
Expand All @@ -115,7 +120,7 @@ impl IssueApplicationService {
.add_conversation(
link,
username,
Some(format!("{username} assigned {to_add:?}")),
Some(format!("{display} assigned {to_add:?}")),
ConvTypeEnum::Assignee,
)
.await?;
Expand Down
14 changes: 2 additions & 12 deletions mono/src/api/router/issue_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,7 @@ async fn close_issue(
state
.services()
.conversation()
.add_conversation(
&link,
actor,
Some(format!("{} closed this", actor)),
ConvType::Closed,
)
.add_system_event(&link, actor, "closed this", ConvType::Closed)
.await?;
Ok(Json(CommonResult::success(None)))
}
Expand All @@ -167,12 +162,7 @@ async fn reopen_issue(
state
.services()
.conversation()
.add_conversation(
&link,
actor,
Some(format!("{} reopen this", actor)),
ConvType::Closed,
)
.add_system_event(&link, actor, "reopen this", ConvType::Reopen)
.await?;
Ok(Json(CommonResult::success(None)))
}
Expand Down
13 changes: 5 additions & 8 deletions moon/apps/web/components/ClView/CloseItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ConditionalWrap } from '@gitmono/ui'
import { ActorAvatar } from '@/components/ActorAvatar'
import { BotBadge } from '@/components/BotBadge'
import { useMemberByActor } from '@/hooks/useMemberByActor'
import { megaUserHandle } from '@/utils/megaUser'
import { megaUserHandle, systemEventPhrase } from '@/utils/megaUser'

import { MemberHovercard } from '../InlinePost/MemberHovercard'
import HandleTime from './components/HandleTime'
Expand All @@ -18,6 +18,7 @@ const CloseItem = ({ conv }: CloseItemProps) => {
const { data: member } = useMemberByActor(conv.username, !isBot)
const profileUsername = member?.user.username || conv.username
const displayName = megaUserHandle(member?.user, conv.username) || conv.username
const phrase = systemEventPhrase(conv.comment, conv.username, 'closed this')

return (
<>
Expand All @@ -37,13 +38,9 @@ const CloseItem = ({ conv }: CloseItemProps) => {
</ConditionalWrap>
</div>
<div className='flex flex-wrap items-center gap-1.5'>
{!conv.comment && (
<>
<span className='font-semibold'>{displayName}</span>
{isBot && <BotBadge size='sm' />}
</>
)}
<span>{conv.comment}</span>
<span className='font-semibold'>{displayName}</span>
{isBot && <BotBadge size='sm' />}
<span>{phrase}</span>
</div>
<div className='text-sm text-gray-500 hover:text-gray-700'>
<HandleTime created_at={conv.created_at} />
Expand Down
Loading
Loading