Almost done with donation page.

This commit is contained in:
Dessalines 2023-09-26 13:08:22 -04:00
parent 74d8257694
commit 14d463464d
8 changed files with 201 additions and 124 deletions

View file

@ -18,6 +18,7 @@
"repository": "https://github.com/LemmyNet/joinlemmy-site", "repository": "https://github.com/LemmyNet/joinlemmy-site",
"dependencies": { "dependencies": {
"@typescript-eslint/parser": "^5.60.1", "@typescript-eslint/parser": "^5.60.1",
"classnames": "^2.3.2",
"express": "~4.18.2", "express": "~4.18.2",
"i18next": "^23.2.6", "i18next": "^23.2.6",
"inferno": "^8.2.1", "inferno": "^8.2.1",

View file

@ -1,2 +1,59 @@
import { Link } from "inferno-router";
import { i18n } from "../i18next";
import { T } from "inferno-i18next";
import { Icon } from "./icon";
export const gradientTextClasses = export const gradientTextClasses =
"bg-gradient-to-r bg-clip-text text-transparent from-[#69D066] to-[#03A80E]"; "bg-gradient-to-r bg-clip-text text-transparent from-[#69D066] to-[#03A80E]";
export const Badge = ({ content }) => (
<div className="p-2 rounded-xl bg-neutral-800 text-gray-300 w-fit">
{content}
</div>
);
export const DonateDesc = () => (
<p className="text-sm text-gray-300 mb-6">
<T i18nKey="donate_desc">
#
<Link className="link" to="/donate">
#
</Link>
#
</T>
</p>
);
export const DonateButtons = () => (
<div class="flex flex-row justify-between gap-2">
<a class="btn btn-primary text-white" href="https://liberapay.com/Lemmy">
{i18n.t("support_on_liberapay")}
</a>
<a
class="btn btn-secondary text-white"
href="https://www.patreon.com/dessalines"
>
{i18n.t("support_on_patreon")}
</a>
<a
class="btn btn-primary text-white"
href="https://opencollective.com/lemmy"
>
{i18n.t("support_on_opencollective")}
</a>
</div>
);
export const SupportDonateBlock = () => (
<div className="flex flex-col items-center mt-16">
<div className="card card-bordered bg-neutral-800 shadow-xl">
<div className="card-body px-32 py-16">
<p class={`card-title text-4xl mb-3 ${gradientTextClasses}`}>
{i18n.t("support_donate")}
</p>
<DonateDesc />
<DonateButtons />
</div>
</div>
</div>
);

View file

@ -5,79 +5,124 @@ 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 { Badge, SupportDonateBlock, gradientTextClasses } from "./common";
import {
CODERS,
GOLD_SPONSORS,
HIGHLIGHTED_SPONSORS,
LATINUM_SPONSORS,
SILVER_SPONSORS,
SPONSORS,
Translation,
} from "./donate-definitions";
import classNames from "classnames";
import { Icon } from "./icon";
const avatarSize = 40; const avatarSize = 40;
const bannerWidth = 240; const bannerWidth = 240;
const bannerHeight = 101; const bannerHeight = 101;
interface LinkedSponsor { const SectionTitle = ({ title }) => (
name: string; <div className="text-2xl mb-3">{title}</div>
link: string; );
}
interface GoldSponsor { const ContributorsBlock = () => (
name: string; <div>
link: string; <SectionTitle title={i18n.t("contributers")} />
avatar: string; <p class="text-sm text-gray-300 mb-3">{i18n.t("thanks_coders")}</p>
} <CodersBlock />
<p class="text-sm text-gray-300 mt-6 mb-3">
{i18n.t("thanks_translators")}
</p>
<TranslatorsBlock />
</div>
);
let goldSponsors: GoldSponsor[] = []; const CodersBlock = () => (
<div className="card card-bordered bg-neutral-900 shadow-xl">
<div className="card-body p-4">
<PersonBadges persons={CODERS} />
</div>
</div>
);
let latinumSponsors: GoldSponsor[] = [ const TranslatorsBlock = () => (
{ <div className="card card-bordered bg-neutral-900 shadow-xl">
name: "NLnet", <div className="card-body p-4">
link: "https://nlnet.nl", <table>
avatar: "https://nlnet.nl/image/logo_nlnet.svg", {convertTranslators().map(t => (
}, <tr>
]; <td>
<div className="text-secondary">
<span>{languagesAll[t.lang].native}</span>
{t.country && <span> {countries[t.country].native}</span>}
<span>:</span>
</div>
</td>
<td>
<PersonBadges persons={t.translators} />
</td>
</tr>
))}
</table>
</div>
</div>
);
let silverSponsors: LinkedSponsor[] = []; interface PersonBadgeData {
let highlightedSponsors = ["DQW", "John Knapp"];
let sponsors = [
"Anthony",
"Remi Rampin",
"Cameron C",
"Vegard",
"0ti.me",
"Brendan",
"mexicanhalloween .",
"Arthur Nieuwland",
"Forrest Weghorst",
"Luke Black",
"Brandon Abbott",
"Eon Gattignolo",
];
export interface Coder {
name: string; name: string;
link?: string; link?: string;
greenOutline?: boolean;
greenAt?: boolean;
} }
let coders: Coder[] = [ interface PersonBadgeProps {
{ name: "dessalines", link: "https://mastodon.social/@dessalines" }, person: PersonBadgeData;
{ name: "Nutomic", link: "https://lemmy.ml/u/nutomic" },
{ name: "asonix", link: "https://github.com/asonix" },
{ name: "krawieck", link: "https://github.com/krawieck" },
{ name: "shilangyu", link: "https://github.com/shilangyu" },
{ name: "uuttff8", link: "https://github.com/uuttff8" },
{ name: "eiknat", link: "https://github.com/eiknat" },
{ name: "ernestwisniewski", link: "https://github.com/ernestwisniewski" },
{ name: "zacanger", link: "https://github.com/zacanger" },
{ name: "iav", link: "https://github.com/iav" },
];
export interface Translation {
lang: string;
country?: string;
translators: Translator[];
} }
export interface Translator { const PersonBadge = ({ person }: PersonBadgeProps) =>
name: string; person.link ? (
link?: string; <a href={person.link}>
<PersonBadgeInternal person={person} />
</a>
) : (
<PersonBadgeInternal person={person} />
);
const PersonBadgeInternal = ({ person }: PersonBadgeProps) => (
<Badge
content={
<div>
<Icon
icon="at-sign"
classes={classNames("fill-current text-gray-600", {
"text-primary": person.greenAt,
})}
/>
<span
className={classNames("ml-1", {
[`${gradientTextClasses}`]: person.link,
})}
>
{person.name}
</span>
</div>
}
/>
);
interface PersonBadgesProps {
persons: PersonBadgeData[];
} }
const PersonBadges = ({ persons }: PersonBadgesProps) => (
<div className="flex flex-row flex-wrap gap-2 mb-2">
{persons.map(p => (
<PersonBadge person={p} />
))}
</div>
);
export class Donate extends Component<any, any> { export class Donate extends Component<any, any> {
constructor(props: any, context: any) { constructor(props: any, context: any) {
super(props, context); super(props, context);
@ -92,10 +137,14 @@ export class Donate extends Component<any, any> {
render() { render() {
const title = i18n.t("support_title"); const title = i18n.t("support_title");
return ( return (
<div> <div className="container mx-auto">
<Helmet title={title}> <Helmet title={title}>
<meta property={"title"} content={title} /> <meta property={"title"} content={title} />
</Helmet> </Helmet>
<SupportDonateBlock />
<div className="mb-16" />
<ContributorsBlock />
<div class="container"> <div class="container">
<div class="text-center"> <div class="text-center">
<h1>{i18n.t("support_lemmy")}</h1> <h1>{i18n.t("support_lemmy")}</h1>
@ -109,11 +158,11 @@ export class Donate extends Component<any, any> {
</div> </div>
<div class="text-center"> <div class="text-center">
<h2>{i18n.t("sponsors")}</h2> <h2>{i18n.t("sponsors")}</h2>
{latinumSponsors.length > 0 && ( {LATINUM_SPONSORS.length > 0 && (
<div> <div>
<p>{i18n.t("gold_pressed_latinum_sponsors_desc")}</p> <p>{i18n.t("gold_pressed_latinum_sponsors_desc")}</p>
<div class="row is-horizontal-align"> <div class="row is-horizontal-align">
{latinumSponsors.map(s => ( {LATINUM_SPONSORS.map(s => (
<div class="col-6"> <div class="col-6">
<a class="button outline" href={s.link}> <a class="button outline" href={s.link}>
<img <img
@ -129,11 +178,11 @@ export class Donate extends Component<any, any> {
<br /> <br />
</div> </div>
)} )}
{goldSponsors.length > 0 && ( {GOLD_SPONSORS.length > 0 && (
<div> <div>
<p>{i18n.t("gold_sponsors_desc")}</p> <p>{i18n.t("gold_sponsors_desc")}</p>
<div class="row is-horizontal-align"> <div class="row is-horizontal-align">
{goldSponsors.map(s => ( {GOLD_SPONSORS.map(s => (
<div class="col"> <div class="col">
<a class="button outline gold" href={s.link}> <a class="button outline gold" href={s.link}>
<img <img
@ -150,11 +199,11 @@ export class Donate extends Component<any, any> {
<br /> <br />
</div> </div>
)} )}
{silverSponsors.length > 0 && ( {SILVER_SPONSORS.length > 0 && (
<div> <div>
<p>{i18n.t("silver_sponsors_desc")}</p> <p>{i18n.t("silver_sponsors_desc")}</p>
<div class="row is-horizontal-align"> <div class="row is-horizontal-align">
{silverSponsors.map(s => ( {SILVER_SPONSORS.map(s => (
<div class="col"> <div class="col">
<a class="button outline primary" href={s.link}> <a class="button outline primary" href={s.link}>
💎 {s.name} 💎 {s.name}
@ -167,12 +216,12 @@ export class Donate extends Component<any, any> {
)} )}
<p>{i18n.t("general_sponsors_desc")}</p> <p>{i18n.t("general_sponsors_desc")}</p>
<div class="row is-horizontal-align"> <div class="row is-horizontal-align">
{highlightedSponsors.map(s => ( {HIGHLIGHTED_SPONSORS.map(s => (
<div class="col"> <div class="col">
<div class="button outline primary">{s}</div> <div class="button outline primary">{s}</div>
</div> </div>
))} ))}
{sponsors.map(s => ( {SPONSORS.map(s => (
<div class="col"> <div class="col">
<div class="button outline">{s}</div> <div class="button outline">{s}</div>
</div> </div>
@ -257,14 +306,14 @@ export class Donate extends Component<any, any> {
<div> <div>
<p> <p>
<span>{i18n.t("thanks_coders")}</span> <span>{i18n.t("thanks_coders")}</span>
{coders.map((coder, i) => ( {CODERS.map((coder, i) => (
<span> <span>
{coder.link ? ( {coder.link ? (
<a href={coder.link}>{coder.name}</a> <a href={coder.link}>{coder.name}</a>
) : ( ) : (
<span>{coder.name}</span> <span>{coder.name}</span>
)} )}
<span>{i != coders.length - 1 ? ", " : " "}</span> <span>{i != CODERS.length - 1 ? ", " : " "}</span>
</span> </span>
))} ))}
</p> </p>

View file

@ -5,7 +5,7 @@ import { i18n } from "../i18next";
import { T } from "inferno-i18next"; import { T } from "inferno-i18next";
import { isBrowser } from "../utils"; import { isBrowser } from "../utils";
import { Icon } from "./icon"; import { Icon } from "./icon";
import { gradientTextClasses } from "./common"; import { SupportDonateBlock, gradientTextClasses } from "./common";
const TitleBlock = () => ( const TitleBlock = () => (
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
@ -354,52 +354,6 @@ const MoreFeaturesCard = ({ icons, text }) => (
</div> </div>
); );
const DonateDesc = () => (
<p className="text-sm text-gray-300 mb-6">
<T i18nKey="donate_desc">
#
<Link className="link" to="/donate">
#
</Link>
#
</T>
</p>
);
const DonateButtons = () => (
<div class="flex flex-row justify-between gap-2">
<a class="btn btn-primary text-white" href="https://liberapay.com/Lemmy">
{i18n.t("support_on_liberapay")}
</a>
<a
class="btn btn-secondary text-white"
href="https://www.patreon.com/dessalines"
>
{i18n.t("support_on_patreon")}
</a>
<a
class="btn btn-primary text-white"
href="https://opencollective.com/lemmy"
>
{i18n.t("support_on_opencollective")}
</a>
</div>
);
const SupportDonateBlock = () => (
<div className="flex flex-col items-center mt-16">
<div className="card card-bordered bg-neutral-800 shadow-xl">
<div className="card-body px-32 py-16">
<p class={`card-title text-4xl mb-3 ${gradientTextClasses}`}>
{i18n.t("support_donate")}
</p>
<DonateDesc />
<DonateButtons />
</div>
</div>
</div>
);
export class Main extends Component<any, any> { export class Main extends Component<any, any> {
constructor(props: any, context: any) { constructor(props: any, context: any) {
super(props, context); super(props, context);

View file

@ -29,7 +29,7 @@ export class NewsItem extends Component<any, any> {
<Helmet title={title}> <Helmet title={title}>
<meta property={"title"} content={title} /> <meta property={"title"} content={title} />
</Helmet> </Helmet>
<div className="flex flex-col items-center"> <div className="flex flex-col items-center mt-8">
<article className="prose prose-a:text-primary prose-h1:text-primary"> <article className="prose prose-a:text-primary prose-h1:text-primary">
<div dangerouslySetInnerHTML={mdToHtml(this.markdown)} /> <div dangerouslySetInnerHTML={mdToHtml(this.markdown)} />
</article> </article>

View file

@ -4,7 +4,8 @@ import { Helmet } from "inferno-helmet";
import { i18n } from "../i18next"; import { i18n } from "../i18next";
import { isBrowser } from "../utils"; import { isBrowser } from "../utils";
import { news_md } from "../translations/news"; import { news_md } from "../translations/news";
import { gradientTextClasses } from "./common"; import { Badge, gradientTextClasses } from "./common";
import { Icon } from "./icon";
const title = i18n.t("news"); const title = i18n.t("news");
const authors = ["nutomic", "dessalines"]; const authors = ["nutomic", "dessalines"];
@ -51,7 +52,7 @@ interface NewsProps {
} }
const NewsCard = ({ news }: NewsProps) => ( const NewsCard = ({ news }: NewsProps) => (
<div className="card card-bordered bg-neutral-800 shadow-xl mb-3"> <div className="card card-bordered bg-neutral-900 shadow-xl mb-3">
<div className="card-body"> <div className="card-body">
<div class="grid md:grid-cols-12 grid-cols-1 gap-4"> <div class="grid md:grid-cols-12 grid-cols-1 gap-4">
<div className="md:col-span-10"> <div className="md:col-span-10">
@ -60,9 +61,12 @@ const NewsCard = ({ news }: NewsProps) => (
{title} {title}
</Link> </Link>
<div className="text-sm text-gray-500">{news.dateStr}</div> <div className="text-sm text-gray-500">{news.dateStr}</div>
{authors.map(name => (
<AuthorBadge name={name} /> <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> <div className="text-sm text-gray-300">{news.preview}</div>
</div> </div>
@ -75,10 +79,14 @@ const NewsCard = ({ news }: NewsProps) => (
); );
const AuthorBadge = ({ name }) => ( const AuthorBadge = ({ name }) => (
<div className="badge bg-neutral-700"> <Badge
<code>@</code> content={
<span className="ml-1 text-gray-300">{name}</span> <div>
</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> {

View file

@ -20,6 +20,9 @@ export class Symbols extends Component<any, any> {
xmlnsXlink="http://www.w3.org/1999/xlink" xmlnsXlink="http://www.w3.org/1999/xlink"
> >
<defs> <defs>
<symbol id="icon-at-sign" viewBox="0 0 24 24">
<path d="M15 12c0 0.829-0.335 1.577-0.879 2.121s-1.292 0.879-2.121 0.879-1.577-0.335-2.121-0.879-0.879-1.292-0.879-2.121 0.335-1.577 0.879-2.121 1.292-0.879 2.121-0.879 1.577 0.335 2.121 0.879 0.879 1.292 0.879 2.121zM15.74 15.318c0.13 0.182 0.274 0.353 0.431 0.51 0.723 0.723 1.725 1.172 2.829 1.172s2.106-0.449 2.828-1.172 1.172-1.724 1.172-2.828v-1c0-3.037-1.233-5.789-3.222-7.778s-4.741-3.222-7.779-3.221-5.788 1.232-7.778 3.222-3.221 4.741-3.221 7.778 1.233 5.789 3.222 7.778 4.741 3.222 7.778 3.221c2.525 0 4.855-0.852 6.69-2.269 0.437-0.337 0.518-0.965 0.18-1.403s-0.965-0.518-1.403-0.18c-1.487 1.148-3.377 1.844-5.435 1.852-2.54-0.009-4.776-1.014-6.398-2.636-1.627-1.629-2.634-3.877-2.634-6.363s1.006-4.734 2.636-6.364 3.878-2.636 6.364-2.636 4.734 1.006 6.364 2.636 2.636 3.878 2.636 6.363v1c0 0.553-0.223 1.051-0.586 1.414s-0.861 0.586-1.414 0.586-1.051-0.223-1.414-0.586-0.586-0.861-0.586-1.414v-5c0-0.552-0.448-1-1-1s-1 0.448-1 1c-0.835-0.627-1.875-1-3-1-1.38 0-2.632 0.561-3.536 1.464s-1.464 2.156-1.464 3.536 0.561 2.632 1.464 3.536 2.156 1.464 3.536 1.464 2.632-0.561 3.536-1.464c0.070-0.070 0.139-0.143 0.205-0.217z"></path>
</symbol>
<symbol id="icon-embed" viewBox="0 0 40 32"> <symbol id="icon-embed" viewBox="0 0 40 32">
<path d="M26 23l3 3 10-10-10-10-3 3 7 7z"></path> <path d="M26 23l3 3 10-10-10-10-3 3 7 7z"></path>
<path d="M14 9l-3-3-10 10 10 10 3-3-7-7z"></path> <path d="M14 9l-3-3-10 10 10 10 3-3-7-7z"></path>

View file

@ -2335,6 +2335,11 @@ cidr-regex@1.0.6:
resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-1.0.6.tgz#74abfd619df370b9d54ab14475568e97dd64c0c1" resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-1.0.6.tgz#74abfd619df370b9d54ab14475568e97dd64c0c1"
integrity sha1-dKv9YZ3zcLnVSrFEdVaOl91kwME= integrity sha1-dKv9YZ3zcLnVSrFEdVaOl91kwME=
classnames@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
clean-stack@^2.0.0: clean-stack@^2.0.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"