Compare commits

...
6 Commits
Author SHA1 Message Date
reya b0c49c5141 chore: release 2024-11-10 13:23:57 +07:00
reya 2ed2cb3afd chore: update dependencies 2024-11-10 10:43:38 +07:00
reya 2bcda1f2ef feat: support NIP22 for comment 2024-11-10 08:51:07 +07:00
reya ca20bbd298 chore: release 2024-11-08 09:19:51 +07:00
reya c6da06cd4d chore: clean up 2024-11-08 09:02:57 +07:00
reya 50bf6c04c1 chore: use upstream nostr-sdk instead of fork 2024-11-08 07:51:48 +07:00
18 changed files with 1195 additions and 1197 deletions
+18 -18
View File
@@ -20,38 +20,38 @@
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.3",
"@tanstack/query-broadcast-client-experimental": "^5.59.16",
"@tanstack/query-persist-client-core": "^5.59.16",
"@tanstack/react-query": "^5.59.16",
"@tanstack/react-router": "^1.77.5",
"@tauri-apps/api": "^2.0.3",
"@tanstack/query-broadcast-client-experimental": "^5.59.20",
"@tanstack/query-persist-client-core": "^5.59.20",
"@tanstack/react-query": "^5.59.20",
"@tanstack/react-router": "^1.81.0",
"@tauri-apps/api": "^2.1.0",
"@tauri-apps/plugin-clipboard-manager": "^2.0.0",
"@tauri-apps/plugin-dialog": "^2.0.1",
"@tauri-apps/plugin-fs": "^2.0.1",
"@tauri-apps/plugin-fs": "^2.0.2",
"@tauri-apps/plugin-http": "^2.0.1",
"@tauri-apps/plugin-os": "^2.0.0",
"@tauri-apps/plugin-process": "^2.0.0",
"@tauri-apps/plugin-shell": "^2.0.1",
"@tauri-apps/plugin-store": "github:tauri-apps/tauri-plugin-store#a564510",
"@tauri-apps/plugin-updater": "^2.0.0",
"@tauri-apps/plugin-upload": "^2.0.0",
"@tauri-apps/plugin-upload": "^2.1.0",
"@tauri-apps/plugin-window-state": "^2.0.0",
"bitcoin-units": "^1.0.0",
"dayjs": "^1.11.13",
"embla-carousel-react": "^8.3.0",
"i18next": "^23.16.4",
"embla-carousel-react": "^8.3.1",
"i18next": "^23.16.5",
"i18next-resources-to-backend": "^1.2.1",
"light-bolt11-decoder": "^3.2.0",
"minidenticons": "^4.2.1",
"nanoid": "^5.0.7",
"nostr-tools": "^2.9.4",
"nanoid": "^5.0.8",
"nostr-tools": "^2.10.1",
"react": "19.0.0-rc-cae764ce-20241025",
"react-currency-input-field": "^3.8.0",
"react-dom": "19.0.0-rc-cae764ce-20241025",
"react-hook-form": "^7.53.1",
"react-i18next": "^15.1.0",
"react-hook-form": "^7.53.2",
"react-i18next": "^15.1.1",
"react-string-replace": "^1.1.1",
"rich-textarea": "^0.26.3",
"rich-textarea": "^0.26.4",
"use-debounce": "^10.0.4",
"virtua": "^0.34.2"
},
@@ -59,9 +59,9 @@
"@biomejs/biome": "^1.9.4",
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"@tanstack/router-devtools": "^1.77.5",
"@tanstack/router-plugin": "^1.76.4",
"@tauri-apps/cli": "^2.0.4",
"@tanstack/router-devtools": "^1.81.0",
"@tanstack/router-plugin": "^1.79.0",
"@tauri-apps/cli": "^2.1.0",
"@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
"@vitejs/plugin-react": "^4.3.3",
@@ -76,7 +76,7 @@
"tailwindcss-content-visibility": "^1.0.0",
"typescript": "^5.6.3",
"vite": "^5.4.10",
"vite-tsconfig-paths": "^5.0.1"
"vite-tsconfig-paths": "^5.1.1"
},
"overrides": {
"@types/react": "npm:types-react@rc",
+300 -300
View File
File diff suppressed because it is too large Load Diff
+761 -767
View File
File diff suppressed because it is too large Load Diff
+7 -5
View File
@@ -33,9 +33,6 @@ tauri-plugin-theme = "2.1.2"
tauri-plugin-decorum = { git = "https://github.com/clearlysid/tauri-plugin-decorum" }
tauri-specta = { version = "2.0.0-rc.15", features = ["derive", "typescript"] }
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb", "webln", "all-nips"] }
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }
specta = "^2.0.0-rc.20"
specta-typescript = "0.0.7"
tokio = { version = "1", features = ["full"] }
@@ -49,10 +46,15 @@ regex = "1.10.4"
keyring = { version = "3", features = ["apple-native", "windows-native"] }
keyring-search = { git = "https://github.com/reyamir/keyring-search" }
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
async-trait = "0.1.83"
webbrowser = "1.0.2"
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb", "webln", "all-nips"] }
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }
[patch.'https://github.com/rust-nostr/nostr']
nostr-sdk = { git = "https://github.com/reyamir/nostr", branch = "feat/open-auth-url", features = ["lmdb", "webln", "all-nips"] }
nostr-connect = { git = "https://github.com/reyamir/nostr", branch = "feat/open-auth-url" }
nostr-sdk = { git = "https://github.com/reyamir/nostr", branch = "feat/nip-22", features = ["lmdb", "webln", "all-nips"] }
nostr-connect = { git = "https://github.com/reyamir/nostr", branch = "feat/nip-22" }
[target.'cfg(target_os = "macos")'.dependencies]
border = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
+2 -2
View File
@@ -37,7 +37,7 @@
],
"definitions": {
"Capability": {
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```",
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
"type": "object",
"required": [
"identifier",
@@ -84,7 +84,7 @@
}
},
"permissions": {
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ```",
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionEntry"
+2 -2
View File
@@ -37,7 +37,7 @@
],
"definitions": {
"Capability": {
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```",
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
"type": "object",
"required": [
"identifier",
@@ -84,7 +84,7 @@
}
},
"permissions": {
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ```",
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionEntry"
+28 -5
View File
@@ -14,6 +14,17 @@ struct Account {
nostr_connect: Option<String>,
}
#[derive(Debug, Clone)]
struct AuthHandler;
#[async_trait::async_trait]
impl AuthUrlHandler for AuthHandler {
async fn on_auth_url(&self, auth_url: Url) -> Result<()> {
webbrowser::open(auth_url.as_str())?;
Ok(())
}
}
#[tauri::command]
#[specta::specta]
pub fn get_accounts() -> Vec<String> {
@@ -94,19 +105,29 @@ pub async fn connect_account(uri: String, state: State<'_, Nostr>) -> Result<Str
let remote_npub = remote_user.to_bech32().map_err(|err| err.to_string())?;
// Init nostr connect
let nostr_connect = NostrConnect::new(bunker_uri, app_keys, Duration::from_secs(120), None)
let mut nostr_connect = NostrConnect::new(bunker_uri, app_keys, Duration::from_secs(120), None)
.map_err(|err| err.to_string())?;
let bunker_uri = nostr_connect
// Handle auth url
nostr_connect.auth_url_handler(AuthHandler);
let keyring = Entry::new("Lume Safe Storage", &remote_npub).map_err(|err| err.to_string())?;
let reuse_bunker = nostr_connect
.bunker_uri()
.await
.map_err(|err| err.to_string())?;
let keyring = Entry::new("Lume Safe Storage", &remote_npub).map_err(|err| err.to_string())?;
let mut reuse_uri = reuse_bunker.to_string();
if let Some(secret) = reuse_bunker.secret() {
let replace = format!("&secret={}", secret);
reuse_uri = reuse_uri.replace(replace.as_str(), "");
}
let account = Account {
secret_key: app_secret,
nostr_connect: Some(bunker_uri.to_string()),
nostr_connect: Some(reuse_uri),
};
// Save secret key to keyring
@@ -217,7 +238,9 @@ pub async fn set_signer(
let app_keys = Keys::from_str(&account.secret_key).map_err(|e| e.to_string())?;
match NostrConnect::new(uri, app_keys, Duration::from_secs(120), None) {
Ok(signer) => {
Ok(mut signer) => {
// Handle auth url
signer.auth_url_handler(AuthHandler);
// Update signer
client.set_signer(signer).await;
// Emit to front-end
+22 -30
View File
@@ -71,11 +71,11 @@ pub async fn get_meta_from_event(content: String) -> Result<Meta, ()> {
#[specta::specta]
pub async fn get_replies(id: String, state: State<'_, Nostr>) -> Result<Vec<RichEvent>, String> {
let client = &state.client;
let event_id = EventId::parse(&id).map_err(|err| err.to_string())?;
let event_id = EventId::parse(&id).map_err(|err| err.to_string())?;
let filter = Filter::new()
.kinds(vec![Kind::TextNote, Kind::Custom(1111)])
.event(event_id);
.kind(Kind::Comment)
.custom_tag(SingleLetterTag::uppercase(Alphabet::E), [event_id]);
let mut events = Events::new(&[filter.clone()]);
@@ -523,39 +523,31 @@ pub async fn reply(content: String, to: String, state: State<'_, Nostr>) -> Resu
Err(e) => return Err(e.to_string()),
};
// Detect root event from reply
let root_ids: Vec<&EventId> = reply_to
// Find root event from reply
let root_tag = reply_to
.tags
.filter_standardized(TagKind::e())
.filter_map(|t| match t {
TagStandard::Event {
event_id, marker, ..
} => {
if let Some(mkr) = marker {
match mkr {
Marker::Root => Some(event_id),
Marker::Reply => Some(event_id),
_ => None,
}
} else {
Some(event_id)
}
}
_ => None,
})
.collect();
.find(TagKind::SingleLetter(SingleLetterTag::uppercase(
Alphabet::E,
)));
// Get root event if exist
let root = match root_ids.first() {
Some(&id) => client
.database()
.event_by_id(id)
.await
.map_err(|err| err.to_string())?,
let root = match root_tag {
Some(tag) => match tag.content() {
Some(content) => {
let id = EventId::parse(content).map_err(|err| err.to_string())?;
client
.database()
.event_by_id(&id)
.await
.map_err(|err| err.to_string())?
}
None => None,
},
None => None,
};
let builder = EventBuilder::text_note_reply(content, &reply_to, root.as_ref(), None)
let builder = EventBuilder::comment(content, &reply_to, root.as_ref(), None)
.add_tags(tags)
.pow(DEFAULT_DIFFICULTY);
+18 -19
View File
@@ -105,28 +105,27 @@ pub async fn create_column(
});
}
} else if let Ok(event_id) = EventId::parse(&id) {
let is_thread = payload.url().to_string().contains("events");
tauri::async_runtime::spawn(async move {
let state = webview.state::<Nostr>();
let client = &state.client;
if is_thread {
tauri::async_runtime::spawn(async move {
let state = webview.state::<Nostr>();
let client = &state.client;
let subscription_id = SubscriptionId::new(webview.label());
let subscription_id = SubscriptionId::new(webview.label());
let filter = Filter::new()
.custom_tag(
SingleLetterTag::uppercase(Alphabet::E),
[event_id],
)
.kind(Kind::Comment)
.since(Timestamp::now());
let filter = Filter::new()
.event(event_id)
.kinds(vec![Kind::TextNote, Kind::Custom(1111)])
.since(Timestamp::now());
if let Err(e) = client
.subscribe_with_id(subscription_id, vec![filter], None)
.await
{
println!("Subscription error: {}", e);
}
});
}
if let Err(e) = client
.subscribe_with_id(subscription_id, vec![filter], None)
.await
{
println!("Subscription error: {}", e);
}
});
}
}
}
+18 -17
View File
@@ -56,13 +56,6 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
// Get words
let words: Vec<_> = content.split_whitespace().collect();
// Get mentions
let mentions = words
.iter()
.filter(|&&word| ["nostr:", "@"].iter().any(|&el| word.starts_with(el)))
.map(|&s| s.to_string())
.collect::<Vec<_>>();
// Get hashtags
let hashtags = words
.iter()
@@ -70,6 +63,13 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
.map(|&s| s.to_string().replace("#", "").to_lowercase())
.collect::<Vec<_>>();
// Get mentions
let mentions = words
.iter()
.filter(|&&word| ["nostr:", "@"].iter().any(|&el| word.starts_with(el)))
.map(|&s| s.to_string())
.collect::<Vec<_>>();
for mention in mentions {
let entity = mention.replace("nostr:", "").replace('@', "");
@@ -92,8 +92,11 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
}
if entity.starts_with("note") {
if let Ok(event_id) = EventId::from_bech32(&entity) {
let hex = event_id.to_hex();
let tag = Tag::parse(&["e", &hex, "", "mention"]).unwrap();
let tag = Tag::from_standardized(TagStandard::Quote {
event_id,
relay_url: None,
public_key: None,
});
tags.push(tag);
} else {
continue;
@@ -101,14 +104,12 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
}
if entity.starts_with("nevent") {
if let Ok(event) = Nip19Event::from_bech32(&entity) {
let hex = event.event_id.to_hex();
let relay = event.clone().relays.into_iter().next().unwrap_or("".into());
let tag = Tag::parse(&["e", &hex, &relay, "mention"]).unwrap();
if let Some(author) = event.author {
let tag = Tag::public_key(author);
tags.push(tag);
}
let relay_url = event.relays.first().map(UncheckedUrl::from);
let tag = Tag::from_standardized(TagStandard::Quote {
event_id: event.event_id,
relay_url,
public_key: event.author,
});
tags.push(tag);
} else {
+4 -4
View File
@@ -236,8 +236,8 @@ fn main() {
// Config
let opts = Options::new()
.gossip(true)
.max_avg_latency(Duration::from_millis(500))
.timeout(Duration::from_secs(5));
.max_avg_latency(Duration::from_secs(2))
.timeout(Duration::from_secs(10));
// Setup nostr client
let client = ClientBuilder::default()
@@ -532,7 +532,7 @@ fn main() {
if let Err(e) = handle_clone.emit("metadata", event.as_json()) {
println!("Emit error: {}", e)
}
} else if event.kind == Kind::TextNote {
} else if event.kind == Kind::Comment {
let payload = RichEvent {
raw: event.as_json(),
parsed: if event.kind == Kind::TextNote {
@@ -544,7 +544,7 @@ fn main() {
if let Err(e) = handle_clone.emit_to(
EventTarget::labeled(subscription_id.to_string()),
"event",
"comment",
payload,
) {
println!("Emit error: {}", e)
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "Lume",
"version": "24.11.5",
"version": "24.11.7",
"identifier": "nu.lume.Lume",
"build": {
"beforeDevCommand": "pnpm dev",
+3 -5
View File
@@ -1,12 +1,12 @@
/* prettier-ignore-start */
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file is auto-generated by TanStack Router
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { createFileRoute } from '@tanstack/react-router'
@@ -1036,8 +1036,6 @@ export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
/* prettier-ignore-end */
/* ROUTE_MANIFEST_START
{
"routes": {
+5 -10
View File
@@ -99,14 +99,9 @@ function ReplyList() {
const res = await commands.getReplies(id);
if (res.status === "ok") {
const events = res.data
// Create Lume Events
.map((item) => LumeEvent.from(item.raw, item.parsed))
// Filter quote
.filter(
(ev) =>
!ev.tags.filter((t) => t[0] === "q" || t[3] === "mention").length,
);
const events = res.data.map((item) =>
LumeEvent.from(item.raw, item.parsed),
);
return events;
} else {
@@ -179,7 +174,7 @@ function ReplyList() {
useEffect(() => {
const unlisten = getCurrentWindow().listen<EventPayload>(
"event",
"comment",
async (data) => {
const event = LumeEvent.from(data.payload.raw, data.payload.parsed);
@@ -216,7 +211,7 @@ function ReplyList() {
{isLoading ? (
<div className="flex items-center justify-center gap-2">
<Spinner className="size-4" />
<span className="text-sm font-medium">Getting replies...</span>
<span className="text-sm font-medium">Loading replies...</span>
</div>
) : (
<div className="flex flex-col gap-3">
+1 -7
View File
@@ -1,9 +1,3 @@
import { commands } from "@/commands.gen";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/columns/_layout/events/$id")({
beforeLoad: async () => {
const accounts = await commands.getAccounts();
return { accounts };
},
});
export const Route = createFileRoute("/columns/_layout/events/$id")();
@@ -73,7 +73,7 @@ function Newsfeeds() {
: item.tags.find((tag) => tag[0] === "title")?.[1] || "Unnamed";
const label =
item.kind === 3
? `newsfeed-${id.slice(0, 5)}`
? `newsfeed-${item.pubkey.slice(0, 5)}`
: item.tags.find((tag) => tag[0] === "d")?.[1] || nanoid();
return (
@@ -120,19 +120,19 @@ function Screen() {
>
<Tabs.List className="h-11 shrink-0 flex items-center">
<Tabs.Trigger
className="flex-1 inline-flex h-11 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-neutral-100 dark:border-neutral-900 data-[state=active]:border-neutral-200 dark:data-[state=active]:border-neutral-800 data-[state=inactive]:opacity-50"
className="flex-1 inline-flex h-11 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-neutral-100 dark:border-neutral-700 data-[state=active]:border-neutral-200 dark:data-[state=active]:border-neutral-600 data-[state=inactive]:opacity-50"
value="replies"
>
Replies
</Tabs.Trigger>
<Tabs.Trigger
className="flex-1 inline-flex h-11 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-neutral-100 dark:border-neutral-900 data-[state=active]:border-neutral-200 dark:data-[state=active]:border-neutral-800 data-[state=inactive]:opacity-50"
className="flex-1 inline-flex h-11 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-neutral-100 dark:border-neutral-700 data-[state=active]:border-neutral-200 dark:data-[state=active]:border-neutral-600 data-[state=inactive]:opacity-50"
value="reactions"
>
Reactions
</Tabs.Trigger>
<Tabs.Trigger
className="flex-1 inline-flex h-11 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-neutral-100 dark:border-neutral-900 data-[state=active]:border-neutral-200 dark:data-[state=active]:border-neutral-800 data-[state=inactive]:opacity-50"
className="flex-1 inline-flex h-11 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-neutral-100 dark:border-neutral-700 data-[state=active]:border-neutral-200 dark:data-[state=active]:border-neutral-600 data-[state=inactive]:opacity-50"
value="zaps"
>
Zaps
+1 -1
View File
@@ -70,7 +70,7 @@ export const Route = createLazyFileRoute("/new-post/")({
function Screen() {
const { reply_to } = Route.useSearch();
const { accounts, initialValue, queryClient } = Route.useRouteContext();
const { accounts, initialValue } = Route.useRouteContext();
const { deferMentionList } = Route.useLoaderData();
const users = useAwaited({ promise: deferMentionList })[0];