feat: latest articles widget
This commit is contained in:
@ -12,7 +12,12 @@ interface IconButtonProps {
|
||||
|
||||
const IconButton = ({ onClick, icon, children, className }: IconButtonProps) => {
|
||||
return (
|
||||
<button className={classNames("icon", className)} type="button" onClick={onClick}>
|
||||
<button
|
||||
className={classNames(
|
||||
"flex items-center justify-center aspect-square w-10 h-10 !p-0 !m-0 bg-gray-dark text-white",
|
||||
className,
|
||||
)}
|
||||
onClick={onClick}>
|
||||
<Icon {...icon} />
|
||||
{children}
|
||||
</button>
|
||||
|
68
packages/app/src/Components/RightWidgets/articles.tsx
Normal file
68
packages/app/src/Components/RightWidgets/articles.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import { NostrLink } from "@snort/system";
|
||||
import { useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { useArticles } from "@/Feed/ArticlesFeed";
|
||||
import { findTag } from "@/Utils";
|
||||
|
||||
import IconButton from "../Button/IconButton";
|
||||
import { ProxyImg } from "../ProxyImg";
|
||||
import ProfilePreview from "../User/ProfilePreview";
|
||||
import { BaseWidget } from "./base";
|
||||
|
||||
export default function LatestArticlesWidget() {
|
||||
const [idx, setIdx] = useState(0);
|
||||
const articles = useArticles();
|
||||
const selected = articles.at(idx);
|
||||
|
||||
function next(i: number) {
|
||||
setIdx(x => {
|
||||
x += i;
|
||||
if (x < 0) {
|
||||
x = articles.length - 1;
|
||||
} else if (x > articles.length) {
|
||||
x = 0;
|
||||
}
|
||||
return x;
|
||||
});
|
||||
}
|
||||
|
||||
if (!selected) return;
|
||||
const link = NostrLink.fromEvent(selected);
|
||||
const image = findTag(selected, "image");
|
||||
const title = findTag(selected, "title");
|
||||
return (
|
||||
<BaseWidget title={<FormattedMessage defaultMessage="Latest Articles" />}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Link
|
||||
to={`/${link.encode()}`}
|
||||
className="relative rounded-xl overflow-hidden w-full aspect-video"
|
||||
state={selected}>
|
||||
{image ? (
|
||||
<ProxyImg src={image} className="absolute w-full h-full object-fit" />
|
||||
) : (
|
||||
<div className="absolute w-full h-full object-fit bg-gray-dark"></div>
|
||||
)}
|
||||
<div className="absolute bottom-2 left-4 right-4 px-2 py-1 rounded-xl text-lg font-bold text-white bg-black/50">
|
||||
{title}
|
||||
</div>
|
||||
</Link>
|
||||
<div>
|
||||
<ProfilePreview
|
||||
pubkey={selected.pubkey}
|
||||
profileImageProps={{
|
||||
subHeader: <></>,
|
||||
}}
|
||||
actions={
|
||||
<div className="flex gap-2">
|
||||
<IconButton icon={{ name: "arrowFront", className: "rotate-180", size: 14 }} onClick={() => next(-1)} />
|
||||
<IconButton icon={{ name: "arrowFront", size: 14 }} onClick={() => next(1)} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BaseWidget>
|
||||
);
|
||||
}
|
@ -11,16 +11,16 @@ export interface BaseWidgetProps {
|
||||
}
|
||||
export function BaseWidget({ children, title, icon, iconClassName, contextMenu }: BaseWidgetProps) {
|
||||
return (
|
||||
<div className="br p bg-gray-ultradark">
|
||||
<div className="b br p bg-gray-ultradark">
|
||||
{title && (
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex gap-2 items-center text-xl text-white font-semibold mb-1">
|
||||
<div className="flex gap-2 items-center text-xl text-white font-semibold mb-2">
|
||||
{icon && (
|
||||
<div className="p-2 bg-gray-dark rounded-full">
|
||||
<Icon name={icon} className={iconClassName} />
|
||||
</div>
|
||||
)}
|
||||
<div>{title}</div>
|
||||
<div className="text-font-color">{title}</div>
|
||||
</div>
|
||||
{contextMenu}
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@ export enum RightColumnWidget {
|
||||
TrendingNotes,
|
||||
TrendingPeople,
|
||||
TrendingHashtags,
|
||||
TrendingArticls,
|
||||
LatestArticls,
|
||||
LiveStreams,
|
||||
InviteFriends,
|
||||
}
|
||||
|
Reference in New Issue
Block a user