Addressing PR comments.

This commit is contained in:
Dessalines 2023-10-14 16:43:40 -04:00
parent d43f482d90
commit dfc7bfdc98
15 changed files with 271 additions and 237 deletions

2
.gitmodules vendored
View file

@ -17,4 +17,4 @@
[submodule "lemmy-stats-crawler"]
path = lemmy-stats-crawler
url = https://github.com/LemmyNet/lemmy-stats-crawler.git
branch = upgrade_deps_1
branch = main

@ -1 +1 @@
Subproject commit cacc71ec5453b4c4d8d701a495659cfc8f92b058
Subproject commit 229a9319ba1130723928e58fcbdbfccad45a4e81

@ -1 +1 @@
Subproject commit 0e5578aaf34e0293c489eea64dbe7444c12732ec
Subproject commit ff8a2db505771c9f142ab31626b49de54a756192

View file

@ -70,7 +70,7 @@ const AppDetailsCard = ({ app }: AppDetailsCardProps) => (
);
const AppTitle = ({ title }) => (
<div className="text-xl mb-3 text-gray-300">{title}</div>
<div className="text-2xl mb-3 text-gray-300">{title}</div>
);
const MobileAppsBlock = () => (

View file

@ -6,7 +6,6 @@ import {
END_FUNDRAISER_DATE,
FUNDED_DEVS,
FUNDED_DEV_GOAL,
MEDIAN_DEV_MONTHLY_EUR,
MEDIAN_DEV_SALARY,
TOTAL_RECURRING_MONTHLY_EUR,
TOTAL_SUPPORTERS,
@ -42,7 +41,7 @@ export const DonateDesc = () => (
<p className="text-sm text-gray-300 mb-3">
<T i18nKey="donate_desc">
#
<Link className="link link-primary" to="/donate">
<Link className="link" to="/donate">
#
</Link>
#
@ -76,6 +75,12 @@ export const DonateButtons = () => (
#<span className="font-bold">#</span>
</T>
</a>
<Link
class="btn btn-secondary text-white sm:max-md:btn-block normal-case"
to="/crypto"
>
Crypto
</Link>
</div>
);
@ -89,14 +94,18 @@ const FundingGoal = () => (
<span className="text-gray-200 mr-3">
{i18n.t("per_month", { formattedCount: "" })}
</span>
<span className="text-sm text-gray-300">
{i18n.t("supporters", {
formattedCount: NUMBER_FORMAT.format(TOTAL_SUPPORTERS),
})}
</span>
</div>
<div className="text-xl font-bold">
{NUMBER_FORMAT.format(MEDIAN_DEV_MONTHLY_EUR * FUNDED_DEV_GOAL)}
<div
className="text-xl font-bold tooltip"
data-tip={i18n.t("based_on_salary", {
formattedCount: NUMBER_FORMAT.format(MEDIAN_DEV_SALARY),
})}
>
{i18n.t("devs_funded", {
formattedCount1: FUNDED_DEVS.toFixed(1),
formattedCount2: FUNDED_DEV_GOAL,
})}
*
</div>
</div>
<progress
@ -105,17 +114,10 @@ const FundingGoal = () => (
max={FUNDED_DEV_GOAL}
></progress>
<div className="flex flex-row flex-wrap justify-between gap-4">
<div
className="text-sm text-gray-300 tooltip"
data-tip={i18n.t("based_on_salary", {
formattedCount: NUMBER_FORMAT.format(MEDIAN_DEV_SALARY),
<div className="text-sm text-gray-300">
{i18n.t("supporters", {
formattedCount: NUMBER_FORMAT.format(TOTAL_SUPPORTERS),
})}
>
{i18n.t("devs_funded", {
formattedCount1: FUNDED_DEVS.toFixed(2),
formattedCount2: FUNDED_DEV_GOAL,
})}
*
</div>
<div className="text-sm text-gray-300">
{monthsBetween(new Date(), END_FUNDRAISER_DATE)} months remaining
@ -124,12 +126,12 @@ const FundingGoal = () => (
</div>
);
export const SupportDonateBlock = () => (
export const DonateBlock = () => (
<div className="flex flex-col items-center pt-16">
<div className={`card card-bordered ${CARD_GRADIENT} shadow-xl`}>
<div className="card-body px-8 md:px-32 py-16">
<p class={`card-title text-4xl mb-3 ${TEXT_GRADIENT}`}>
{i18n.t("support_donate")}
{i18n.t("donate")}
</p>
<DonateDesc />
<FundingGoal />

View file

@ -0,0 +1,59 @@
import { Helmet } from "inferno-helmet";
import { Badge } from "./common";
const title = "Crypto";
interface Crypto {
name: string;
address: string;
}
const CRYPTOS: Crypto[] = [
{
name: "bitcoin",
address: "1Hefs7miXS5ff5Ck5xvmjKjXf5242KzRtK",
},
{
name: "ethereum",
address: "0x400c96c96acbC6E7B3B43B1dc1BB446540a88A01",
},
{
name: "monero",
address:
"41taVyY6e1xApqKyMVDRVxJ76sPkfZhALLTjRvVKpaAh2pBd4wv9RgYj1tSPrx8wc6iE1uWUfjtQdTmTy2FGMeChGVKPQuV",
},
{
name: "cardano",
address:
"addr1q858t89l2ym6xmrugjs0af9cslfwvnvsh2xxp6x4dcez7pf5tushkp4wl7zxfhm2djp6gq60dk4cmc7seaza5p3slx0sakjutm",
},
];
export const Crypto = () => (
<div className="container mx-auto px-4">
<Helmet title={title}>
<meta property={"title"} content={title} />
</Helmet>
<div className="pt-16 text-center text-4xl font-bold mb-8">{title}</div>
<div className="card card-bordered bg-neutral-900 shadow-xl">
<div className="card-body p-4">
<table className="table table-sm">
{CRYPTOS.map(c => (
<tr>
<td className="text-sm text-gray-300">{c.name}</td>
<td>
<Badge
content={
<code className="text-sm text-secondary break-all">
{c.address}
</code>
}
/>
</td>
</tr>
))}
</table>
</div>
</div>
</div>
);

View file

@ -99,8 +99,8 @@ export interface Coder {
}
export const CODERS: Coder[] = [
{ name: "dessalines", link: "https://mastodon.social/@dessalines" },
{ name: "Nutomic", link: "https://lemmy.ml/u/nutomic" },
{ name: "dessalines", link: "https://github.com/dessalines" },
{ name: "Nutomic", link: "https://github.com/nutomic" },
{ name: "phiresky", link: "https://github.com/phiresky" },
{ name: "SleeplessOne1917", link: "https://github.com/SleeplessOne1917" },
{ name: "asonix", link: "https://github.com/asonix" },
@ -124,32 +124,6 @@ export interface Translator {
link?: string;
}
export interface Crypto {
name: string;
address: string;
}
export const CRYPTOS: Crypto[] = [
{
name: "bitcoin",
address: "1Hefs7miXS5ff5Ck5xvmjKjXf5242KzRtK",
},
{
name: "ethereum",
address: "0x400c96c96acbC6E7B3B43B1dc1BB446540a88A01",
},
{
name: "monero",
address:
"41taVyY6e1xApqKyMVDRVxJ76sPkfZhALLTjRvVKpaAh2pBd4wv9RgYj1tSPrx8wc6iE1uWUfjtQdTmTy2FGMeChGVKPQuV",
},
{
name: "cardano",
address:
"addr1q858t89l2ym6xmrugjs0af9cslfwvnvsh2xxp6x4dcez7pf5tushkp4wl7zxfhm2djp6gq60dk4cmc7seaza5p3slx0sakjutm",
},
];
interface FundingPlatform {
supporterCount: number;
monthlyEUR: number;

View file

@ -5,12 +5,7 @@ import { T } from "inferno-i18next";
import { translators } from "../translations/translators";
import { languagesAll, countries } from "countries-list";
import { isBrowser } from "../utils";
import {
Badge,
BottomSpacer,
SupportDonateBlock,
TEXT_GRADIENT,
} from "./common";
import { Badge, BottomSpacer, DonateBlock, TEXT_GRADIENT } from "./common";
import {
CODERS,
GOLD_SPONSORS,
@ -20,7 +15,6 @@ import {
LATINUM_SPONSORS,
GENERAL_SPONSORS,
Translation,
CRYPTOS,
} from "./donate-definitions";
import classNames from "classnames";
import { Icon } from "./icon";
@ -44,7 +38,7 @@ const ContributorsBlock = () => (
<T i18nKey="add_weblate">
#
<a
className="link link-primary"
className="link"
href="https://weblate.join-lemmy.org/projects/lemmy/"
>
#
@ -191,32 +185,6 @@ const GeneralSponsorCard = () => {
);
};
const CryptoBlock = () => (
<div>
<SectionTitle title={"Crypto"} />
<div className="card card-bordered bg-neutral-900 shadow-xl">
<div className="card-body p-4">
<table className="table table-sm">
{CRYPTOS.map(c => (
<tr>
<td className="text-sm text-gray-300">{c.name}</td>
<td>
<Badge
content={
<code className="text-sm text-secondary break-all">
{c.address}
</code>
}
/>
</td>
</tr>
))}
</table>
</div>
</div>
</div>
);
interface PersonBadgeData {
name: string;
link?: string;
@ -284,16 +252,15 @@ export class Donate extends Component<any, any> {
}
render() {
const title = i18n.t("support_title");
const title = i18n.t("donate_title");
return (
<div className="container mx-auto px-4">
<Helmet title={title}>
<meta property={"title"} content={title} />
</Helmet>
<SupportDonateBlock />
<DonateBlock />
<ContributorsBlock />
<SponsorsBlock />
<CryptoBlock />
<BottomSpacer />
</div>
);

View file

@ -2,6 +2,7 @@ export enum IconSize {
Small = "w-3 h-3",
Medium = "w-4 h-4",
Large = "w-6 h-6",
Largest = "w-8 h-8",
}
interface IconProps {

View file

@ -8,7 +8,10 @@ import { Icon } from "./icon";
enum Step {
Interest,
Language,
Join,
}
interface Props {
reset?: boolean;
}
interface State {
@ -17,14 +20,21 @@ interface State {
language?: string;
}
export class InstancePicker extends Component<any, State> {
export class InstancePicker extends Component<Props, State> {
state: State = {
activeStep: Step.Interest,
};
constructor(props: any, context: any) {
super(props, context);
}
componentWillReceiveProps(): void {
this.setState({
activeStep: Step.Interest,
});
}
render() {
return (
<dialog id="picker" className="modal">
@ -43,23 +53,18 @@ export class InstancePicker extends Component<any, State> {
<p className="text-2xl font-bold text-center pb-4">
{i18n.t("what_topic")}
</p>
<div className="flex flex-row flex-wrap gap-4 pb-4">
{TOPICS.map(c => (
<div className="form-control">
<label className="label cursor-pointer">
<span className="label-text text-sm text-gray-300">
<Icon icon={c.icon} classes="mr-3" />
{i18n.t(c.name as I18nKeys)}
</span>
<input
type="radio"
name="topic-radio"
className="radio"
<button
className="btn btn-sm btn-outline normal-case"
value={c.name}
onChange={linkEvent(this, handleTopicChange)}
/>
</label>
</div>
onClick={linkEvent(this, handleTopicChange)}
>
<Icon icon={c.icon} />
{i18n.t(c.name as I18nKeys)}
</button>
))}
</div>
</>
)}
{this.state.activeStep == Step.Language && (
@ -67,36 +72,24 @@ export class InstancePicker extends Component<any, State> {
<p className="text-2xl font-bold text-center pb-4">
{i18n.t("what_language")}
</p>
<div className="form-control">
<label className="label cursor-pointer">
<span className="label-text text-sm text-gray-300">
{i18n.t("all_languages")}
</span>
<input
type="radio"
name="language-radio"
className="radio"
<div className="flex flex-row flex-wrap gap-4 pb-4">
<button
className="btn btn-sm btn-outline normal-case"
value={"all"}
onChange={linkEvent(this, handleLanguageChange)}
/>
</label>
</div>
onClick={linkEvent(this, handleLanguageChange)}
>
{i18n.t("all_languages")}
</button>
{LANGUAGES.map(l => (
<div className="form-control">
<label className="label cursor-pointer">
<span className="label-text text-sm text-gray-300">
{l.name}
</span>
<input
type="radio"
name="language-radio"
className="radio"
<button
className="btn btn-sm btn-outline normal-case"
value={l.code}
onChange={linkEvent(this, handleLanguageChange)}
/>
</label>
</div>
onClick={linkEvent(this, handleLanguageChange)}
>
{l.name}
</button>
))}
</div>
</>
)}
@ -117,13 +110,6 @@ export class InstancePicker extends Component<any, State> {
>
{i18n.t("languages")}
</li>
<li
className={classNames("step text-gray-300", {
"step-primary": this.state.activeStep == Step.Join,
})}
>
{i18n.t("join")}
</li>
</ul>
</div>
</div>

View file

@ -12,20 +12,44 @@ import {
All_TOPIC,
TOPICS,
} from "./instances-definitions";
import { Icon } from "./icon";
import { Icon, IconSize } from "./icon";
import { I18nKeys } from "i18next";
const TitleBlock = () => (
<div className="flex flex-col items-center pt-16 mb-16">
<T i18nKey="lemmy_servers" className="text-4xl font-bold mb-3">
<T i18nKey="lemmy_servers" className="text-4xl font-bold mb-8">
#<span className={TEXT_GRADIENT}>#</span>
</T>
<p className="text-xl text-gray-300 text-center max-w-xl">
{i18n.t("instance_totals", {
instances: numToSI(instance_stats.stats.crawled_instances),
users: numToSI(instance_stats.stats.users_active_month),
<div
className="tooltip"
data-tip={i18n.t("monthly_active_users", {
formattedCount: numToSI(instance_stats.stats.users_active_month),
})}
</p>
>
<div className="stats shadow mb-8">
<div className="stat">
<div className="stat-figure text-primary">
<Icon icon="globe" size={IconSize.Largest} />
</div>
<div className="stat-title">{i18n.t("servers")}</div>
<div className="stat-value">
{numToSI(instance_stats.stats.crawled_instances)}
</div>
<div className="stat-desc">{i18n.t("lemmyverse")}</div>
</div>
<div className="stat">
<div className="stat-figure text-secondary">
<Icon icon="users" size={IconSize.Largest} />
</div>
<div className="stat-title">{i18n.t("active_users")}*</div>
<div className="stat-value">
{numToSI(instance_stats.stats.users_active_month)}
</div>
<div className="stat-desc">{new Date().toLocaleDateString()}</div>
</div>
</div>
</div>
<p className="text-xl text-gray-300">{i18n.t("instance_disclaimer")}</p>
</div>
);
@ -122,7 +146,7 @@ const InstanceCard = ({ instance }: InstanceCardProps) => {
class="btn btn-primary text-white sm:max-md:btn-block bg-gradient-to-r from-[#69D066] to-[#03A80E] normal-case"
href={buildUrl(domain)}
>
{i18n.t("join_a_server")}
{i18n.t("browse_instance")}
</a>
<button
class="btn btn-secondary btn-outline text-white sm:max-md:btn-block normal-case"
@ -161,7 +185,12 @@ export const StatsBadges = ({ users, comments, monthlyUsers }) => (
<>
<Badge
content={
<div className="text-sm text-gray-500">
<div
className="text-sm text-gray-500 tooltip"
data-tip={i18n.t("total_users", {
formattedCount: users.toLocaleString(),
})}
>
<Icon icon="users" classes="mr-2" />
<span>{users.toLocaleString()}</span>
</div>
@ -169,7 +198,12 @@ export const StatsBadges = ({ users, comments, monthlyUsers }) => (
/>
<Badge
content={
<div className="text-sm text-gray-500">
<div
className="text-sm text-gray-500 tooltip"
data-tip={i18n.t("total_comments", {
formattedCount: comments.toLocaleString(),
})}
>
<Icon icon="message-circle" classes="mr-2" />
<span>{comments.toLocaleString()}</span>
</div>
@ -177,7 +211,12 @@ export const StatsBadges = ({ users, comments, monthlyUsers }) => (
/>
<Badge
content={
<div className="text-sm text-gray-500">
<div
className="text-sm text-gray-500 tooltip"
data-tip={i18n.t("monthly_active_users", {
formattedCount: monthlyUsers.toLocaleString(),
})}
>
<Icon icon="user-check" classes="mr-2" />
<span>
{i18n.t("per_month", {

View file

@ -8,64 +8,67 @@ import { Icon } from "./icon";
import {
BottomSpacer,
CARD_GRADIENT,
SupportDonateBlock,
DonateBlock,
TEXT_GRADIENT,
} from "./common";
import { InstancePicker } from "./instance-picker";
import classNames from "classnames";
const TitleBlock = () => (
interface MainProps {
i: Main;
}
const TitleBlock = ({ i }: MainProps) => (
<div className="py-16 flex flex-col items-center">
<div className="flex flex-col items-center gap-4 mb-8">
<p className={`text-6xl font-bold ${TEXT_GRADIENT}`}>Lemmy</p>
<p className="text-3xl font-medium text-center">{i18n.t("lemmy_desc")}</p>
</div>
<div className="flex flex-row justify-around gap-4">
<JoinServerButton />
<JoinServerButton i={i} />
<SeeAllServersButton />
</div>
</div>
);
const carouselImages = [
"/static/assets/images/main_screen_2.webp",
"/static/assets/images/main_screen_3.webp",
"/static/assets/images/main_screen_1.webp",
];
const CarouselBlock = () => (
<div>
<div className="carousel carousel-center p-8 space-x-8 rounded-box">
<div id="item1" className="carousel-item w-9/12">
<div className="carousel carousel-center p-8 space-x-8 rounded-box mt-16">
{carouselImages.map((image, i) => (
<div id={`item-${i}`} className="carousel-item w-9/12 lg:w-5/12">
<img
src={"/static/assets/images/main_screen_2.webp"}
className="rounded-box border-8 border-secondary/[.15] z-10"
/>
</div>
<div id="item2" className="carousel-item w-9/12">
<img
src={"/static/assets/images/main_screen_3.webp"}
className="rounded-box border-8 border-primary/[.15] z-10"
/>
</div>
<div id="item3" className="carousel-item w-9/12">
<img
src={"/static/assets/images/main_screen_1.webp"}
className="rounded-box border-8 border-secondary/[.15] z-10"
src={image}
className={classNames("rounded-box border-8 z-10", {
"border-primary/[.15]": i & 1,
"border-secondary/[.15]": !(i & 1),
})}
/>
</div>
))}
</div>
<div className="flex justify-center w-full py-2 gap-4">
<a href="#item1" className={TEXT_GRADIENT}>
</a>
<a href="#item2" className={TEXT_GRADIENT}>
</a>
<a href="#item3" className={TEXT_GRADIENT}>
{carouselImages.map((_, i) => (
<a href={`#item-${i}`} className={TEXT_GRADIENT}>
</a>
))}
</div>
</div>
);
const JoinServerButton = () => (
const JoinServerButton = ({ i }: MainProps) => (
<button
className="btn btn-primary text-white normal-case z-10"
onClick={() => (document.getElementById("picker") as any).showModal()}
onClick={() => {
i.setState({ resetInstancePicker: true });
(document.getElementById("picker") as any).showModal();
}}
>
{i18n.t("join_a_server")}
</button>
@ -80,8 +83,8 @@ const SeeAllServersButton = () => (
</Link>
);
const FollowCommunitiesBlock = () => (
<div className="flex flex-col items-center mt-16">
const FollowCommunitiesBlock = ({ i }: MainProps) => (
<div className="flex flex-col items-center">
<div className={`card card-bordered ${CARD_GRADIENT} shadow-xl`}>
<div className="card-body items-center px-8 md:px-32 py-16">
<T
@ -93,14 +96,14 @@ const FollowCommunitiesBlock = () => (
<p className="text-sm text-gray-300 text-center mb-6">
{i18n.t("lemmy_long_desc")}
</p>
<JoinServerButton />
<JoinServerButton i={i} />
</div>
</div>
</div>
);
const FeatureCard = ({ pic, title, subtitle, classes }) => (
<div className={`card card-bordered bg-neutral-800 shadow-xl ${classes}`}>
<div className={`card ${CARD_GRADIENT} shadow-xl ${classes}`}>
<figure className="p-4">
<img src={pic} className="rounded-xl w-full object-fill min-h-[300px]" />
</figure>
@ -247,13 +250,13 @@ const MoreFeaturesBlock = () => (
<T i18nKey="self_hostable">
#
<a
className="link link-primary"
className="link"
href={`/docs/administration/install_docker.html`}
>
#
</a>
<a
className="link link-primary"
className="link"
href={`/docs/administration/install_ansible.html`}
>
#
@ -276,7 +279,7 @@ const MoreFeaturesBlock = () => (
</div>
}
text={
<Link className="link link-primary" to="/apps">
<Link className="link" to="/apps">
{i18n.t("mobile_apps_for_ios_and_android")}
</Link>
}
@ -297,7 +300,7 @@ const MoreFeaturesBlock = () => (
}
text={
<T i18nKey="full_vote_scores">
#<code className="text-primary">#</code>#
#<code>#</code>#
</T>
}
/>
@ -313,7 +316,7 @@ const MoreFeaturesBlock = () => (
icons={<div>:</div>}
text={
<T i18nKey="emojis_autocomplete">
#<code className="text-primary">#</code>
#<code>#</code>
</T>
}
/>
@ -325,8 +328,8 @@ const MoreFeaturesBlock = () => (
}
text={
<T i18nKey="user_tagging">
#<code className="text-primary">#</code>
<code className="text-primary">#</code>
#<code>#</code>
<code>#</code>
</T>
}
/>
@ -356,7 +359,7 @@ const MoreFeaturesBlock = () => (
<T i18nKey="i18n_support">
#
<a
className="link link-primary"
className="link"
href="https://weblate.join-lemmy.org/projects/lemmy/lemmy/"
>
#
@ -372,11 +375,11 @@ const MoreFeaturesBlock = () => (
}
text={
<T i18nKey="rss_feeds">
#<code className="text-primary">#</code>
<code className="text-primary">#</code>
<code className="text-primary">#</code>
<code className="text-primary">#</code>
<code className="text-primary">#</code>
#<code>#</code>
<code>#</code>
<code>#</code>
<code>#</code>
<code>#</code>
</T>
}
/>
@ -411,13 +414,19 @@ const MoreFeaturesBlock = () => (
const MoreFeaturesCard = ({ icons, text }) => (
<div className="card card-bordered w-auto bg-neutral-800 shadow-xl">
<div className="card-body">
<div className="btn btn-sm btn-secondary w-fit mb-2">{icons}</div>
<div className="btn btn-sm btn-secondary w-fit mb-2 pointer-events-none">
{icons}
</div>
<p className="text-sm text-gray-300">{text}</p>
</div>
</div>
);
export class Main extends Component<any, any> {
state = {
resetInstancePicker: false,
};
constructor(props: any, context: any) {
super(props, context);
}
@ -432,7 +441,7 @@ export class Main extends Component<any, any> {
const title = i18n.t("lemmy_title");
return (
<div>
<InstancePicker />
<InstancePicker reset={this.state.resetInstancePicker} />
<Helmet title={title}>
<meta property={"title"} content={title} />
</Helmet>
@ -441,15 +450,15 @@ export class Main extends Component<any, any> {
className="bg-top bg-no-repeat bg-contain opacity-20 absolute"
/>
<div className="container mx-auto px-4">
<TitleBlock />
<TitleBlock i={this} />
<FollowCommunitiesBlock i={this} />
</div>
<CarouselBlock />
<div className="container mx-auto px-4">
<FollowCommunitiesBlock />
<FeatureCardsBlock />
<DiscussionPlatformBlock />
<MoreFeaturesBlock />
<SupportDonateBlock />
<DonateBlock />
<BottomSpacer />
</div>
</div>

View file

@ -82,7 +82,10 @@ export const Navbar = ({ footer = false }) => (
</div>
<div className="navbar-end">
{footer ? (
<a className="text-sm text-gray-600 sm:max-lg:hidden">
<a
className="text-sm text-gray-600 sm:max-lg:hidden text-right"
href="https://github.com/LemmyNet/lemmy/blob/main/LICENSE"
>
{i18n.t("copyright_line")}
</a>
) : (

View file

@ -2,20 +2,17 @@ import { Component } from "inferno";
import { Link } from "inferno-router";
import { Helmet } from "inferno-helmet";
import { i18n } from "../i18next";
import { isBrowser } from "../utils";
import { isBrowser, mdToHtml } from "../utils";
import { news_md } from "../translations/news";
import { Badge, BottomSpacer, TEXT_GRADIENT } from "./common";
import { Icon } from "./icon";
import { BottomSpacer, TEXT_GRADIENT } from "./common";
const title = i18n.t("news");
const authors = ["nutomic", "dessalines"];
const news_reversed = news_md.reverse();
interface NewsInfo {
title: string;
dateStr: string;
preview: string;
authors: Array<string>;
url: string;
}
@ -27,7 +24,6 @@ function buildNewsInfoArray(): Array<NewsInfo> {
dateStr: split[0],
title: split[1],
preview: split[2] || previewMarkdown(n.markdown),
authors,
url: `news/${titleToUrl(n.title)}`,
};
});
@ -38,7 +34,11 @@ function titleToUrl(title: string): string {
}
function previewMarkdown(markdown: string): string {
return markdown.replace(/#/g, "").slice(0, 100).concat("...");
return markdown
.replace(/#/g, "")
.replace(/[\n\r]/g, " ")
.slice(0, 100)
.concat("...");
}
const TitleBlock = () => (
@ -61,14 +61,13 @@ const NewsCard = ({ news }: NewsProps) => (
{news.title}
</Link>
<div className="text-sm text-gray-500">{news.dateStr}</div>
<div className="flex flex-row flex-wrap items-baseline space-x-3 mb-2">
{authors.map(name => (
<AuthorBadge name={name} />
))}
</div>
</div>
<div className="text-sm text-gray-300">{news.preview}</div>
{news.preview && (
<div
className="text-sm text-gray-300"
dangerouslySetInnerHTML={mdToHtml(news.preview)}
/>
)}
</div>
<Link
to={news.url}
@ -81,17 +80,6 @@ const NewsCard = ({ news }: NewsProps) => (
</div>
);
const AuthorBadge = ({ name }) => (
<Badge
content={
<div>
<Icon icon="at-sign" classes={"fill-current text-gray-600"} />
<span className="ml-1 text-gray-300">{name}</span>
</div>
}
/>
);
export class News extends Component<any, any> {
constructor(props: any, context: any) {
super(props, context);

View file

@ -6,6 +6,7 @@ import { Contact } from "./components/contact";
import { Donate } from "./components/donate";
import { News } from "./components/news";
import { NewsItem } from "./components/news-item";
import { Crypto } from "./components/crypto";
export const routes: IRouteProps[] = [
{
@ -48,4 +49,9 @@ export const routes: IRouteProps[] = [
exact: true,
component: Donate,
},
{
path: `/crypto`,
exact: true,
component: Crypto,
},
];