hgame 0.26.4

CG production management structs, e.g. of assets, personnels, progress, etc.
Documentation
use super::*;
#[cfg(feature = "gui")]
use mktree::egui::Stroke;

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct MsgExpand {
    pub(super) mode: MediaMode,

    /// Used to check against various [`MsgActionPerm`].
    perm: u8,

    /// Queried and deserialized topical asset of the message.
    /// Used in both [`MediaMode::Read`] and [`MediaMode::WriteCompose`]
    pub(super) topical_asset: Option<AssetExcerpt>,

    /// Queried and deserialized recipients as [`Staff`]s.
    recipients: HashSet<Staff>,

    /// Queried and deserialized sender as [`Staff`].
    pub sender: Staff,

    /// Queried and deserialized excerpt of the original message which we're replying to.
    /// Used in both [`MediaMode::Read`] and [`MediaMode::WriteCompose`]
    pub as_reply_to: Option<MsgExcerpt>,

    pub seen_by: HashSet<Staff>,

    pub(super) seen_by_str: String,

    pub seen_by_self: bool,

    /// The local time when the message was sent.
    pub(super) created_at_local: String,
}

impl MsgExpand {
    pub(super) fn empty() -> Self {
        Self {
            perm: MsgActionPerm::default_sum(),
            sender: Staff::empty(),
            ..Default::default()
        }
    }

    pub(super) fn perm(&self) -> &u8 {
        &self.perm
    }

    pub fn perm_per_role(&mut self, viewer: Option<&Staff>) {
        if let Some(viewer) = viewer {
            self.perm = MsgActionPerm::per_role(viewer, &self.sender);
        } else {
            // no idea about the viewer
            self.perm = MsgActionPerm::default_sum();
        };
    }

    pub(super) fn is_recipient_empty(&self) -> bool {
        match self.recipients.is_empty() {
            true => true,
            false => {
                // check if there's any recipient other than CC-ing self
                self.recipients == HashSet::from([self.sender.clone()])
            }
        }
    }

    pub(super) fn topical_asset(&self) -> Option<&AssetExcerpt> {
        self.topical_asset.as_ref()
    }

    pub(super) fn topical_asset_mut(&mut self, asset: Option<AssetExcerpt>) {
        self.topical_asset = asset;
    }

    pub(super) fn topical_asset_clone(&self) -> Option<AssetExcerpt> {
        self.topical_asset.clone()
    }

    // pub(super) fn pop_composer(&mut self) {
    //     self.recipients.remove(&self.sender);
    // }

    // pub(super) fn cc_composer(&mut self) {
    //     self.recipients.insert(self.sender.clone());
    // }

    pub(super) fn recipients(&self) -> &HashSet<Staff> {
        &self.recipients
    }

    pub fn recipients_mut(&mut self, recipients: HashSet<Staff>) {
        self.recipients = recipients;
    }

    pub(super) fn recipients_owned(&self) -> HashSet<Staff> {
        self.recipients.clone()
    }
}

#[cfg(feature = "gui")]
impl MsgExpand {
    /// Modifies color of the `Ui::group` if the message is unread.
    pub(super) fn color_unread_frame(&self, ui: &mut egui::Ui) {
        ui.style_mut().visuals.widgets.noninteractive.bg_stroke = Stroke::new(
            1.,
            if self.seen_by_self {
                // must cover both branches
                Color32::DARK_GRAY
            } else {
                Color32::YELLOW
            },
        );
    }

    /// Display of the topical asset in write mode.
    pub fn topical_asset_hint_mut_ui(
        &mut self,
        ui: &mut egui::Ui,
        alternative: &mut Option<AssetExcerpt>,
        favor_alternative: bool,
    ) {
        ui.horizontal(|ui| {
            ui.label("Topical Asset:");
            match favor_alternative {
                false => {
                    if self.topical_asset.is_none() {
                        no_topical_asset_warning(ui);
                        return;
                    };
                    if let Some(asset) = &self.topical_asset {
                        asset.preview_name(ui);
                    };
                    if ui
                        .button(RichText::new("").color(Color32::LIGHT_RED))
                        .on_hover_text("Clear the topical asset")
                        .clicked()
                    {
                        self.topical_asset.take();
                    };
                }
                true => match alternative {
                    Some(asset) => {
                        asset.preview_name(ui);
                        if ui
                            .button(RichText::new("").color(Color32::LIGHT_RED))
                            .on_hover_text("Clear the topical asset")
                            .clicked()
                        {
                            alternative.take();
                        };
                    }
                    None => {
                        no_topical_asset_warning(ui);
                    }
                },
            };
        });
    }

    /// Displays count and details-on-hover of recipient list when it's not empty,
    /// and a warning when it is.
    pub(super) fn recipients_hint_ui(&self, ui: &mut egui::Ui) {
        if self.recipients.is_empty() {
            // shows warning when list of recipients is empty
            ui.colored_label(Color32::LIGHT_RED, "▪ Recipient list is empty");
            return;
        };
        // no need for showing complete list, since a recursive tree fits this purpose better
        ui.strong(format!("{} recipients", self.recipients.len()))
            .on_hover_text(
                self.recipients
                    .iter()
                    .map(|s| s.name_unwrap().to_owned())
                    .collect::<Vec<String>>()
                    .join(", "),
            );
    }

    #[allow(dead_code)]
    #[cfg(debug_assertions)]
    pub(super) fn permission_ui(&self, ui: &mut egui::Ui) {
        ui.label(RichText::new(format!("Permission Sum: {}", self.perm)).color(Color32::RED));
    }

    /// Display of the reply hint in read mode.
    pub(super) fn reply_hint_ui(&self, ui: &mut egui::Ui) {
        if let Some(reply) = &self.as_reply_to {
            ui.horizontal_wrapped(|ui| {
                reply.reply_read_ui(ui);
            });
        };
    }

    /// Display of the reply hint in write mode.
    pub(super) fn reply_hint_mut_ui(&mut self, ui: &mut egui::Ui) {
        ui.horizontal(|ui| {
            if let Some(msg) = &self.as_reply_to {
                msg.reply_draft_ui(ui);
                if ui
                    .button(RichText::new("").color(Color32::LIGHT_RED))
                    .on_hover_text("Clear the reply target")
                    .clicked()
                {
                    self.as_reply_to.take();
                };
            };
        });
    }

    pub(super) fn created_at_ui(&self, ui: &mut egui::Ui) {
        ui.horizontal(|ui| {
            ui.with_layout(Layout::right_to_left(Align::Max), |ui| {
                ui.small(&self.created_at_local);
            });
        });
    }

    pub(super) fn sent_at_ui(&self, ui: &mut egui::Ui) {
        ui.horizontal(|ui| {
            ui.with_layout(Layout::right_to_left(Align::Max), |ui| {
                ui.small(format!("Sent {}", self.created_at_local));
            });
        });
    }

    pub(super) fn seen_by_ui(&self, ui: &mut egui::Ui) {
        ui.horizontal_wrapped(|ui| {
            // do not use `Layout::right_to_left`
            ui.label(RichText::new(&self.seen_by_str).weak().small());
        });
    }
}