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"] [submodule "lemmy-stats-crawler"]
path = lemmy-stats-crawler path = lemmy-stats-crawler
url = https://github.com/LemmyNet/lemmy-stats-crawler.git 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 }) => ( 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 = () => ( const MobileAppsBlock = () => (

View file

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

View file

@ -5,12 +5,7 @@ import { T } from "inferno-i18next";
import { translators } from "../translations/translators"; import { translators } from "../translations/translators";
import { languagesAll, countries } from "countries-list"; import { languagesAll, countries } from "countries-list";
import { isBrowser } from "../utils"; import { isBrowser } from "../utils";
import { import { Badge, BottomSpacer, DonateBlock, TEXT_GRADIENT } from "./common";
Badge,
BottomSpacer,
SupportDonateBlock,
TEXT_GRADIENT,
} from "./common";
import { import {
CODERS, CODERS,
GOLD_SPONSORS, GOLD_SPONSORS,
@ -20,7 +15,6 @@ import {
LATINUM_SPONSORS, LATINUM_SPONSORS,
GENERAL_SPONSORS, GENERAL_SPONSORS,
Translation, Translation,
CRYPTOS,
} from "./donate-definitions"; } from "./donate-definitions";
import classNames from "classnames"; import classNames from "classnames";
import { Icon } from "./icon"; import { Icon } from "./icon";
@ -44,7 +38,7 @@ const ContributorsBlock = () => (
<T i18nKey="add_weblate"> <T i18nKey="add_weblate">
# #
<a <a
className="link link-primary" className="link"
href="https://weblate.join-lemmy.org/projects/lemmy/" 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 { interface PersonBadgeData {
name: string; name: string;
link?: string; link?: string;
@ -284,16 +252,15 @@ export class Donate extends Component<any, any> {
} }
render() { render() {
const title = i18n.t("support_title"); const title = i18n.t("donate_title");
return ( return (
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<Helmet title={title}> <Helmet title={title}>
<meta property={"title"} content={title} /> <meta property={"title"} content={title} />
</Helmet> </Helmet>
<SupportDonateBlock /> <DonateBlock />
<ContributorsBlock /> <ContributorsBlock />
<SponsorsBlock /> <SponsorsBlock />
<CryptoBlock />
<BottomSpacer /> <BottomSpacer />
</div> </div>
); );

View file

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

View file

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

View file

@ -12,20 +12,44 @@ import {
All_TOPIC, All_TOPIC,
TOPICS, TOPICS,
} from "./instances-definitions"; } from "./instances-definitions";
import { Icon } from "./icon"; import { Icon, IconSize } from "./icon";
import { I18nKeys } from "i18next"; import { I18nKeys } from "i18next";
const TitleBlock = () => ( const TitleBlock = () => (
<div className="flex flex-col items-center pt-16 mb-16"> <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> #<span className={TEXT_GRADIENT}>#</span>
</T> </T>
<p className="text-xl text-gray-300 text-center max-w-xl"> <div
{i18n.t("instance_totals", { className="tooltip"
instances: numToSI(instance_stats.stats.crawled_instances), data-tip={i18n.t("monthly_active_users", {
users: numToSI(instance_stats.stats.users_active_month), 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> </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" class="btn btn-primary text-white sm:max-md:btn-block bg-gradient-to-r from-[#69D066] to-[#03A80E] normal-case"
href={buildUrl(domain)} href={buildUrl(domain)}
> >
{i18n.t("join_a_server")} {i18n.t("browse_instance")}
</a> </a>
<button <button
class="btn btn-secondary btn-outline text-white sm:max-md:btn-block normal-case" 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 <Badge
content={ 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" /> <Icon icon="users" classes="mr-2" />
<span>{users.toLocaleString()}</span> <span>{users.toLocaleString()}</span>
</div> </div>
@ -169,7 +198,12 @@ export const StatsBadges = ({ users, comments, monthlyUsers }) => (
/> />
<Badge <Badge
content={ 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" /> <Icon icon="message-circle" classes="mr-2" />
<span>{comments.toLocaleString()}</span> <span>{comments.toLocaleString()}</span>
</div> </div>
@ -177,7 +211,12 @@ export const StatsBadges = ({ users, comments, monthlyUsers }) => (
/> />
<Badge <Badge
content={ 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" /> <Icon icon="user-check" classes="mr-2" />
<span> <span>
{i18n.t("per_month", { {i18n.t("per_month", {

View file

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

View file

@ -82,7 +82,10 @@ export const Navbar = ({ footer = false }) => (
</div> </div>
<div className="navbar-end"> <div className="navbar-end">
{footer ? ( {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")} {i18n.t("copyright_line")}
</a> </a>
) : ( ) : (

View file

@ -2,20 +2,17 @@ import { Component } from "inferno";
import { Link } from "inferno-router"; import { Link } from "inferno-router";
import { Helmet } from "inferno-helmet"; import { Helmet } from "inferno-helmet";
import { i18n } from "../i18next"; import { i18n } from "../i18next";
import { isBrowser } from "../utils"; import { isBrowser, mdToHtml } from "../utils";
import { news_md } from "../translations/news"; import { news_md } from "../translations/news";
import { Badge, BottomSpacer, TEXT_GRADIENT } from "./common"; import { BottomSpacer, TEXT_GRADIENT } from "./common";
import { Icon } from "./icon";
const title = i18n.t("news"); const title = i18n.t("news");
const authors = ["nutomic", "dessalines"];
const news_reversed = news_md.reverse(); const news_reversed = news_md.reverse();
interface NewsInfo { interface NewsInfo {
title: string; title: string;
dateStr: string; dateStr: string;
preview: string; preview: string;
authors: Array<string>;
url: string; url: string;
} }
@ -27,7 +24,6 @@ function buildNewsInfoArray(): Array<NewsInfo> {
dateStr: split[0], dateStr: split[0],
title: split[1], title: split[1],
preview: split[2] || previewMarkdown(n.markdown), preview: split[2] || previewMarkdown(n.markdown),
authors,
url: `news/${titleToUrl(n.title)}`, url: `news/${titleToUrl(n.title)}`,
}; };
}); });
@ -38,7 +34,11 @@ function titleToUrl(title: string): string {
} }
function previewMarkdown(markdown: 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 = () => ( const TitleBlock = () => (
@ -61,14 +61,13 @@ const NewsCard = ({ news }: NewsProps) => (
{news.title} {news.title}
</Link> </Link>
<div className="text-sm text-gray-500">{news.dateStr}</div> <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>
<div className="text-sm text-gray-300">{news.preview}</div> {news.preview && (
<div
className="text-sm text-gray-300"
dangerouslySetInnerHTML={mdToHtml(news.preview)}
/>
)}
</div> </div>
<Link <Link
to={news.url} to={news.url}
@ -81,17 +80,6 @@ const NewsCard = ({ news }: NewsProps) => (
</div> </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> { export class News extends Component<any, any> {
constructor(props: any, context: any) { constructor(props: any, context: any) {
super(props, context); super(props, context);

View file

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