Compare commits

...

3 commits

Author SHA1 Message Date
10214f3369 Fix clippy warning 2025-06-27 11:03:06 +02:00
2ca6190d97 Use older ubuntu runner for older glibc version 2025-05-31 15:08:24 +02:00
67e77c8880 Show emoji hash in nick list 2025-05-31 15:06:55 +02:00
7 changed files with 86 additions and 26 deletions

View file

@ -16,7 +16,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: os:
- ubuntu-latest - ubuntu-22.04
- windows-latest - windows-latest
- macos-latest - macos-latest
- macos-13 - macos-13
@ -59,11 +59,11 @@ jobs:
- name: Zip artifacts - name: Zip artifacts
run: | run: |
chmod +x cove-ubuntu-latest/cove chmod +x cove-ubuntu-22.04/cove
chmod +x cove-windows-latest/cove.exe chmod +x cove-windows-latest/cove.exe
chmod +x cove-macos-latest/cove chmod +x cove-macos-latest/cove
chmod +x cove-macos-13/cove chmod +x cove-macos-13/cove
zip -jr "cove-$(cat cove-ubuntu-latest/host).zip" cove-ubuntu-latest/cove zip -jr "cove-$(cat cove-ubuntu-22.04/host).zip" cove-ubuntu-22.04/cove
zip -jr "cove-$(cat cove-windows-latest/host).zip" cove-windows-latest/cove.exe zip -jr "cove-$(cat cove-windows-latest/host).zip" cove-windows-latest/cove.exe
zip -jr "cove-$(cat cove-macos-latest/host).zip" cove-macos-latest/cove zip -jr "cove-$(cat cove-macos-latest/host).zip" cove-macos-latest/cove
zip -jr "cove-$(cat cove-macos-13/host).zip" cove-macos-13/cove zip -jr "cove-$(cat cove-macos-13/host).zip" cove-macos-13/cove

View file

@ -15,6 +15,11 @@ Procedure when bumping the version number:
## Unreleased ## Unreleased
### Changed
- Display emoji user id hashes in the nick list
- Compile linux binary with older glibc version
## v0.9.3 - 2025-05-31 ## v0.9.3 - 2025-05-31
### Added ### Added

View file

@ -1,5 +1,3 @@
use std::hash::{DefaultHasher, Hash, Hasher};
use crossterm::style::Stylize; use crossterm::style::Stylize;
use euphoxide::api::{MessageId, Snowflake, Time, UserId}; use euphoxide::api::{MessageId, Snowflake, Time, UserId};
use jiff::Timestamp; use jiff::Timestamp;
@ -77,11 +75,7 @@ impl Msg for SmallMessage {
} }
fn nick_emoji(&self) -> Option<String> { fn nick_emoji(&self) -> Option<String> {
let mut hasher = DefaultHasher::new(); Some(util::user_id_emoji(&self.user_id))
self.user_id.0.hash(&mut hasher);
let hash = hasher.finish();
let emoji = &util::EMOJI_LIST[hash as usize % util::EMOJI_LIST.len()];
Some(emoji.clone())
} }
} }

View file

@ -1,7 +1,11 @@
use std::{collections::HashSet, sync::LazyLock}; use std::{
collections::HashSet,
hash::{DefaultHasher, Hash, Hasher},
sync::LazyLock,
};
use crossterm::style::{Color, Stylize}; use crossterm::style::{Color, Stylize};
use euphoxide::Emoji; use euphoxide::{Emoji, api::UserId};
use toss::{Style, Styled}; use toss::{Style, Styled};
pub static EMOJI: LazyLock<Emoji> = LazyLock::new(Emoji::load); pub static EMOJI: LazyLock<Emoji> = LazyLock::new(Emoji::load);
@ -82,3 +86,11 @@ pub fn style_mention_exact(mention: &str, base: Style) -> Styled {
.expect("mention must start with @"); .expect("mention must start with @");
Styled::new(mention, nick_style(nick, base)) Styled::new(mention, nick_style(nick, base))
} }
pub fn user_id_emoji(user_id: &UserId) -> String {
let mut hasher = DefaultHasher::new();
user_id.0.hash(&mut hasher);
let hash = hasher.finish();
let emoji = &EMOJI_LIST[hash as usize % EMOJI_LIST.len()];
emoji.clone()
}

View file

@ -58,6 +58,10 @@ impl<M: Msg, S: MsgStore<M> + Clone> ChatState<M, S> {
store, store,
} }
} }
pub fn nick_emoji(&self) -> bool {
self.nick_emoji
}
} }
impl<M: Msg, S: MsgStore<M>> ChatState<M, S> { impl<M: Msg, S: MsgStore<M>> ChatState<M, S> {

View file

@ -22,9 +22,10 @@ pub fn widget<'a>(
list: &'a mut ListState<SessionId>, list: &'a mut ListState<SessionId>,
joined: &Joined, joined: &Joined,
focused: bool, focused: bool,
nick_emoji: bool,
) -> impl Widget<UiError> + use<'a> { ) -> impl Widget<UiError> + use<'a> {
let mut list_builder = ListBuilder::new(); let mut list_builder = ListBuilder::new();
render_rows(&mut list_builder, joined, focused); render_rows(&mut list_builder, joined, focused, nick_emoji);
list_builder.build(list) list_builder.build(list)
} }
@ -70,6 +71,7 @@ fn render_rows(
list_builder: &mut ListBuilder<'_, SessionId, Background<Text>>, list_builder: &mut ListBuilder<'_, SessionId, Background<Text>>,
joined: &Joined, joined: &Joined,
focused: bool, focused: bool,
nick_emoji: bool,
) { ) {
let mut people = vec![]; let mut people = vec![];
let mut bots = vec![]; let mut bots = vec![];
@ -95,10 +97,38 @@ fn render_rows(
lurkers.sort_unstable(); lurkers.sort_unstable();
nurkers.sort_unstable(); nurkers.sort_unstable();
render_section(list_builder, "People", &people, &joined.session, focused); render_section(
render_section(list_builder, "Bots", &bots, &joined.session, focused); list_builder,
render_section(list_builder, "Lurkers", &lurkers, &joined.session, focused); "People",
render_section(list_builder, "Nurkers", &nurkers, &joined.session, focused); &people,
&joined.session,
focused,
nick_emoji,
);
render_section(
list_builder,
"Bots",
&bots,
&joined.session,
focused,
nick_emoji,
);
render_section(
list_builder,
"Lurkers",
&lurkers,
&joined.session,
focused,
nick_emoji,
);
render_section(
list_builder,
"Nurkers",
&nurkers,
&joined.session,
focused,
nick_emoji,
);
} }
fn render_section( fn render_section(
@ -107,6 +137,7 @@ fn render_section(
sessions: &[HalfSession], sessions: &[HalfSession],
own_session: &SessionView, own_session: &SessionView,
focused: bool, focused: bool,
nick_emoji: bool,
) { ) {
if sessions.is_empty() { if sessions.is_empty() {
return; return;
@ -124,7 +155,7 @@ fn render_section(
list_builder.add_unsel(Text::new(row).background()); list_builder.add_unsel(Text::new(row).background());
for session in sessions { for session in sessions {
render_row(list_builder, session, own_session, focused); render_row(list_builder, session, own_session, focused, nick_emoji);
} }
} }
@ -133,6 +164,7 @@ fn render_row(
session: &HalfSession, session: &HalfSession,
own_session: &SessionView, own_session: &SessionView,
focused: bool, focused: bool,
nick_emoji: bool,
) { ) {
let (name, style, style_inv, perms_style_inv) = if session.name.is_empty() { let (name, style, style_inv, perms_style_inv) = if session.name.is_empty() {
let name = "lurk".to_string(); let name = "lurk".to_string();
@ -166,16 +198,24 @@ fn render_row(
" " " "
}; };
let emoji = if nick_emoji {
format!(" ({})", euph::user_id_emoji(&session.id))
} else {
"".to_string()
};
list_builder.add_sel(session.session_id.clone(), move |selected| { list_builder.add_sel(session.session_id.clone(), move |selected| {
if focused && selected { if focused && selected {
let text = Styled::new_plain(owner) let text = Styled::new_plain(owner)
.then(name, style_inv) .then(name, style_inv)
.then(perms, perms_style_inv); .then(perms, perms_style_inv)
.then(emoji, perms_style_inv);
Text::new(text).background().with_style(style_inv) Text::new(text).background().with_style(style_inv)
} else { } else {
let text = Styled::new_plain(owner) let text = Styled::new_plain(owner)
.then(&name, style) .then(&name, style)
.then_plain(perms); .then_plain(perms)
.then_plain(emoji);
Text::new(text).background() Text::new(text).background()
} }
}); });

View file

@ -121,7 +121,7 @@ impl EuphRoom {
.server_config .server_config
.clone() .clone()
.room(self.vault().room().name.clone()) .room(self.vault().room().name.clone())
.name(format!("{room:?}-{}", next_instance_id)) .name(format!("{room:?}-{next_instance_id}"))
.human(true) .human(true)
.username(self.room_config.username.clone()) .username(self.room_config.username.clone())
.force_username(self.room_config.force_username) .force_username(self.room_config.force_username)
@ -291,11 +291,16 @@ impl EuphRoom {
joined: &Joined, joined: &Joined,
focus: Focus, focus: Focus,
) -> BoxedAsync<'a, UiError> { ) -> BoxedAsync<'a, UiError> {
let nick_list_widget = nick_list::widget(nick_list, joined, focus == Focus::NickList) let nick_list_widget = nick_list::widget(
.padding() nick_list,
.with_right(1) joined,
.border() focus == Focus::NickList,
.desync(); chat.nick_emoji(),
)
.padding()
.with_right(1)
.border()
.desync();
let chat_widget = chat.widget(joined.session.name.clone(), focus == Focus::Chat); let chat_widget = chat.widget(joined.session.name.clone(), focus == Focus::Chat);