Skip to content

Commit

Permalink
added Archived folder to ui and finalized SQL queries for archived items
Browse files Browse the repository at this point in the history
  • Loading branch information
cool-ant committed Sep 28, 2024
1 parent 98284b6 commit 7c6c778
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 11 deletions.
5 changes: 5 additions & 0 deletions rust/psibase/src/services/sites.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
mod service {
use crate::{http::HttpRequest, Hex};

#[action]
fn enableSpa(enable: bool) {
unimplemented!()
}

#[action]
fn serveSys(request: HttpRequest) -> Option<crate::http::HttpReply> {
unimplemented!()
Expand Down
58 changes: 49 additions & 9 deletions services/user/Chainmail/service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ fn validate_user(user: &str) -> bool {
}

fn make_query(req: &HttpRequest, sql: String) -> HttpRequest {
//events.psibase.???
return HttpRequest {
host: req.host.clone(),
rootHost: req.rootHost.clone(),
Expand Down Expand Up @@ -74,23 +75,58 @@ fn serve_rest_api(request: &HttpRequest) -> Option<HttpReply> {
return None;
}

let mut where_clause: String = String::from("WHERE ");
let archived_requested = match params.get(&String::from("archived")) {
Some(arch) => arch == "true",
None => false,
};

let mut where_clause_sender_receiver: String = String::from("");
if s_opt.is_some() {
where_clause += s_clause.as_str();
where_clause_sender_receiver += s_clause.as_str();
}
if s_opt.is_some() && r_opt.is_some() {
where_clause += " AND ";
where_clause_sender_receiver += " AND ";
}
if r_opt.is_some() {
where_clause += r_clause.as_str();
where_clause_sender_receiver += r_clause.as_str();
}

let mq = make_query(
request,
format!("SELECT *
FROM \"history.chainmail.sent\" AS sent
LEFT JOIN \"history.chainmail.archive\" AS archive ON CONCAT(sent.receiver, sent.rowid) = archive.event_id {} ORDER BY ROWID", where_clause),
/* TASKS
* x- clean this up so chainmail shows sent/received messages instead of []
* x- send a couple of archive actions so 'alice test 2' and 'bob test 1' are archived
* x- tweak this sql until I can retrieve archived an not archived messages properly
*/

// let archived_msgs_query = "SELECT DISTINCT sent.rowid as msg_id, sent.* FROM \"history.chainmail.sent\" AS sent INNER JOIN \"history.chainmail.archive\" AS archive ON CONCAT(sent.receiver, sent.rowid) = archive.event_id";
// let not_archvied_msgs_query = "SELECT DISTINCT sent.rowid as msg_id, archive.event_id, sent.* FROM \"history.chainmail.sent\" AS sent LEFT JOIN \"history.chainmail.archive\" AS archive ON CONCAT(sent.receiver, sent.rowid) = archive.event_id WHERE event_id IS NULL";

// Select from all sent emails *not archived* where receiver/send are as query params specify
let select_clause = format!("DISTINCT sent.rowid as msg_id, archive.event_id, sent.*");
let from_clause = format!("\"history.chainmail.sent\" AS sent LEFT JOIN \"history.chainmail.archive\" AS archive ON CONCAT(sent.receiver, sent.rowid) = archive.event_id" );
let where_clause_archived_or_not = format!(
"archive.event_id IS {} NULL",
if archived_requested { "NOT" } else { "" }
);
let order_by_clause = "sent.ROWID";

let sql_query_str = format!(
"SELECT {} FROM {} WHERE {} {} {} ORDER BY {}",
select_clause,
from_clause,
where_clause_archived_or_not,
if s_opt.is_some() || r_opt.is_some() {
"AND"
} else {
""
},
where_clause_sender_receiver,
order_by_clause
);

let mq = make_query(request, sql_query_str.clone());

println!("query: {}", sql_query_str);

return REventsSvc::call().serveSys(mq);
}
return None;
Expand All @@ -102,6 +138,7 @@ mod service {
use serde::{Deserialize, Serialize};
use services::accounts::Wrapper as AccountsSvc;
use services::events::Wrapper as EventsSvc;
// use services::sites::Wrapper as SitesSvc;

use crate::serve_rest_api;

Expand All @@ -125,6 +162,9 @@ mod service {
);
table.put(&InitRow {}).unwrap();

// Register as SPA
// SitesSvc::call().

EventsSvc::call().setSchema(create_schema::<Wrapper>());
EventsSvc::call().addIndex(DbId::HistoryEvent, SERVICE, MethodNumber::from("sent"), 0);
EventsSvc::call().addIndex(DbId::HistoryEvent, SERVICE, MethodNumber::from("sent"), 1);
Expand Down
26 changes: 26 additions & 0 deletions services/user/Chainmail/ui/src/components/mail-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { formatDistanceToNow } from "date-fns";
import { cn } from "@lib/utils";
import { ScrollArea } from "@shadcn/scroll-area";
import { Tooltip, TooltipContent, TooltipTrigger } from "@shadcn/tooltip";
import { Archive } from "lucide-react";
import { Button } from "@shadcn/button";
import { type Message } from "@hooks";
import { toast } from "sonner";
import { getSupervisor } from "@lib/supervisor";

interface SharedProps {
mailbox: Mailbox;
Expand All @@ -17,6 +21,20 @@ interface MailListProps extends SharedProps {
messages: Message[];
}

const onArchive = async (itemId: string) => {
console.info(`onArchive.top(itemid[${itemId}])`);
let id = parseInt(itemId);
const supervisor = await getSupervisor();
// TODO: Improve error detection. This promise resolves with success before the transaction is pushed.
await supervisor.functionCall({
service: "chainmail",
intf: "api",
method: "archive",
params: [id],
});
toast.success("Your message has been archived");
};

export function MailList({
mailbox,
messages,
Expand Down Expand Up @@ -85,6 +103,14 @@ const MailItem = ({
<span className="flex h-2 w-2 rounded-full bg-blue-600" />
)} */}
</div>
<Button
variant="ghost"
size="icon"
onClick={() => onArchive(item.msgId)}
>
<Archive className="h-4 w-4" />
<span className="sr-only">Archive</span>
</Button>
<div
className={cn(
"ml-auto text-xs",
Expand Down
8 changes: 7 additions & 1 deletion services/user/Chainmail/ui/src/components/nav-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PencilLine, Send, Inbox } from "lucide-react";
import { PencilLine, Send, Inbox, Archive } from "lucide-react";

import { Separator } from "@shadcn/separator";
import { cn } from "@lib/utils";
Expand Down Expand Up @@ -54,6 +54,12 @@ export const NavMenu = ({ isCollapsed = false }: Props) => {
variant: at === "/sent" ? "default" : "ghost",
href: "/sent",
},
{
title: "Archived",
icon: Send,
variant: at === "/archived" ? "default" : "ghost",
href: "/archived",
},
]}
/>
</>
Expand Down
32 changes: 32 additions & 0 deletions services/user/Chainmail/ui/src/hooks/use-mail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export function useCompose() {

export type Message = {
id: string;
msgId: string;
from: string;
to: string;
datetime: number;
Expand All @@ -26,13 +27,17 @@ type RawMessage = {
receiver: string;
subject: string;
body: string;
msg_id: string;
};

const transformRawMessagesToMessages = (rawMessages: RawMessage[]) => {
console.info("transformRawMessagesToMessages().top; rawMessagses:");
console.info(rawMessages);
return rawMessages.reverse().map(
(msg, i) =>
({
id: `${msg.sender}-${msg.receiver}-${msg.subject}-${msg.body}`,
msgId: msg.msg_id,
from: msg.sender,
to: msg.receiver,
datetime: Date.now() - i * 1_000_000,
Expand Down Expand Up @@ -72,6 +77,33 @@ export function useIncomingMessages() {
};
}

const getArchivedMessages = async (account: string) => {
const res = await fetch(`/messages?archived=true&receiver=${account}`);
const rawMessages = (await res.json()) as RawMessage[];
return transformRawMessagesToMessages(rawMessages);
};

const archivedMsgAtom = atom<Message["id"]>("");
export function useArchivedMessages() {
const { user } = useUser();
const query = useQuery({
queryKey: ["archived", user],
queryFn: () => getArchivedMessages(user),
enabled: Boolean(user),
});

const [selectedMessageId, setSelectedMessageId] = useAtom(archivedMsgAtom);
const selectedMessage = query.data?.find(
(msg) => msg.id === selectedMessageId,
);

return {
query,
selectedMessage,
setSelectedMessageId,
};
}

const getSentMessages = async (account: string) => {
const res = await fetch(`/messages?sender=${account}`);
const rawMessages = (await res.json()) as RawMessage[];
Expand Down
6 changes: 6 additions & 0 deletions services/user/Chainmail/ui/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { TooltipProvider } from "@shadcn/tooltip";
import DefaultLayout from "./layouts/default";

import "./styles/globals.css";
import Archived from "@routes/archived";

const queryClient = new QueryClient();

Expand All @@ -33,6 +34,11 @@ const router = createBrowserRouter([
element: <Sent />,
children: [],
},
{
path: "/archived",
element: <Archived />,
children: [],
},
],
},
{
Expand Down
56 changes: 56 additions & 0 deletions services/user/Chainmail/ui/src/routes/archived.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@shadcn/resizable";
import { Separator } from "@shadcn/separator";

import { MailDisplay } from "@components/mail-display";
import { MailList } from "@components/mail-list";

import { useArchivedMessages } from "@hooks";
import { useEffect } from "react";
import { EmptyBox, ModeToggle } from "@components";

export function Archived() {
const { query, selectedMessage, setSelectedMessageId } =
useArchivedMessages();

useEffect(() => {
setSelectedMessageId("");
}, []);

return (
<ResizablePanelGroup
direction="horizontal"
className="items-stretch"
autoSaveId="post-list-panels"
>
<ResizablePanel minSize={30} id="list" order={3}>
<div className="flex h-[56px] items-center justify-between px-4">
<h1 className="text-xl font-bold">Archived</h1>
<ModeToggle />
</div>
<Separator />
<div className="h-full py-4">
{query.data?.length ? (
<MailList
mailbox="archived"
messages={query.data ?? []}
selectedMessage={selectedMessage}
setSelectedMessageId={setSelectedMessageId}
/>
) : (
<EmptyBox>No archived messages</EmptyBox>
)}
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel id="message" order={4}>
<MailDisplay message={selectedMessage} mailbox="archived" />
</ResizablePanel>
</ResizablePanelGroup>
);
}

export default Archived;
2 changes: 1 addition & 1 deletion services/user/Chainmail/ui/src/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export type Mailbox = "inbox" | "sent" | "drafts";
export type Mailbox = "inbox" | "sent" | "drafts" | "archived";

0 comments on commit 7c6c778

Please sign in to comment.