feat: latest articles widget

This commit is contained in:
kieran 2024-09-19 11:18:01 +01:00
parent 026aaed5a1
commit 8c2833684b
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
10 changed files with 100 additions and 13 deletions

View File

@ -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>

View 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>
);
}

View File

@ -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>

View File

@ -3,7 +3,7 @@ export enum RightColumnWidget {
TrendingNotes,
TrendingPeople,
TrendingHashtags,
TrendingArticls,
LatestArticls,
LiveStreams,
InviteFriends,
}

View File

@ -1,15 +1,10 @@
import { useContext } from "react";
import Note, { NoteProps } from "@/Components/Event/EventComponent";
import { useArticles } from "@/Feed/ArticlesFeed";
import { DeckContext } from "@/Pages/Deck/DeckLayout";
import Note from "../../Components/Event/EventComponent";
const options = {
longFormPreview: true,
};
export default function Articles() {
export default function Articles({ noteProps }: { noteProps?: Omit<NoteProps, "data"> }) {
const data = useArticles();
const deck = useContext(DeckContext);
@ -19,9 +14,14 @@ export default function Articles() {
<Note
data={a}
key={a.id}
options={options}
{...noteProps}
options={{
longFormPreview: true,
...noteProps?.options,
}}
onClick={ev => {
deck?.setArticle(ev);
noteProps?.onClick?.(ev);
}}
/>
))}

View File

@ -2,6 +2,7 @@ import classNames from "classnames";
import { FormattedMessage } from "react-intl";
import { RightColumnWidget } from "@/Components/RightWidgets";
import LatestArticlesWidget from "@/Components/RightWidgets/articles";
import { BaseWidget } from "@/Components/RightWidgets/base";
import InviteFriendsWidget from "@/Components/RightWidgets/invite-friends";
import MiniStreamWidget from "@/Components/RightWidgets/mini-stream";
@ -23,6 +24,7 @@ export default function RightColumn() {
RightColumnWidget.InviteFriends,
//RightColumnWidget.LiveStreams,
RightColumnWidget.TrendingNotes,
RightColumnWidget.LatestArticls,
RightColumnWidget.TrendingPeople,
RightColumnWidget.TrendingHashtags,
]
@ -65,6 +67,8 @@ export default function RightColumn() {
return <InviteFriendsWidget />;
case RightColumnWidget.LiveStreams:
return <MiniStreamWidget />;
case RightColumnWidget.LatestArticls:
return <LatestArticlesWidget />;
}
};

View File

@ -175,6 +175,10 @@ a.ext {
-webkit-text-fill-color: transparent;
}
.text-outline {
-webkit-text-stroke: 1px black;
}
.br {
border-radius: 16px;
}
@ -941,6 +945,7 @@ svg.repeat {
.pb-safe-area-plus-footer {
padding-bottom: calc(env(safe-area-inset-bottom) + 56px);
}
.sm-hide-scrollbar {
scrollbar-width: none;
-ms-overflow-style: none;

View File

@ -183,6 +183,9 @@
"2ukA4d": {
"defaultMessage": "{n} hours"
},
"2z7Kky": {
"defaultMessage": "Latest Articles"
},
"3/onCd": {
"defaultMessage": "Replies"
},

View File

@ -60,6 +60,7 @@
"2oCF7O": "Followed by friends of friends",
"2raFAu": "Application-specific data",
"2ukA4d": "{n} hours",
"2z7Kky": "Latest Articles",
"3/onCd": "Replies",
"39AHJm": "Sign Up",
"3GWu6/": "User Statuses",

View File

@ -28,6 +28,7 @@ module.exports = {
},
textColor: {
secondary: "var(--font-secondary-color)",
"font-color": "var(--font-color)",
},
spacing: {
px: "1px",