This commit is contained in:
2026-09-04 17:23:10 +07:00
parent 1496b7afeb
commit 15c2122f38
13 changed files with 63 additions and 46 deletions
+4
View File
@@ -2,10 +2,12 @@ use std::path::PathBuf;
use std::sync::OnceLock;
/// The application name.
///
/// It derives the platform-specific data, config and cache directory paths.
pub const APP_NAME: &str = "Signed";
/// Lowercased form of [`APP_NAME`].
///
/// Used in XDG-style paths on Linux and FreeBSD, and the macOS `~/.config` fallback.
pub const APP_NAME_LOWERCASE: &str = "signed";
@@ -27,12 +29,14 @@ pub fn home_dir() -> PathBuf {
}
/// Returns the current user's Desktop folder.
///
/// Falls back to the home directory or an empty path when it cannot be determined.
pub fn desktop_dir() -> PathBuf {
dirs::desktop_dir().unwrap_or_else(|| dirs::home_dir().unwrap_or_default())
}
/// Returns the current user's Documents folder.
///
/// Falls back to the home directory or an empty path when it cannot be determined.
pub fn documents_dir() -> PathBuf {
dirs::document_dir().unwrap_or_else(|| dirs::home_dir().unwrap_or_default())
-1
View File
@@ -5,5 +5,4 @@ edition.workspace = true
publish.workspace = true
[dependencies]
gpui.workspace = true
nostr.workspace = true
+16 -18
View File
@@ -1,6 +1,5 @@
use std::collections::HashSet;
use gpui::SharedString;
use nostr::prelude::*;
use crate::RepoAddr;
@@ -16,8 +15,8 @@ pub struct Announcement {
pub owner: PublicKey,
/// When the announcement was published, used for latest-wins resolution.
pub created_at: Timestamp,
pub name: Option<SharedString>,
pub description: Option<SharedString>,
pub name: Option<String>,
pub description: Option<String>,
/// Webpage URLs for browsing.
pub web: Vec<Url>,
/// URLs for `git clone`.
@@ -62,17 +61,17 @@ impl Upstream {
}
/// Text for display.
pub fn display(&self) -> SharedString {
pub fn display(&self) -> String {
match &self.addr {
Some(addr) => SharedString::from(addr.to_string()),
None => SharedString::from(self.raw.clone()),
Some(addr) => addr.to_string(),
None => self.raw.clone(),
}
}
}
/// Subject of a NIP-34 issue or pull request event.
/// Taken from the `subject` tag, else the first non-empty line of the content.
pub fn activity_subject(event: &Event) -> SharedString {
pub fn activity_subject(event: &Event) -> String {
let subject = event
.tags
.iter()
@@ -82,16 +81,15 @@ pub fn activity_subject(event: &Event) -> SharedString {
});
subject
.map(SharedString::from)
.or_else(|| {
event
.content
.lines()
.map(str::trim)
.find(|line| !line.is_empty())
.map(SharedString::from)
.map(|value| value.to_string())
})
.unwrap_or(SharedString::from("Untitled"))
.unwrap_or("Untitled".to_string())
}
/// The patch set of a pull request.
@@ -219,8 +217,8 @@ impl Announcement {
let mut hashtags: Vec<String> = Vec::new();
hashtags.extend(event.tags.hashtags().map(|t| t.to_string()));
let mut name: Option<SharedString> = None;
let mut description: Option<SharedString> = None;
let mut name: Option<String> = None;
let mut description: Option<String> = None;
let mut web: Vec<Url> = Vec::new();
let mut clone: Vec<Url> = Vec::new();
let mut relays: Vec<RelayUrl> = Vec::new();
@@ -230,8 +228,8 @@ impl Announcement {
for tag in event.tags.iter() {
match Nip34Tag::parse(tag.as_slice()) {
Ok(Nip34Tag::Name(value)) => name = Some(value.into()),
Ok(Nip34Tag::Description(value)) => description = Some(value.into()),
Ok(Nip34Tag::Name(value)) => name = Some(value),
Ok(Nip34Tag::Description(value)) => description = Some(value),
Ok(Nip34Tag::Web(urls)) => web.extend(urls),
Ok(Nip34Tag::Clone(urls)) => clone.extend(urls),
Ok(Nip34Tag::Relays(urls)) => relays.extend(urls),
@@ -288,10 +286,10 @@ impl Announcement {
}
/// The description of the repository, or a default if none is provided.
pub fn description(&self) -> SharedString {
pub fn description(&self) -> String {
self.description
.clone()
.unwrap_or(SharedString::from("No description"))
.unwrap_or("No description".to_string())
}
/// The effective maintainers of this repository,
@@ -307,11 +305,11 @@ impl Announcement {
}
/// The `git clone` URLs for this repository, deduplicated.
pub fn clone_urls(&self) -> Vec<SharedString> {
pub fn clone_urls(&self) -> Vec<String> {
let mut seen = HashSet::new();
self.clone
.iter()
.map(|url| SharedString::from(format!("git clone {url}")))
.map(|url| format!("git clone {url}"))
.filter(|command| seen.insert(command.clone()))
.collect()
}
+1 -1
View File
@@ -153,7 +153,7 @@ impl RepoStore {
self.announcement
.as_ref()
.map_or(SharedString::default(), |a| {
a.name.clone().unwrap_or(SharedString::from("Unknown"))
SharedString::from(a.name.as_deref().unwrap_or("Unknown"))
})
}
+4 -1
View File
@@ -5,10 +5,13 @@ use gpui_component::menu::PopupMenuItem;
use gpui_component::{ActiveTheme, StyledExt, h_flex};
/// A muted command row with a copy button.
pub fn copy_row<E>(copy_id: E, command: &SharedString, cx: &App) -> Div
pub fn copy_row<E, T>(copy_id: E, command: T, cx: &App) -> Div
where
E: Into<ElementId>,
T: Into<SharedString>,
{
let command = command.into();
h_flex()
.h_8()
.w_full()
@@ -30,8 +30,9 @@ fn announcement_rows(announcement: &Announcement, cx: &App) -> AnyElement {
text(
announcement
.name
.clone()
.unwrap_or_else(|| SharedString::from("")),
.as_deref()
.map(SharedString::from)
.unwrap_or_else(|| SharedString::from("-")),
),
cx,
));
@@ -41,8 +42,9 @@ fn announcement_rows(announcement: &Announcement, cx: &App) -> AnyElement {
text(
announcement
.description
.clone()
.unwrap_or_else(|| SharedString::from("")),
.as_deref()
.map(SharedString::from)
.unwrap_or_else(|| SharedString::from("-")),
),
cx,
));
@@ -131,7 +133,12 @@ fn row(label: &'static str, value: AnyElement, cx: &App) -> AnyElement {
}
/// Plain text value, wrapping within the dialog.
fn text(value: SharedString) -> AnyElement {
fn text<T>(value: T) -> AnyElement
where
T: Into<SharedString>,
{
let value = value.into();
div()
.text_sm()
.w_full()
@@ -143,7 +143,7 @@ impl RepoDetailView {
self.code_element(path.as_ref(), cx)
}
}
Some(FileContent::Binary) => placeholder("Binary file preview not supported", cx),
Some(FileContent::Binary) => placeholder("Binary file - preview not supported", cx),
Some(FileContent::TooLarge) => placeholder("File is too large to preview", cx),
Some(FileContent::Failed(message)) => placeholder(message, cx),
None => preview_spinner(),
@@ -1342,7 +1342,8 @@ impl RepoDetailView {
.map(|announcement| {
announcement
.name
.clone()
.as_deref()
.map(SharedString::from)
.unwrap_or_else(|| SharedString::from(announcement.id.clone()))
})
.unwrap_or_default()
@@ -2359,13 +2360,14 @@ fn fork_row(announcement: &Announcement, cx: &mut Context<RepoDetailView>) -> Op
.find(|a| a.addr() == *addr)
.map(|a| {
a.name
.clone()
.as_deref()
.map(SharedString::from)
.unwrap_or_else(|| SharedString::from(a.id.clone()))
})
.unwrap_or_else(|| SharedString::from(addr.identifier.clone()));
(SharedString::from(format!("Forked from {name}")), true)
}
None => (upstream.display(), false),
None => (SharedString::from(upstream.display().as_str()), false),
};
let row = h_flex()
@@ -140,7 +140,8 @@ fn fork_candidates<'a>(
fn fork_display_name(announcement: &Announcement) -> SharedString {
announcement
.name
.clone()
.as_deref()
.map(SharedString::from)
.unwrap_or_else(|| SharedString::from(announcement.id.clone()))
}
+7 -4
View File
@@ -187,12 +187,14 @@ impl RepoListView {
let name = announcement
.name
.clone()
.unwrap_or_else(|| SharedString::from(announcement.id.clone()));
.as_deref()
.map(SharedString::from)
.unwrap_or(SharedString::from(announcement.id.clone()));
let description = announcement
.description
.clone()
.as_deref()
.map(SharedString::from)
.unwrap_or(SharedString::from("No description"));
let activity = last_activity
@@ -213,7 +215,8 @@ impl RepoListView {
.find(|a| a.addr() == *addr)
.map(|a| {
a.name
.clone()
.as_deref()
.map(SharedString::from)
.unwrap_or_else(|| SharedString::from(a.id.clone()))
})
.unwrap_or_else(|| SharedString::from(addr.identifier.clone()));
+2 -1
View File
@@ -347,7 +347,8 @@ impl SidebarPanel {
) -> impl IntoElement {
let name = announcement
.name
.clone()
.as_deref()
.map(SharedString::from)
.unwrap_or_else(|| SharedString::from(announcement.id.clone()));
let avatar = PixelAvatar::new(format!("{}:{}", announcement.owner, announcement.id));