Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions frontend/packages/app/src/lib/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
danger?: boolean;
nav?: boolean;
to?: string;
realLink?: boolean;
ref?: RefObject<HTMLButtonElement | HTMLAnchorElement | null>;
children: React.ReactNode;
}

function Button({wide, thin, center, primary, danger, nav, to, ref, children, ...props}: ButtonProps) {
function Button({wide, thin, center, primary, danger, nav, to, realLink, ref, children, ...props}: ButtonProps) {
const navigate = useNavigate();

if (to !== undefined) {
props.onClick = (e) => {
e.preventDefault() // annoying hack
navigate({to: to})
if (!realLink) {
props.onClick = (e) => {
e.preventDefault() // annoying hack
navigate({to: to})
}
}

return (
Expand Down
4 changes: 4 additions & 0 deletions frontend/packages/app/src/lib/components/Container.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
}
}

&.wrap {
flex-wrap: wrap;
}

&.center {
width: 100%;
align-items: center;
Expand Down
5 changes: 3 additions & 2 deletions frontend/packages/app/src/lib/components/Container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ import * as React from "react";
import './Container.scss'

function Container(
{gap, align, border, padding, clazz, fill, children, ...props}:
{gap, align, border, padding, clazz, fill, wrap, children, ...props}:
{
gap?: "sm" | "md" | "lg" | "xl" | undefined,
align?: 'center' | 'left' | 'right' | 'horizontal' | 'horizontalCenter' | 'horizontalRight' | 'startHorizontal',
border?: "top" | "bottom" | undefined,
padding?: string | undefined,
clazz?: string,
fill?: boolean,
wrap?: boolean,
children: React.ReactNode,
props?: never
}
) {
return (
<div
className={`container${gap ? " gap-" + gap : ""}${align ? " " + align : ""}${border ? " border-" + border : ""}${clazz ? " " + clazz : ""}${fill ? " fill" : ""}`}
className={`container${gap ? " gap-" + gap : ""}${align ? " " + align : ""}${border ? " border-" + border : ""}${clazz ? " " + clazz : ""}${fill ? " fill" : ""}${wrap ? " wrap" : ""}`}
style={padding ? `padding:${padding};` : ""}
{...props}
>
Expand Down
15 changes: 14 additions & 1 deletion frontend/packages/app/src/lib/components/DriveFile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,24 @@
background-color: var(--bg-3-50);
}

img {
img, .preview {
display: flex;
align-items: center;
justify-content: center;

height: 115px;
width: 115px;
object-fit: cover;

border-radius: var(--br-sm);
background-color: var(--bg-3);
color: var(--tx-3);
}

.name {
max-width: 110px;
text-wrap: wrap;
word-break: break-all;
text-align: center;
}
}
45 changes: 43 additions & 2 deletions frontend/packages/app/src/lib/components/DriveFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,56 @@ import {Api} from "aster-common";
import './DriveFile.scss'
import Container from "./Container.tsx";
import Button from "./Button.tsx";
import {IconPencil, IconTrash} from "@tabler/icons-react";
import {
IconCoffee,
IconFile,
IconFileZip,
IconPencil,
IconQuestionMark,
IconTrash,
IconVideo
} from "@tabler/icons-react";
import * as React from "react";

function DriveFile({data}: { data: common.DriveFile }) {
const [hidden, setHidden] = React.useState(false);

function renderPreview() {
const type = data.type
if (type.startsWith("image")) {
return <img src={data.src} alt={data.alt}/>
} else if (type.startsWith("video")) {
return <div className={"preview"}>
<IconVideo size={30}/>
</div>
} else if (type.startsWith("application/zip")) {
return <div className={"preview"}>
<IconFileZip size={30}/>
</div>
} else if (type.startsWith("application/java-archive")) {
return <div className={"preview"}>
<IconCoffee size={30}/>
</div>
} else if (type.startsWith("application") || type.startsWith("text")) {
return <div className={"preview"}>
<IconFile size={30}/>
</div>
} else {
return <div className={"preview"}>
<IconQuestionMark size={30}/>
</div>
}
}

function renderName() {
let split = data?.src?.split("/")
return <span className={"name"}>{split[split.length - 1]}</span>
}

return hidden ? null : (
<div className={"driveFile"}>
<img src={data.src} alt={data.alt}/>
{renderPreview()}
{renderName()}
<Container gap={"md"} align={"horizontalCenter"}>
<Button onClick={() => alert("TODO: Edit")}>
<IconPencil size={18}/>
Expand Down
6 changes: 5 additions & 1 deletion frontend/packages/app/src/lib/components/Error.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './Error.scss'
import Button from "./Button.tsx";
import {IconBug, IconReload} from "@tabler/icons-react";
import {IconBug, IconReload, IconTool} from "@tabler/icons-react";
import ApiError from "../utils/ApiError.ts";

function Error(
Expand Down Expand Up @@ -37,6 +37,10 @@ function Error(
<IconBug size={18}/>
Report Bug
</Button>
<Button onClick={() => console.error(error)}>
<IconTool size={18}/>
Throw
</Button>
</div>
</div>
)
Expand Down
82 changes: 54 additions & 28 deletions frontend/packages/app/src/lib/components/Timeline.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,72 @@
import * as React from "react";
import {useRef} from "react";
import './Timeline.scss';
import Container from "./Container.tsx";
import {randomString} from "aster-common"
import type {DefinedUseInfiniteQueryResult} from "@tanstack/react-query";
import Container from "./Container";
import Loading from "./Loading.tsx";

function Timeline(
{data, Component}:
{ data?: any[], Component: any }
{query, Component, grid = false}:
{ query?: DefinedUseInfiniteQueryResult<any>, Component: any, grid?: boolean }
) {
React.useEffect(() => {
render()
})
const intersectionRef = useRef(null)
const intersectionId = randomString()

let random = Math.floor(Math.random() * (Math.ceil(1) - Math.floor(100000)));
let noMore = false

let timeline: any[] = []
const observer = new IntersectionObserver(async (entries) => {
if (!noMore) {
console.log("Intersection observed")
if (entries[0]?.isIntersecting) query?.fetchNextPage()
}
}, {
threshold: 0.8
});

function clear() {
timeline = []
}
React.useEffect(() => {
if (intersectionRef?.current)
observer.observe(intersectionRef.current);
})

function render() {
clear()
data?.forEach((item) => {
random++
timeline.push(
<Component data={item}
key={"TimelineChild-" + Component.name + "-" + (item?.id ? item?.id : "r" + random)}>
</Component>
function renderBaseTimeline() {
if (query?.data && query.data?.pages && query.data.pages.length > 0) {
return (
query?.data?.pages?.map((items) => (
(items && items.length > 0) ?
items?.map((item) => (
(item ? <Component data={item}
key={"TimelineChild-" + Component.name + "-" + (item?.id ? item?.id : "r" + randomString())}>
</Component> : null)
)) : renderNone()
))
)
})
} else {
return renderNone()
}
}

render()
function renderNone() {
noMore = true
return (
<Container align={"center"} padding={"12px 0"}>
<span className={"notice"}>Nothing more to show...</span>
</Container>
)
}

return (
return <>
<div className={`timeline`}>
{timeline.length > 0 ? timeline : (
<Container align={"center"} padding={"12px 0"}>
<span className={"notice"}>Nothing to show...</span>
</Container>
)}
{grid ? <Container gap={"md"} align={"startHorizontal"} wrap>
{renderBaseTimeline()}
</Container> : renderBaseTimeline()}
</div>
<div id={"intersection-" + intersectionId} ref={intersectionRef}>
<Container align={"center"} padding={"12px 0"}>
{query?.isFetchingNextPage ? <Loading/> : null}
</Container>
</div>
)
</>
}

export default Timeline
11 changes: 2 additions & 9 deletions frontend/packages/app/src/lib/components/widgets/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
IconDashboard,
IconDots,
IconFolder,
IconHash,
IconHome,
IconSearch,
IconSettings,
Expand All @@ -31,12 +30,6 @@ function NavigationWidget() {
Notifications
</Button>
</li>
<li>
<Button wide nav primary={pathname === "/explore"} to={'/explore'}>
<IconHash size={18}/>
Explore
</Button>
</li>
<li>
<Button wide nav primary={pathname === "/follow-requests"} to={'/follow-requests'}>
<IconUserPlus size={18}/>
Expand Down Expand Up @@ -70,7 +63,7 @@ function NavigationWidget() {
</Button>
</li>
<li>
<Button wide nav primary={pathname === "/admin"} to={'/admin'}>
<Button wide nav primary={pathname === "/admin"} to={'/admin'} realLink>
<IconDashboard size={18}/>
Dashboard
</Button>
Expand All @@ -85,4 +78,4 @@ function NavigationWidget() {
)
}

export default NavigationWidget;
export default NavigationWidget;
2 changes: 1 addition & 1 deletion frontend/packages/app/src/lib/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const queryClient = new QueryClient({
queries: {
retry: 2,
retryDelay: 5000,
refetchOnWindowFocus: false,
refetchOnWindowFocus: false
}
}
});
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/app/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "preact/debug"
import {StrictMode} from 'react'
import {createRoot} from 'react-dom/client'
import {RouterProvider} from "@tanstack/react-router";
Expand Down
9 changes: 1 addition & 8 deletions frontend/packages/app/src/routes/about.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,12 @@ function RouteComponent() {
title={"About"}
/>
<PageWrapper padding={"full"} center={true}>
<img alt={"Aster logo"} src={"/favicon.png"} height={"50px"}/>
<img alt={"Aster logo"} src={"/uikit/branding/favicon.png"} height={"50px"}/>

<p className={"centerText"}>
Aster {data?.version?.aster}
</p>

{data?.plugins?.length > 0 ? (
<details>
<summary>Plugins</summary>
{JSON.stringify(data?.plugins)}
</details>
) : null}

<p className={"centerText"}>
Kotlin {data?.version?.kotlin}<br/>
Runtime {data?.version?.java}<br/>
Expand Down
24 changes: 12 additions & 12 deletions frontend/packages/app/src/routes/drive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@ import {createFileRoute} from "@tanstack/react-router";
import PageHeader from "../lib/components/PageHeader.tsx";
import {IconFolder, IconPlus} from "@tabler/icons-react";
import PageWrapper from "../lib/components/PageWrapper.tsx";
import {useQuery} from "@tanstack/react-query";
import {useInfiniteQuery} from "@tanstack/react-query";
import localstore from "../lib/utils/localstore.ts";
import DriveFile from "../lib/components/DriveFile.tsx";
import Container from "../lib/components/Container.tsx";
import Loading from "../lib/components/Loading.tsx";
import Error from "../lib/components/Error.tsx";
import Button from "../lib/components/Button.tsx";
import type {ChangeEvent} from "react";
import {Api} from 'aster-common'
import Timeline from "../lib/components/Timeline.tsx";

export const Route = createFileRoute('/drive')({
component: RouteComponent,
})

function RouteComponent() {
const {data, error, isPending, isFetching, refetch} = useQuery({
const query = useInfiniteQuery({
queryKey: [`drive_${localstore.getSelf()?.id}`],
queryFn: () => Api.getDrive(),
queryFn: ({pageParam}) => Api.getDrive(pageParam),
initialPageParam: undefined,
getNextPageParam: (lastPage) => {
return lastPage ? lastPage?.at(-1)?.createdAt : undefined
}
});

function upload(e: ChangeEvent<HTMLInputElement>) {
Expand Down Expand Up @@ -53,16 +57,12 @@ function RouteComponent() {
/>
</PageHeader>
<PageWrapper padding={"full"} center={false}>
{isPending || isFetching ? (
{query.isPending ? (
<Loading fill/>
) : error ? (
<Error error={error} retry={refetch}/>
) : query.error ? (
<Error error={query.error} retry={query.refetch}/>
) : (
<Container gap={"md"} align={"horizontal"}>
{data?.map((file) => (
<DriveFile data={file}/>
))}
</Container>
<Timeline query={query} Component={DriveFile} grid/>
)}
</PageWrapper>
</>
Expand Down
Loading