mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-29 16:30:55 +00:00
feat: improve official columns
This commit is contained in:
parent
f3c52237fa
commit
cd31b99559
@ -1,6 +1,6 @@
|
||||
import { CheckCircleIcon } from "@lume/icons";
|
||||
import { ColumnRouteSearch } from "@lume/types";
|
||||
import { Column, User } from "@lume/ui";
|
||||
import { Column, Spinner, User } from "@lume/ui";
|
||||
import { createFileRoute, useRouter } from "@tanstack/react-router";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
@ -30,6 +30,7 @@ function Screen() {
|
||||
|
||||
const [title, setTitle] = useState<string>("Just a new group");
|
||||
const [users, setUsers] = useState<Array<string>>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isDone, setIsDone] = useState(false);
|
||||
|
||||
const toggleUser = (pubkey: string) => {
|
||||
@ -43,13 +44,22 @@ function Screen() {
|
||||
try {
|
||||
if (isDone) return router.history.push(redirect);
|
||||
|
||||
// start loading
|
||||
setLoading(true);
|
||||
|
||||
const groups = await ark.set_nstore(
|
||||
`lume_group_${label}`,
|
||||
JSON.stringify(users),
|
||||
);
|
||||
|
||||
if (groups) setIsDone(true);
|
||||
if (groups) {
|
||||
toast.success("Group has been created successfully.");
|
||||
// start loading
|
||||
setIsDone(true);
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
setLoading(false);
|
||||
toast.error(e);
|
||||
}
|
||||
};
|
||||
@ -101,14 +111,14 @@ function Screen() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="fixed z-10 flex items-center justify-center w-full bottom-3">
|
||||
<div className="fixed z-10 flex items-center justify-center w-full bottom-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={submit}
|
||||
disabled={users.length < 1}
|
||||
className="inline-flex items-center justify-center px-4 font-medium text-white transform bg-blue-500 rounded-full active:translate-y-1 w-36 h-11 hover:bg-blue-600 focus:outline-none disabled:cursor-not-allowed"
|
||||
className="inline-flex items-center justify-center px-4 font-medium text-white transform bg-blue-500 rounded-full active:translate-y-1 w-26 h-9 hover:bg-blue-600 focus:outline-none disabled:cursor-not-allowed"
|
||||
>
|
||||
{isDone ? "Back" : "Update"}
|
||||
{isDone ? "Back" : loading ? <Spinner /> : "Update"}
|
||||
</button>
|
||||
</div>
|
||||
</Column.Content>
|
||||
|
@ -18,6 +18,7 @@ export const Route = createFileRoute("/foryou")({
|
||||
beforeLoad: async ({ search, context }) => {
|
||||
const ark = context.ark;
|
||||
const interests = await ark.get_interest();
|
||||
const settings = await ark.get_settings();
|
||||
|
||||
if (!interests) {
|
||||
throw redirect({
|
||||
@ -31,6 +32,7 @@ export const Route = createFileRoute("/foryou")({
|
||||
|
||||
return {
|
||||
interests,
|
||||
settings,
|
||||
};
|
||||
},
|
||||
component: Screen,
|
||||
@ -48,7 +50,6 @@ export function Screen() {
|
||||
interests.hashtags,
|
||||
20,
|
||||
pageParam,
|
||||
true,
|
||||
);
|
||||
return events;
|
||||
},
|
||||
@ -87,13 +88,12 @@ export function Screen() {
|
||||
{data.map((item) => renderItem(item))}
|
||||
</Virtualizer>
|
||||
)}
|
||||
|
||||
{data?.length && hasNextPage ? (
|
||||
<div className="flex h-20 items-center justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => fetchNextPage()}
|
||||
disabled={isFetchingNextPage || isFetchingNextPage}
|
||||
disabled={isFetchingNextPage}
|
||||
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
|
||||
>
|
||||
{isFetchingNextPage ? (
|
||||
|
@ -17,7 +17,10 @@ export const Route = createFileRoute("/group")({
|
||||
},
|
||||
beforeLoad: async ({ search, context }) => {
|
||||
const ark = context.ark;
|
||||
const groups = await ark.get_nstore(`lume_group_${search.label}`);
|
||||
const groups = (await ark.get_nstore(
|
||||
`lume_group_${search.label}`,
|
||||
)) as string[];
|
||||
const settings = await ark.get_settings();
|
||||
|
||||
if (!groups) {
|
||||
throw redirect({
|
||||
@ -31,6 +34,7 @@ export const Route = createFileRoute("/group")({
|
||||
|
||||
return {
|
||||
groups,
|
||||
settings,
|
||||
};
|
||||
},
|
||||
component: Screen,
|
||||
@ -38,13 +42,13 @@ export const Route = createFileRoute("/group")({
|
||||
|
||||
export function Screen() {
|
||||
const { label, name, account } = Route.useSearch();
|
||||
const { ark } = Route.useRouteContext();
|
||||
const { ark, groups } = Route.useRouteContext();
|
||||
const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } =
|
||||
useInfiniteQuery({
|
||||
queryKey: [name, account],
|
||||
initialPageParam: 0,
|
||||
queryFn: async ({ pageParam }: { pageParam: number }) => {
|
||||
const events = await ark.get_events(20, pageParam);
|
||||
const events = await ark.get_events(20, pageParam, groups);
|
||||
return events;
|
||||
},
|
||||
getNextPageParam: (lastPage) => {
|
||||
|
@ -7,7 +7,6 @@ import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export const Route = createFileRoute("/interests")({
|
||||
component: Screen,
|
||||
validateSearch: (search: Record<string, string>): ColumnRouteSearch => {
|
||||
return {
|
||||
account: search.account,
|
||||
@ -15,6 +14,7 @@ export const Route = createFileRoute("/interests")({
|
||||
name: search.name,
|
||||
};
|
||||
},
|
||||
component: Screen,
|
||||
});
|
||||
|
||||
function Screen() {
|
||||
|
@ -6,8 +6,16 @@ export const Route = createFileRoute("/store/community")({
|
||||
|
||||
function Screen() {
|
||||
return (
|
||||
<div className="flex flex-col gap-3 p-3">
|
||||
<p>Coming Soon</p>
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 p-3">
|
||||
<div className="size-24 bg-blue-100 flex flex-col items-center justify-end overflow-hidden dark:bg-blue-900 rounded-full">
|
||||
<div className="w-12 h-16 bg-gradient-to-b from-blue-500 dark:from-blue-200 to-blue-50 dark:to-blue-900 rounded-t-lg" />
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<h1 className="font-semibold text-lg">Coming Soon</h1>
|
||||
<p className="text-sm text-neutral-700 dark:text-neutral-300 leading-tight">
|
||||
Enhance your experience <br /> by adding column shared by community.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -195,23 +195,20 @@ export class Ark {
|
||||
hashtags: string[],
|
||||
limit: number,
|
||||
asOf?: number,
|
||||
global?: boolean,
|
||||
) {
|
||||
let until: string = undefined;
|
||||
if (asOf && asOf > 0) until = asOf.toString();
|
||||
|
||||
const dedup = true;
|
||||
const seenIds = new Set<string>();
|
||||
const dedupQueue = new Set<string>();
|
||||
const nostrTags = hashtags.map((tag) => tag.replace("#", "").toLowerCase());
|
||||
|
||||
const nostrEvents: Event[] = await invoke("get_events_from_interests", {
|
||||
hashtags,
|
||||
hashtags: nostrTags,
|
||||
limit,
|
||||
until,
|
||||
global,
|
||||
});
|
||||
|
||||
if (dedup) {
|
||||
for (const event of nostrEvents) {
|
||||
const tags = event.tags
|
||||
.filter((el) => el[0] === "e")
|
||||
@ -233,9 +230,6 @@ export class Ark {
|
||||
.sort((a, b) => b.created_at - a.created_at);
|
||||
}
|
||||
|
||||
return nostrEvents.sort((a, b) => b.created_at - a.created_at);
|
||||
}
|
||||
|
||||
public async publish(
|
||||
content: string,
|
||||
reply_to?: string,
|
||||
|
@ -126,7 +126,7 @@ export function NoteContent({
|
||||
);
|
||||
|
||||
if (compact) {
|
||||
parsedContent = reactStringReplace(parsedContent, /[\r\n]{2,}/g, () => (
|
||||
parsedContent = reactStringReplace(parsedContent, /\n*\n/g, () => (
|
||||
<div key={nanoid()} className="h-1.5" />
|
||||
));
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export function MentionNote({
|
||||
return (
|
||||
<div
|
||||
contentEditable={false}
|
||||
className="my-1 flex w-full cursor-default items-center justify-between rounded-2xl border border-black/10 p-3 dark:border-white/10"
|
||||
className="my-1 flex w-full cursor-default items-center justify-between rounded-xl border border-black/10 p-3 dark:border-white/10"
|
||||
>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
@ -30,7 +30,7 @@ export function MentionNote({
|
||||
return (
|
||||
<div
|
||||
contentEditable={false}
|
||||
className="my-1 w-full cursor-default rounded-2xl border border-black/10 p-3 dark:border-white/10"
|
||||
className="my-1 w-full cursor-default rounded-xl border border-black/10 p-3 dark:border-white/10"
|
||||
>
|
||||
{t("note.error")}
|
||||
</div>
|
||||
|
@ -19,16 +19,6 @@
|
||||
"author": "Lume",
|
||||
"description": "Collective of people you're interested in."
|
||||
},
|
||||
{
|
||||
"label": "sDbO6XxAGnW5XuUZEgZMn",
|
||||
"name": "Antenas",
|
||||
"content": "/antenas",
|
||||
"logo": "",
|
||||
"cover": "/antenas.png",
|
||||
"coverRetina": "/antenas@2x.png",
|
||||
"author": "Lume",
|
||||
"description": "Keep track to specific content."
|
||||
},
|
||||
{
|
||||
"label": "gxtcIbgD8YNPbeI5o92I8",
|
||||
"name": "Trending",
|
||||
|
@ -155,7 +155,6 @@ pub async fn get_events_from_interests(
|
||||
hashtags: Vec<&str>,
|
||||
limit: usize,
|
||||
until: Option<&str>,
|
||||
global: Option<bool>,
|
||||
state: State<'_, Nostr>,
|
||||
) -> Result<Vec<Event>, String> {
|
||||
let client = &state.client;
|
||||
@ -163,34 +162,11 @@ pub async fn get_events_from_interests(
|
||||
Some(until) => Timestamp::from_str(until).unwrap(),
|
||||
None => Timestamp::now(),
|
||||
};
|
||||
let authors = match global {
|
||||
Some(val) => match val {
|
||||
true => None,
|
||||
false => {
|
||||
match client
|
||||
.get_contact_list_public_keys(Some(Duration::from_secs(10)))
|
||||
.await
|
||||
{
|
||||
Ok(val) => Some(val),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let filter = match authors {
|
||||
Some(val) => Filter::new()
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.authors(val)
|
||||
.limit(limit)
|
||||
.until(as_of)
|
||||
.hashtags(hashtags),
|
||||
None => Filter::new()
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.limit(limit)
|
||||
.until(as_of)
|
||||
.hashtags(hashtags),
|
||||
};
|
||||
.hashtags(hashtags);
|
||||
|
||||
if let Ok(events) = client
|
||||
.get_events_of(vec![filter], Some(Duration::from_secs(15)))
|
||||
|
Loading…
Reference in New Issue
Block a user