ui: update account management to design
Closes: https://github.com/damus-io/notedeck/issues/486 Fixes: https://github.com/damus-io/notedeck/issues/444 Signed-off-by: kernelkind <kernelkind@gmail.com> Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
committed by
William Casarin
parent
409e8c2e3a
commit
0ac131ef06
@@ -131,7 +131,16 @@ impl Accounts {
|
|||||||
self.select_account(selected_index - 1);
|
self.select_account(selected_index - 1);
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
self.clear_selected_account();
|
if self.accounts.is_empty() {
|
||||||
|
// If no accounts remain, clear the selection
|
||||||
|
self.clear_selected_account();
|
||||||
|
} else if index >= self.accounts.len() {
|
||||||
|
// If the removed account was the last one, select the new last account
|
||||||
|
self.select_account(self.accounts.len() - 1);
|
||||||
|
} else {
|
||||||
|
// Otherwise, select the account at the same position
|
||||||
|
self.select_account(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ordering::Less => {}
|
Ordering::Less => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ pub fn desktop_font_size(text_style: &NotedeckTextStyle) -> f32 {
|
|||||||
NotedeckTextStyle::Monospace => 13.0,
|
NotedeckTextStyle::Monospace => 13.0,
|
||||||
NotedeckTextStyle::Button => 13.0,
|
NotedeckTextStyle::Button => 13.0,
|
||||||
NotedeckTextStyle::Small => 12.0,
|
NotedeckTextStyle::Small => 12.0,
|
||||||
|
NotedeckTextStyle::Tiny => 11.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +95,7 @@ pub fn mobile_font_size(text_style: &NotedeckTextStyle) -> f32 {
|
|||||||
NotedeckTextStyle::Monospace => 13.0,
|
NotedeckTextStyle::Monospace => 13.0,
|
||||||
NotedeckTextStyle::Button => 13.0,
|
NotedeckTextStyle::Button => 13.0,
|
||||||
NotedeckTextStyle::Small => 12.0,
|
NotedeckTextStyle::Small => 12.0,
|
||||||
|
NotedeckTextStyle::Tiny => 11.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +116,7 @@ pub enum NotedeckTextStyle {
|
|||||||
Monospace,
|
Monospace,
|
||||||
Button,
|
Button,
|
||||||
Small,
|
Small,
|
||||||
|
Tiny,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NotedeckTextStyle {
|
impl NotedeckTextStyle {
|
||||||
@@ -126,6 +129,7 @@ impl NotedeckTextStyle {
|
|||||||
Self::Monospace => TextStyle::Monospace,
|
Self::Monospace => TextStyle::Monospace,
|
||||||
Self::Button => TextStyle::Button,
|
Self::Button => TextStyle::Button,
|
||||||
Self::Small => TextStyle::Small,
|
Self::Small => TextStyle::Small,
|
||||||
|
Self::Tiny => TextStyle::Name("Tiny".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +142,7 @@ impl NotedeckTextStyle {
|
|||||||
Self::Monospace => FontFamily::Monospace,
|
Self::Monospace => FontFamily::Monospace,
|
||||||
Self::Button => FontFamily::Proportional,
|
Self::Button => FontFamily::Proportional,
|
||||||
Self::Small => FontFamily::Proportional,
|
Self::Small => FontFamily::Proportional,
|
||||||
|
Self::Tiny => FontFamily::Proportional,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,6 +159,7 @@ pub fn create_themed_visuals(theme: ColorTheme, default: Visuals) -> Visuals {
|
|||||||
color: theme.selection_color,
|
color: theme.selection_color,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
warn_fg_color: theme.warn_fg_color,
|
||||||
widgets: Widgets {
|
widgets: Widgets {
|
||||||
noninteractive: WidgetVisuals {
|
noninteractive: WidgetVisuals {
|
||||||
bg_fill: theme.noninteractive_bg_fill,
|
bg_fill: theme.noninteractive_bg_fill,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ pub const PINK: Color32 = Color32::from_rgb(0xE4, 0x5A, 0xC9);
|
|||||||
pub const GRAY_SECONDARY: Color32 = Color32::from_rgb(0x8A, 0x8A, 0x8A);
|
pub const GRAY_SECONDARY: Color32 = Color32::from_rgb(0x8A, 0x8A, 0x8A);
|
||||||
const BLACK: Color32 = Color32::from_rgb(0x00, 0x00, 0x00);
|
const BLACK: Color32 = Color32::from_rgb(0x00, 0x00, 0x00);
|
||||||
const RED_700: Color32 = Color32::from_rgb(0xC7, 0x37, 0x5A);
|
const RED_700: Color32 = Color32::from_rgb(0xC7, 0x37, 0x5A);
|
||||||
//const ORANGE_700: Color32 = Color32::from_rgb(0xF6, 0xB1, 0x4A);
|
const ORANGE_700: Color32 = Color32::from_rgb(0xF6, 0xB1, 0x4A);
|
||||||
|
|
||||||
// BACKGROUNDS
|
// BACKGROUNDS
|
||||||
const SEMI_DARKER_BG: Color32 = Color32::from_rgb(0x39, 0x39, 0x39);
|
const SEMI_DARKER_BG: Color32 = Color32::from_rgb(0x39, 0x39, 0x39);
|
||||||
@@ -30,7 +30,7 @@ pub struct ColorTheme {
|
|||||||
pub extreme_bg_color: Color32,
|
pub extreme_bg_color: Color32,
|
||||||
pub text_color: Color32,
|
pub text_color: Color32,
|
||||||
pub err_fg_color: Color32,
|
pub err_fg_color: Color32,
|
||||||
//pub warn_fg_color: Color32,
|
pub warn_fg_color: Color32,
|
||||||
pub hyperlink_color: Color32,
|
pub hyperlink_color: Color32,
|
||||||
pub selection_color: Color32,
|
pub selection_color: Color32,
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ pub fn desktop_dark_color_theme() -> ColorTheme {
|
|||||||
extreme_bg_color: DARK_ISH_BG,
|
extreme_bg_color: DARK_ISH_BG,
|
||||||
text_color: Color32::WHITE,
|
text_color: Color32::WHITE,
|
||||||
err_fg_color: RED_700,
|
err_fg_color: RED_700,
|
||||||
//warn_fg_color: ORANGE_700,
|
warn_fg_color: ORANGE_700,
|
||||||
hyperlink_color: PURPLE,
|
hyperlink_color: PURPLE,
|
||||||
selection_color: PURPLE_ALT,
|
selection_color: PURPLE_ALT,
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ pub fn light_color_theme() -> ColorTheme {
|
|||||||
extreme_bg_color: LIGHTER_GRAY,
|
extreme_bg_color: LIGHTER_GRAY,
|
||||||
text_color: BLACK,
|
text_color: BLACK,
|
||||||
err_fg_color: RED_700,
|
err_fg_color: RED_700,
|
||||||
//warn_fg_color: ORANGE_700,
|
warn_fg_color: ORANGE_700,
|
||||||
hyperlink_color: PURPLE,
|
hyperlink_color: PURPLE,
|
||||||
selection_color: PURPLE_ALT,
|
selection_color: PURPLE_ALT,
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::colors::PINK;
|
use crate::colors::{self, PINK};
|
||||||
use crate::imgcache::ImageCache;
|
use crate::imgcache::ImageCache;
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts::Accounts,
|
accounts::Accounts,
|
||||||
@@ -6,7 +6,9 @@ use crate::{
|
|||||||
ui::{Preview, PreviewConfig, View},
|
ui::{Preview, PreviewConfig, View},
|
||||||
Damus,
|
Damus,
|
||||||
};
|
};
|
||||||
use egui::{Align, Button, Frame, Image, InnerResponse, Layout, RichText, ScrollArea, Ui, Vec2};
|
use egui::{
|
||||||
|
Align, Button, Frame, Image, InnerResponse, Layout, RichText, ScrollArea, Stroke, Ui, Vec2,
|
||||||
|
};
|
||||||
use nostrdb::{Ndb, Transaction};
|
use nostrdb::{Ndb, Transaction};
|
||||||
|
|
||||||
use super::profile::preview::SimpleProfilePreview;
|
use super::profile::preview::SimpleProfilePreview;
|
||||||
@@ -25,7 +27,7 @@ pub enum AccountsViewResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ProfilePreviewOp {
|
enum ProfilePreviewAction {
|
||||||
RemoveAccount,
|
RemoveAccount,
|
||||||
SwitchTo,
|
SwitchTo,
|
||||||
}
|
}
|
||||||
@@ -72,14 +74,9 @@ impl<'a> AccountsView<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for i in 0..accounts.num_accounts() {
|
for i in 0..accounts.num_accounts() {
|
||||||
let account_pubkey = accounts
|
let (account_pubkey, has_nsec) = match accounts.get_account(i) {
|
||||||
.get_account(i)
|
Some(acc) => (acc.pubkey.bytes(), acc.secret_key.is_some()),
|
||||||
.map(|account| account.pubkey.bytes());
|
None => continue,
|
||||||
|
|
||||||
let account_pubkey = if let Some(pubkey) = account_pubkey {
|
|
||||||
pubkey
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let profile = ndb.get_profile_by_pubkey(&txn, account_pubkey).ok();
|
let profile = ndb.get_profile_by_pubkey(&txn, account_pubkey).ok();
|
||||||
@@ -91,15 +88,22 @@ impl<'a> AccountsView<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let profile_peview_view = {
|
let profile_peview_view = {
|
||||||
let width = ui.available_width();
|
let max_size = egui::vec2(ui.available_width(), 77.0);
|
||||||
let preview = SimpleProfilePreview::new(profile.as_ref(), img_cache);
|
let resp = ui.allocate_response(max_size, egui::Sense::click());
|
||||||
show_profile_card(ui, preview, width, is_selected)
|
ui.allocate_ui_at_rect(resp.rect, |ui| {
|
||||||
|
let preview =
|
||||||
|
SimpleProfilePreview::new(profile.as_ref(), img_cache, has_nsec);
|
||||||
|
show_profile_card(ui, preview, max_size, is_selected, resp)
|
||||||
|
})
|
||||||
|
.inner
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(op) = profile_peview_view {
|
if let Some(op) = profile_peview_view {
|
||||||
return_op = Some(match op {
|
return_op = Some(match op {
|
||||||
ProfilePreviewOp::SwitchTo => AccountsViewResponse::SelectAccount(i),
|
ProfilePreviewAction::SwitchTo => {
|
||||||
ProfilePreviewOp::RemoveAccount => {
|
AccountsViewResponse::SelectAccount(i)
|
||||||
|
}
|
||||||
|
ProfilePreviewAction::RemoveAccount => {
|
||||||
AccountsViewResponse::RemoveAccount(i)
|
AccountsViewResponse::RemoveAccount(i)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -130,30 +134,36 @@ impl<'a> AccountsView<'a> {
|
|||||||
fn show_profile_card(
|
fn show_profile_card(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
preview: SimpleProfilePreview,
|
preview: SimpleProfilePreview,
|
||||||
width: f32,
|
max_size: egui::Vec2,
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
) -> Option<ProfilePreviewOp> {
|
card_resp: egui::Response,
|
||||||
let mut op: Option<ProfilePreviewOp> = None;
|
) -> Option<ProfilePreviewAction> {
|
||||||
|
let mut op: Option<ProfilePreviewAction> = None;
|
||||||
|
|
||||||
ui.add_sized(Vec2::new(width, 50.0), |ui: &mut egui::Ui| {
|
ui.add_sized(max_size, |ui: &mut egui::Ui| {
|
||||||
Frame::none()
|
let mut frame = Frame::none();
|
||||||
|
if is_selected || card_resp.hovered() {
|
||||||
|
frame = frame.fill(ui.visuals().noninteractive().weak_bg_fill)
|
||||||
|
}
|
||||||
|
if is_selected {
|
||||||
|
frame = frame.stroke(Stroke::new(2.0, colors::PINK))
|
||||||
|
}
|
||||||
|
frame
|
||||||
|
.rounding(8.0)
|
||||||
|
.inner_margin(8.0)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add(preview);
|
ui.add(preview);
|
||||||
|
|
||||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||||
if is_selected {
|
if card_resp.clicked() {
|
||||||
ui.add(selected_widget());
|
op = Some(ProfilePreviewAction::SwitchTo);
|
||||||
} else {
|
}
|
||||||
if ui
|
if ui
|
||||||
.add(switch_button(ui.style().visuals.dark_mode))
|
.add_sized(egui::Vec2::new(84.0, 32.0), sign_out_button())
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
op = Some(ProfilePreviewOp::SwitchTo);
|
op = Some(ProfilePreviewAction::RemoveAccount)
|
||||||
}
|
|
||||||
if ui.add(sign_out_button(ui)).clicked() {
|
|
||||||
op = Some(ProfilePreviewOp::RemoveAccount)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -183,34 +193,8 @@ fn add_account_button() -> Button<'static> {
|
|||||||
.frame(false)
|
.frame(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_out_button(ui: &egui::Ui) -> egui::Button<'static> {
|
fn sign_out_button() -> egui::Button<'static> {
|
||||||
let img_data = egui::include_image!("../../assets/icons/signout_icon_4x.png");
|
egui::Button::new(RichText::new("Sign out"))
|
||||||
let img = Image::new(img_data).fit_to_exact_size(Vec2::new(16.0, 16.0));
|
|
||||||
|
|
||||||
egui::Button::image_and_text(
|
|
||||||
img,
|
|
||||||
RichText::new("Sign out").color(ui.visuals().noninteractive().fg_stroke.color),
|
|
||||||
)
|
|
||||||
.frame(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn switch_button(dark_mode: bool) -> egui::Button<'static> {
|
|
||||||
let _ = dark_mode;
|
|
||||||
|
|
||||||
egui::Button::new("Switch").min_size(Vec2::new(76.0, 32.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn selected_widget() -> impl egui::Widget {
|
|
||||||
|ui: &mut egui::Ui| {
|
|
||||||
Frame::none()
|
|
||||||
.show(ui, |ui| {
|
|
||||||
ui.label(RichText::new("Selected").size(13.0).color(PINK));
|
|
||||||
let img_data = egui::include_image!("../../assets/icons/select_icon_3x.png");
|
|
||||||
let img = Image::new(img_data).max_size(Vec2::new(16.0, 16.0));
|
|
||||||
ui.add(img);
|
|
||||||
})
|
|
||||||
.response
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PREVIEWS
|
// PREVIEWS
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::app_style::NotedeckTextStyle;
|
use crate::app_style::{get_font_size, NotedeckTextStyle};
|
||||||
use crate::imgcache::ImageCache;
|
use crate::imgcache::ImageCache;
|
||||||
use crate::storage::{DataPath, DataPathType};
|
use crate::storage::{DataPath, DataPathType};
|
||||||
use crate::ui::ProfilePic;
|
use crate::ui::ProfilePic;
|
||||||
use crate::user_account::UserAccount;
|
use crate::user_account::UserAccount;
|
||||||
use crate::{colors, images, DisplayName};
|
use crate::{colors, images, DisplayName};
|
||||||
use egui::load::TexturePoll;
|
use egui::load::TexturePoll;
|
||||||
use egui::{Frame, RichText, Sense, Widget};
|
use egui::{Frame, Label, RichText, Sense, Widget};
|
||||||
use egui_extras::Size;
|
use egui_extras::Size;
|
||||||
use enostr::NoteId;
|
use enostr::NoteId;
|
||||||
use nostrdb::ProfileRecord;
|
use nostrdb::ProfileRecord;
|
||||||
@@ -93,11 +93,20 @@ impl egui::Widget for ProfilePreview<'_, '_> {
|
|||||||
pub struct SimpleProfilePreview<'a, 'cache> {
|
pub struct SimpleProfilePreview<'a, 'cache> {
|
||||||
profile: Option<&'a ProfileRecord<'a>>,
|
profile: Option<&'a ProfileRecord<'a>>,
|
||||||
cache: &'cache mut ImageCache,
|
cache: &'cache mut ImageCache,
|
||||||
|
is_nsec: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'cache> SimpleProfilePreview<'a, 'cache> {
|
impl<'a, 'cache> SimpleProfilePreview<'a, 'cache> {
|
||||||
pub fn new(profile: Option<&'a ProfileRecord<'a>>, cache: &'cache mut ImageCache) -> Self {
|
pub fn new(
|
||||||
SimpleProfilePreview { profile, cache }
|
profile: Option<&'a ProfileRecord<'a>>,
|
||||||
|
cache: &'cache mut ImageCache,
|
||||||
|
is_nsec: bool,
|
||||||
|
) -> Self {
|
||||||
|
SimpleProfilePreview {
|
||||||
|
profile,
|
||||||
|
cache,
|
||||||
|
is_nsec,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +117,16 @@ impl egui::Widget for SimpleProfilePreview<'_, '_> {
|
|||||||
ui.add(ProfilePic::new(self.cache, get_profile_url(self.profile)).size(48.0));
|
ui.add(ProfilePic::new(self.cache, get_profile_url(self.profile)).size(48.0));
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.add(display_name_widget(get_display_name(self.profile), true));
|
ui.add(display_name_widget(get_display_name(self.profile), true));
|
||||||
|
if !self.is_nsec {
|
||||||
|
ui.add(
|
||||||
|
Label::new(
|
||||||
|
RichText::new("View only mode")
|
||||||
|
.size(get_font_size(ui.ctx(), &NotedeckTextStyle::Tiny))
|
||||||
|
.color(ui.visuals().warn_fg_color),
|
||||||
|
)
|
||||||
|
.selectable(false),
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.response
|
.response
|
||||||
@@ -203,8 +222,10 @@ fn display_name_widget(
|
|||||||
) -> impl egui::Widget + '_ {
|
) -> impl egui::Widget + '_ {
|
||||||
move |ui: &mut egui::Ui| match display_name {
|
move |ui: &mut egui::Ui| match display_name {
|
||||||
DisplayName::One(n) => {
|
DisplayName::One(n) => {
|
||||||
let name_response =
|
let name_response = ui.add(
|
||||||
ui.label(RichText::new(n).text_style(NotedeckTextStyle::Heading3.text_style()));
|
Label::new(RichText::new(n).text_style(NotedeckTextStyle::Heading3.text_style()))
|
||||||
|
.selectable(false),
|
||||||
|
);
|
||||||
if add_placeholder_space {
|
if add_placeholder_space {
|
||||||
ui.add_space(16.0);
|
ui.add_space(16.0);
|
||||||
}
|
}
|
||||||
@@ -215,14 +236,21 @@ fn display_name_widget(
|
|||||||
display_name,
|
display_name,
|
||||||
username,
|
username,
|
||||||
} => {
|
} => {
|
||||||
ui.label(
|
ui.add(
|
||||||
RichText::new(display_name).text_style(NotedeckTextStyle::Heading3.text_style()),
|
Label::new(
|
||||||
|
RichText::new(display_name)
|
||||||
|
.text_style(NotedeckTextStyle::Heading3.text_style()),
|
||||||
|
)
|
||||||
|
.selectable(false),
|
||||||
);
|
);
|
||||||
|
|
||||||
ui.label(
|
ui.add(
|
||||||
RichText::new(format!("@{}", username))
|
Label::new(
|
||||||
.size(12.0)
|
RichText::new(format!("@{}", username))
|
||||||
.color(colors::MID_GRAY),
|
.size(12.0)
|
||||||
|
.color(colors::MID_GRAY),
|
||||||
|
)
|
||||||
|
.selectable(false),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user