forked from OctoWoW/OctoLauncher
Fixed tweaks and mods, added localization, added antivirus walkthrough
This commit is contained in:
@@ -2,6 +2,7 @@ import { AlertTriangle, ExternalLink, RefreshCw } from 'lucide-react';
|
||||
|
||||
import { type NewsItem } from '~main/types';
|
||||
import { api } from '~renderer/utils/api';
|
||||
import { useT } from '~renderer/i18n';
|
||||
import useScrollHint from '~renderer/utils/useScrollHint';
|
||||
|
||||
import IconSpinner from '../styled/IconSpinner';
|
||||
@@ -18,15 +19,20 @@ const formatDate = (raw: string) => {
|
||||
};
|
||||
|
||||
const NewsEntry = ({ item }: { item: NewsItem }) => {
|
||||
const t = useT();
|
||||
const openLink = api.general.openLink.useMutation();
|
||||
return (
|
||||
<article className="flex flex-col gap-1 border-b border-blueGray/30 pb-3 last:border-0">
|
||||
<div className="flex items-baseline justify-between gap-3">
|
||||
<h5 className="tw-color">{item.title}</h5>
|
||||
<span className="s1 shrink-0 text-blueGray">{formatDate(item.date)}</span>
|
||||
<span className="s1 shrink-0 text-blueGray">
|
||||
{formatDate(item.date)}
|
||||
</span>
|
||||
</div>
|
||||
{item.author && (
|
||||
<span className="s1 italic text-blueGray">by {item.author}</span>
|
||||
<span className="s1 italic text-blueGray">
|
||||
{t('misc.newsByAuthor', { author: item.author })}
|
||||
</span>
|
||||
)}
|
||||
<p className="whitespace-pre-wrap text-sm leading-relaxed">{item.body}</p>
|
||||
{item.url && (
|
||||
@@ -36,7 +42,7 @@ const NewsEntry = ({ item }: { item: NewsItem }) => {
|
||||
className="-ml-2 self-start text-pink"
|
||||
onClick={() => openLink.mutateAsync(item.url!)}
|
||||
>
|
||||
Read more
|
||||
{t('misc.newsReadMore')}
|
||||
</TextButton>
|
||||
)}
|
||||
</article>
|
||||
@@ -44,6 +50,7 @@ const NewsEntry = ({ item }: { item: NewsItem }) => {
|
||||
};
|
||||
|
||||
const NewsTab = () => {
|
||||
const t = useT();
|
||||
const query = api.news.list.useQuery(undefined, {
|
||||
staleTime: 5 * 60 * 1000,
|
||||
refetchOnWindowFocus: false,
|
||||
@@ -54,14 +61,14 @@ const NewsTab = () => {
|
||||
return (
|
||||
<div className="tw-surface flex min-h-0 flex-grow flex-col gap-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="tw-color">News</h4>
|
||||
<h4 className="tw-color">{t('misc.newsTitle')}</h4>
|
||||
<TextButton
|
||||
icon={RefreshCw}
|
||||
size={18}
|
||||
className="-mr-2 text-blueGray"
|
||||
loading={query.isFetching}
|
||||
onClick={() => query.refetch()}
|
||||
title="Refresh"
|
||||
title={t('misc.refresh')}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
@@ -72,24 +79,24 @@ const NewsTab = () => {
|
||||
{query.isLoading ? (
|
||||
<div className="flex flex-grow flex-col items-center justify-center gap-2">
|
||||
<IconSpinner className="text-blueGray" />
|
||||
<p className="italic text-blueGray">Loading news...</p>
|
||||
<p className="italic text-blueGray">{t('misc.newsLoading')}</p>
|
||||
</div>
|
||||
) : query.isError ? (
|
||||
<div className="flex flex-grow flex-col items-center justify-center gap-3">
|
||||
<AlertTriangle size={32} className="text-red" />
|
||||
<p className="italic text-blueGray">Couldn't reach the news feed.</p>
|
||||
<p className="italic text-blueGray">{t('misc.newsError')}</p>
|
||||
<TextButton
|
||||
icon={RefreshCw}
|
||||
size={18}
|
||||
className="text-pink"
|
||||
onClick={() => query.refetch()}
|
||||
>
|
||||
Try again
|
||||
{t('misc.tryAgain')}
|
||||
</TextButton>
|
||||
</div>
|
||||
) : !query.data?.length ? (
|
||||
<div className="flex flex-grow flex-col items-center justify-center">
|
||||
<p className="italic text-blueGray">No news yet — check back later.</p>
|
||||
<p className="italic text-blueGray">{t('misc.newsEmpty')}</p>
|
||||
</div>
|
||||
) : (
|
||||
query.data.map(item => <NewsEntry key={item.id} item={item} />)
|
||||
|
||||
Reference in New Issue
Block a user