Adding most of instances page.

This commit is contained in:
Dessalines 2023-09-26 21:53:24 -04:00
parent 14d463464d
commit 7f92343921
14 changed files with 547 additions and 319 deletions

@ -1 +1 @@
Subproject commit b5b5981499854a7e25f128a5c8cc541b258e7e09
Subproject commit f96ec38a927aa2477d9a1d21f6a637ed8838b913

@ -1 +1 @@
Subproject commit de9de2c53bee034d3824ecaa9a2104f8f341332e
Subproject commit 18da10858d8c63750beb06247947f25d91944741

View file

@ -7,6 +7,7 @@ import { NoMatch } from "./no-match";
import { Symbols } from "./symbols";
import { Navbar } from "./navbar";
import { Footer } from "./footer";
import { BACKGROUND_GRADIENT_1, BACKGROUND_GRADIENT_2 } from "./common";
export class App extends Component<any, any> {
constructor(props: any, context: any) {
@ -14,8 +15,8 @@ export class App extends Component<any, any> {
}
render() {
return (
<>
<div>
<div className={BACKGROUND_GRADIENT_1}>
<div className={BACKGROUND_GRADIENT_2}>
<Provider i18next={i18n}>
{/* <Navbar /> */}
<Switch>
@ -33,7 +34,7 @@ export class App extends Component<any, any> {
<Symbols />
</Provider>
</div>
</>
</div>
);
}
}

View file

@ -1,7 +1,7 @@
import { Component } from "inferno";
import { Helmet } from "inferno-helmet";
import { i18n } from "../i18next";
import { gradientTextClasses } from "./common";
import { TEXT_GRADIENT } from "./common";
import {
ANDROID_APPS,
API_LIBRARIES,
@ -14,10 +14,8 @@ import {
import { Icon } from "./icon";
const TitleBlock = () => (
<div className="flex flex-col items-center mt-16 mb-8">
<p className={`text-4xl ${gradientTextClasses} mb-3`}>
{i18n.t("lemmy_apps")}
</p>
<div className="flex flex-col items-center pt-16 mb-8">
<p className={`text-4xl ${TEXT_GRADIENT} mb-3`}>{i18n.t("lemmy_apps")}</p>
<p className="text-2xl text-gray-300 text-center">
{i18n.t("choose_from_apps")}
</p>
@ -34,7 +32,7 @@ const AppDetailsTitle = ({ app }: AppDetailsCardProps) => (
src={app.icon || "/static/assets/images/lemmy.svg"}
className="rounded-xl w-7 h-7"
/>
<a href={app.link} className={`card-title text-2xl ${gradientTextClasses}`}>
<a href={app.link} className={`card-title text-2xl ${TEXT_GRADIENT}`}>
{app.name}
</a>
</div>
@ -55,7 +53,7 @@ const AppDetailsButtons = ({ links }: AppDetailsButtonsProps) => (
);
const AppDetailsCard = ({ app }: AppDetailsCardProps) => (
<div className="card card-bordered bg-neutral-800 shadow-xl">
<div className="card card-bordered bg-neutral-900 shadow-xl">
<div className="card-body items-center">
<AppDetailsTitle app={app} />
<img
@ -106,14 +104,14 @@ const AppGrid = ({ apps }: AppGridProps) => (
const ApiLibrariesBlock = () => (
<div>
<AppTitle title={i18n.t("api_libraries")} />
<div className="card card-bordered bg-neutral-800 shadow-xl md:w-1/2">
<div className="card card-bordered bg-neutral-900 shadow-xl md:w-1/2">
<div className="card-body">
<ul>
{API_LIBRARIES.map(a => (
<li>
<span className={`${gradientTextClasses} mr-2`}></span>
<span className={`${TEXT_GRADIENT} mr-2`}></span>
<span>
<a href={a.link} className={`${gradientTextClasses}`}>
<a href={a.link} className={`${TEXT_GRADIENT}`}>
{a.name}
</a>
</span>

View file

@ -1,13 +1,23 @@
import { Link } from "inferno-router";
import { i18n } from "../i18next";
import { T } from "inferno-i18next";
import { Icon } from "./icon";
import classNames from "classnames";
export const gradientTextClasses =
export const TEXT_GRADIENT =
"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">
export const BACKGROUND_GRADIENT_1 =
"min-h-full bg-gradient-to-r from-transparent via-[#12D10E]/[.15] to-transparent";
export const BACKGROUND_GRADIENT_2 =
"min-h-full bg-gradient-to-b from-transparent to-black/[.30] to-20%";
export const Badge = ({ content, outline = false }) => (
<div
className={classNames("p-2 rounded-xl bg-neutral-800 text-gray-300 w-fit", {
"outline outline-primary": outline,
})}
>
{content}
</div>
);
@ -16,7 +26,7 @@ export const DonateDesc = () => (
<p className="text-sm text-gray-300 mb-6">
<T i18nKey="donate_desc">
#
<Link className="link" to="/donate">
<Link className="link link-primary" to="/donate">
#
</Link>
#
@ -25,18 +35,21 @@ export const DonateDesc = () => (
);
export const DonateButtons = () => (
<div class="flex flex-row justify-between gap-2">
<a class="btn btn-primary text-white" href="https://liberapay.com/Lemmy">
<div class="flex flex-row flex-wrap justify-between gap-2">
<a
class="btn btn-primary text-white sm:max-md:btn-block"
href="https://liberapay.com/Lemmy"
>
{i18n.t("support_on_liberapay")}
</a>
<a
class="btn btn-secondary text-white"
class="btn btn-secondary text-white sm:max-md:btn-block"
href="https://www.patreon.com/dessalines"
>
{i18n.t("support_on_patreon")}
</a>
<a
class="btn btn-primary text-white"
class="btn btn-primary text-white sm:max-md:btn-block"
href="https://opencollective.com/lemmy"
>
{i18n.t("support_on_opencollective")}
@ -45,10 +58,10 @@ export const DonateButtons = () => (
);
export const SupportDonateBlock = () => (
<div className="flex flex-col items-center mt-16">
<div className="flex flex-col items-center pt-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}`}>
<p class={`card-title text-4xl mb-3 ${TEXT_GRADIENT}`}>
{i18n.t("support_donate")}
</p>
<DonateDesc />

View file

@ -1,11 +1,11 @@
import { Component } from "inferno";
import { Helmet } from "inferno-helmet";
import { i18n } from "../i18next";
import { gradientTextClasses } from "./common";
import { TEXT_GRADIENT } from "./common";
import { Icon } from "./icon";
const TitleBlock = () => (
<div className={`mt-16 text-center text-4xl mb-8 ${gradientTextClasses}`}>
<div className={`pt-16 text-center text-4xl mb-8 ${TEXT_GRADIENT}`}>
{i18n.t("contact")}
</div>
);
@ -13,7 +13,7 @@ const TitleBlock = () => (
const ContactBlock = () => (
<div className="flex flex-col items-center">
<div className="card w-96 card-bordered bg-neutral-900 shadow-xl">
<div className="card-body items-center p-16">
<div className="card-body items-center p-8">
<ContactBtn
title="/c/lemmy_support"
url="https://lemmy.ml/c/lemmy_support"

View file

@ -0,0 +1,93 @@
export interface LinkedSponsor {
name: string;
link: string;
}
export interface GoldSponsor {
name: string;
link: string;
avatar: string;
}
export const GOLD_SPONSORS: GoldSponsor[] = [];
export const LATINUM_SPONSORS: GoldSponsor[] = [
{
name: "NLnet",
link: "https://nlnet.nl",
avatar: "https://nlnet.nl/image/logo_nlnet.svg",
},
];
export const SILVER_SPONSORS: LinkedSponsor[] = [];
export const HIGHLIGHTED_SPONSORS = ["DQW", "John Knapp"];
export const GENERAL_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;
link?: string;
}
export const CODERS: Coder[] = [
{ name: "dessalines", link: "https://mastodon.social/@dessalines" },
{ 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 {
name: 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",
},
];

View file

@ -5,29 +5,26 @@ import { T } from "inferno-i18next";
import { translators } from "../translations/translators";
import { languagesAll, countries } from "countries-list";
import { isBrowser } from "../utils";
import { Badge, SupportDonateBlock, gradientTextClasses } from "./common";
import { Badge, SupportDonateBlock, TEXT_GRADIENT } from "./common";
import {
CODERS,
GOLD_SPONSORS,
GoldSponsor,
HIGHLIGHTED_SPONSORS,
LATINUM_SPONSORS,
SILVER_SPONSORS,
SPONSORS,
GENERAL_SPONSORS,
Translation,
CRYPTOS,
} from "./donate-definitions";
import classNames from "classnames";
import { Icon } from "./icon";
const avatarSize = 40;
const bannerWidth = 240;
const bannerHeight = 101;
const SectionTitle = ({ title }) => (
<div className="text-2xl mb-3">{title}</div>
);
const ContributorsBlock = () => (
<div>
<div className="my-16">
<SectionTitle title={i18n.t("contributers")} />
<p class="text-sm text-gray-300 mb-3">{i18n.t("thanks_coders")}</p>
<CodersBlock />
@ -35,6 +32,21 @@ const ContributorsBlock = () => (
{i18n.t("thanks_translators")}
</p>
<TranslatorsBlock />
<div className="card card-bordered bg-neutral-900 shadow-xl text-center text-secondary">
<div className="card-body p-4">
<p>
<T i18nKey="add_weblate">
#
<a
className="link link-primary"
href="https://weblate.join-lemmy.org/projects/lemmy/"
>
#
</a>
</T>
</p>
</div>
</div>
</div>
);
@ -46,11 +58,36 @@ const CodersBlock = () => (
</div>
);
const TranslatorsBlock = () => (
const TranslatorsBlock = () => {
// Split these into two cards for md
const transArr = convertTranslators();
const halfway = Math.floor(transArr.length / 2);
const first = transArr.slice(0, halfway);
const second = transArr.slice(halfway, transArr.length);
return (
<div className="mb-8">
<div className="md:hidden">
<TranslatorsCard translations={transArr} />
</div>
<div className="sm:max-md:hidden grid grid-cols-2 gap-4">
<TranslatorsCard translations={first} />
<TranslatorsCard translations={second} />
</div>
</div>
);
};
interface TranslatorsCardProps {
translations: Translation[];
}
const TranslatorsCard = ({ translations }: TranslatorsCardProps) => (
<div className="card card-bordered bg-neutral-900 shadow-xl">
<div className="card-body p-4">
<table>
{convertTranslators().map(t => (
{translations.map(t => (
<tr>
<td>
<div className="text-secondary">
@ -69,11 +106,96 @@ const TranslatorsBlock = () => (
</div>
);
const SponsorsBlock = () => (
<div className="mb-16">
<SectionTitle title={i18n.t("sponsors")} />
<GoldSponsorCards
title={i18n.t("gold_pressed_latinum_sponsors_desc")}
sponsors={LATINUM_SPONSORS}
/>
<GoldSponsorCards
title={i18n.t("gold_sponsors_desc")}
sponsors={GOLD_SPONSORS}
/>
<GeneralSponsorCard />
</div>
);
interface GoldSponsorCardsProps {
title: string;
sponsors: GoldSponsor[];
}
const GoldSponsorCards = ({ title, sponsors }: GoldSponsorCardsProps) =>
sponsors.length > 0 && (
<div>
<p class="text-sm text-gray-300 mb-3">{title}</p>
<div class="flex flex-row flex-wrap gap-2 mb-2">
{sponsors.map(s => (
<a class="btn btn-primary btn-outline w-32 h-16" href={s.link}>
<img src={s.avatar} />
</a>
))}
</div>
</div>
);
const GeneralSponsorCard = () => {
const highlighted: PersonBadgeData[] = HIGHLIGHTED_SPONSORS.map(name => {
return { name, primaryOutline: true, primaryAt: true };
});
const general: PersonBadgeData[] = GENERAL_SPONSORS.map(name => {
return { name, primaryAt: true };
});
const combined = highlighted.concat(general);
return (
<div>
<p class="text-sm text-gray-300 mt-6 mb-3">
{i18n.t("general_sponsors_desc")}
</p>
<div className="card card-bordered bg-neutral-900 shadow-xl">
<div className="card-body p-4">
<PersonBadges persons={combined} />
</div>
</div>
</div>
);
};
const CryptoBlock = () => (
<div className="pb-16">
<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;
greenOutline?: boolean;
greenAt?: boolean;
primaryOutline?: boolean;
primaryAt?: boolean;
}
interface PersonBadgeProps {
@ -96,18 +218,19 @@ const PersonBadgeInternal = ({ person }: PersonBadgeProps) => (
<Icon
icon="at-sign"
classes={classNames("fill-current text-gray-600", {
"text-primary": person.greenAt,
"text-primary": person.primaryAt,
})}
/>
<span
className={classNames("ml-1", {
[`${gradientTextClasses}`]: person.link,
[`${TEXT_GRADIENT}`]: person.link,
})}
>
{person.name}
</span>
</div>
}
outline={person.primaryOutline}
/>
);
@ -142,181 +265,9 @@ export class Donate extends Component<any, any> {
<meta property={"title"} content={title} />
</Helmet>
<SupportDonateBlock />
<div className="mb-16" />
<ContributorsBlock />
<div class="container">
<div class="text-center">
<h1>{i18n.t("support_lemmy")}</h1>
</div>
<hr />
<div class="text-center">
<h2>{i18n.t("contributers")}</h2>
{this.codersLine()}
{this.translatorsLine()}
</div>
<div class="text-center">
<h2>{i18n.t("sponsors")}</h2>
{LATINUM_SPONSORS.length > 0 && (
<div>
<p>{i18n.t("gold_pressed_latinum_sponsors_desc")}</p>
<div class="row is-horizontal-align">
{LATINUM_SPONSORS.map(s => (
<div class="col-6">
<a class="button outline" href={s.link}>
<img
src={s.avatar}
width={bannerWidth}
height={bannerHeight}
/>
<div>{s.name}</div>
</a>
</div>
))}
</div>
<br />
</div>
)}
{GOLD_SPONSORS.length > 0 && (
<div>
<p>{i18n.t("gold_sponsors_desc")}</p>
<div class="row is-horizontal-align">
{GOLD_SPONSORS.map(s => (
<div class="col">
<a class="button outline gold" href={s.link}>
<img
class="is-rounded"
src={s.avatar}
width={avatarSize}
height={avatarSize}
/>
<div>{s.name}</div>
</a>
</div>
))}
</div>
<br />
</div>
)}
{SILVER_SPONSORS.length > 0 && (
<div>
<p>{i18n.t("silver_sponsors_desc")}</p>
<div class="row is-horizontal-align">
{SILVER_SPONSORS.map(s => (
<div class="col">
<a class="button outline primary" href={s.link}>
💎 {s.name}
</a>
</div>
))}
</div>
<br />
</div>
)}
<p>{i18n.t("general_sponsors_desc")}</p>
<div class="row is-horizontal-align">
{HIGHLIGHTED_SPONSORS.map(s => (
<div class="col">
<div class="button outline primary">{s}</div>
</div>
))}
{SPONSORS.map(s => (
<div class="col">
<div class="button outline">{s}</div>
</div>
))}
</div>
</div>
<div class="text-center">
<h1>Crypto</h1>
<table>
<tr>
<td>bitcoin</td>
<td>
<code>1Hefs7miXS5ff5Ck5xvmjKjXf5242KzRtK</code>
</td>
</tr>
<tr>
<td>ethereum</td>
<td>
<code>0x400c96c96acbC6E7B3B43B1dc1BB446540a88A01</code>
</td>
</tr>
<tr>
<td>monero</td>
<td>
<code>
41taVyY6e1xApqKyMVDRVxJ76sPkfZhALLTjRvVKpaAh2pBd4wv9RgYj1tSPrx8wc6iE1uWUfjtQdTmTy2FGMeChGVKPQuV
</code>
</td>
</tr>
<tr>
<td>cardano</td>
<td>
<code>
addr1q858t89l2ym6xmrugjs0af9cslfwvnvsh2xxp6x4dcez7pf5tushkp4wl7zxfhm2djp6gq60dk4cmc7seaza5p3slx0sakjutm
</code>
</td>
</tr>
</table>
</div>
</div>
</div>
);
}
translatorsLine() {
return (
<div>
<p>
<span>{i18n.t("thanks_translators")}</span>
{convertTranslators().map(t => (
<span>
<span class="text-error">{languagesAll[t.lang].native}</span>
{t.country && (
<span class="text-error"> {countries[t.country].native}</span>
)}
<span>: </span>
{t.translators.map((translator, i) => (
<span>
{translator.link ? (
<a href={translator.link}>{translator.name}</a>
) : (
<span>{translator.name}</span>
)}
<span>{i != t.translators.length - 1 ? ", " : " "}</span>
</span>
))}
</span>
))}
</p>
<p>
<T i18nKey="add_weblate">
#<a href="https://weblate.join-lemmy.org/projects/lemmy/">#</a>
</T>
</p>
</div>
);
}
codersLine() {
return (
<div>
<p>
<span>{i18n.t("thanks_coders")}</span>
{CODERS.map((coder, i) => (
<span>
{coder.link ? (
<a href={coder.link}>{coder.name}</a>
) : (
<span>{coder.name}</span>
)}
<span>{i != CODERS.length - 1 ? ", " : " "}</span>
</span>
))}
</p>
<SponsorsBlock />
<CryptoBlock />
</div>
);
}

View file

@ -0,0 +1,31 @@
export interface InstanceHelper {
name: string;
link: string;
}
export const INSTANCE_HELPERS: InstanceHelper[] = [
{
name: "Lemmy Community Explorer",
link: "https://lemmyverse.net/communities",
},
{
name: "lemmy.fediverse.observer",
link: "https://lemmy.fediverse.observer/list",
},
{
name: "Awesome-Lemmy-Instances on GitHub",
link: "https://github.com/maltfield/awesome-lemmy-instances",
},
{
name: "The-Federation.info Lemmy Instances Page",
link: "https://the-federation.info/platform/73",
},
{
name: "Feddit's Lemmymap",
link: "https://lemmymap.feddit.de/",
},
{
name: "Feddit's Lemmy Community Browser",
link: "https://browse.feddit.de/",
},
];

View file

@ -3,60 +3,209 @@ import { Helmet } from "inferno-helmet";
import { i18n } from "../i18next";
import { instance_stats } from "../instance_stats";
import { numToSI } from "../utils";
import { Badge, TEXT_GRADIENT } from "./common";
import { INSTANCE_HELPERS } from "./instances-definitions";
import { Icon } from "./icon";
export class Instances extends Component<any, any> {
constructor(props: any, context: any) {
super(props, context);
const TitleBlock = () => (
<div className="flex flex-col items-center pt-16 mb-16">
<p className={`text-4xl ${TEXT_GRADIENT} mb-3`}>
{i18n.t("lemmy_servers")}
</p>
<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),
})}
</p>
</div>
);
const ComparisonBlock = () => (
<div>
<div className="text-md text-gray-300 mb-3">
{i18n.t("instance_comparison")}:
</div>
<div className="card card-bordered bg-neutral-900 shadow-xl">
<div className="card-body p-4">
<div className="flex flex-row flex-wrap gap-4 mb-2">
{INSTANCE_HELPERS.map(i => (
<Badge
content={
<a href={i.link}>
<Icon
icon="embed"
classes={`fill-current text-primary mr-2`}
/>
<span className="text-gray-300">{i.name}</span>
</a>
}
/>
))}
</div>
</div>
</div>
</div>
);
biasedRandom(active_users, avg, max) {
const SectionTitle = ({ title }) => (
<div className="text-2xl mb-3">{title}</div>
);
interface InstanceCardGridProps {
title: string;
instances: any[];
}
const InstanceCardGrid = ({ title, instances }: InstanceCardGridProps) => (
<div className="my-16">
<SectionTitle title={title} />
<div className="grid md:grid-cols-3 grid-cols-1 gap-4">
{instances.map(i => (
<InstanceCard instance={i} />
))}
</div>
</div>
);
interface InstanceCardProps {
instance: any;
}
const InstanceCard = ({ instance }: InstanceCardProps) => {
const domain = instance.domain;
const description = instance.site_info.site_view.site.description;
const icon =
instance.site_info.site_view.site.icon || "/static/assets/images/lemmy.svg";
const users = instance.site_info.site_view.counts.users;
const comments = instance.site_info.site_view.counts.comments;
const monthlyUsers = instance.site_info.site_view.counts.users_active_month;
return (
<div className="card card-bordered bg-neutral-900 shadow-xl">
<div className="card-body p-4">
<div className="flex flex-row flex-wrap gap-4">
<InstanceIcon icon={icon} />
<InstanceStats
users={users}
comments={comments}
monthlyUsers={monthlyUsers}
/>
</div>
<div className={`text-2xl font-bold ${TEXT_GRADIENT}`}>{domain}</div>
<p className="text-sm text-gray-300 mb-2">{description}</p>
<div class="flex flex-row flex-wrap justify-between gap-2">
<a
class="btn btn-primary text-white sm:max-md:btn-block bg-gradient-to-r from-[#69D066] to-[#03A80E]"
href="https://liberapay.com/Lemmy"
>
{i18n.t("join_a_server")}
</a>
<a
class="btn btn-secondary btn-outline text-white sm:max-md:btn-block"
href="https://www.patreon.com/dessalines"
>
{/* TODO add more information */}
{i18n.t("more_features")}
</a>
</div>
</div>
</div>
);
};
const InstanceIcon = ({ icon }) => (
<div className="rounded-xl bg-neutral-800 p-4">
<img className="w-24 h-24" src={icon} />
</div>
);
const InstanceStats = ({ users, comments, monthlyUsers }) => (
<div className="flex flex-col flex-wrap justify-between">
<Badge
content={
<div className="text-sm text-gray-500">
<Icon icon="users" classes="mr-2" />
<span>{users.toLocaleString()}</span>
</div>
}
/>
<Badge
content={
<div className="text-sm text-gray-500">
<Icon icon="message-circle" classes="mr-2" />
<span>{comments.toLocaleString()}</span>
</div>
}
/>
<Badge
content={
<div className="text-sm text-gray-500">
<Icon icon="user-check" classes="mr-2" />
<span>
{i18n.t("users_active_per_month", {
formattedCount: monthlyUsers.toLocaleString(),
})}
</span>
</div>
}
/>
</div>
);
function biasedRandom(activeUsers: number, avg: number, max: number): number {
// Lets introduce a better bias to random shuffle instances list
var influence = 1.25;
var rnd = Math.random() * (max / influence) + active_users;
var mix = Math.random() * influence;
const influence = 1.25;
const rnd = Math.random() * (max / influence) + activeUsers;
const mix = Math.random() * influence;
return rnd * (1 - mix) + avg * mix;
}
}
averageFunc(values: any) {
return values.reduce((a, b) => a + b) / values.length;
}
function average(arr: number[]): number {
return arr.reduce((a, b) => a + b, 0) / arr.length;
}
render() {
const title = i18n.t("join_title");
interface InstanceList {
recommended: any[];
popular: any[];
}
var recommended_instances = instance_stats.recommended[i18n.language];
if (!recommended_instances) {
recommended_instances = instance_stats.recommended["en"];
}
function buildInstanceList(): InstanceList {
const recommendedList =
instance_stats.recommended[i18n.language] ??
instance_stats.recommended["en"];
var recommended = [];
var remaining = [];
var values = [];
let recommended = [];
let popular = [];
let usersActiveMonth = [];
for (var i of instance_stats.stats.instance_details) {
if (recommended_instances.indexOf(i.domain) > -1) {
// Loop over all the instances, and add them to the two lists
for (const i of instance_stats.stats.instance_details) {
if (recommendedList.indexOf(i.domain) > -1) {
recommended.push(i);
} else {
remaining.push(i);
popular.push(i);
}
values.push(i.site_info.site_view.counts.users_active_month);
usersActiveMonth.push(i.site_info.site_view.counts.users_active_month);
}
// Use these values for the shuffle
const avgMonthlyUsers = this.averageFunc(values);
const maxMonthlyUsers = Math.max(...values);
const avgMonthlyUsers = average(usersActiveMonth);
const maxMonthlyUsers = Math.max(...usersActiveMonth);
let recommended2 = recommended
// Randomize the recommended
recommended = recommended
.map(value => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value);
// BIASED sorting for instances, based on the min/max of users_active_month
let remaining2 = remaining
popular = popular
.map(i => ({
instance: i,
sort: this.biasedRandom(
sort: biasedRandom(
i.site_info.site_view.counts.users_active_month,
avgMonthlyUsers,
maxMonthlyUsers,
@ -65,54 +214,37 @@ export class Instances extends Component<any, any> {
.sort((a, b) => b.sort - a.sort)
.map(({ instance }) => instance);
return { recommended, popular };
}
export class Instances extends Component<any, any> {
constructor(props: any, context: any) {
super(props, context);
}
render() {
const title = i18n.t("join_title");
const instances = buildInstanceList();
return (
<div class="container">
<div className="container mx-auto">
<Helmet title={title}>
<meta property={"title"} content={title} />
</Helmet>
<h1 class="is-marginless">{i18n.t("lemmy_servers")}</h1>
{this.header()}
<br />
<br />
{this.renderList(i18n.t("recommended_instances"), recommended2)}
{this.renderList(i18n.t("popular_instances"), remaining2)}
<TitleBlock />
<ComparisonBlock />
<InstanceCardGrid
title={i18n.t("recommended_instances")}
instances={instances.recommended}
/>
<InstanceCardGrid
title={i18n.t("popular_instances")}
instances={instances.popular}
/>
</div>
);
}
header() {
return (
<i>
{i18n.t("instance_totals", {
instances: numToSI(instance_stats.stats.crawled_instances),
users: numToSI(instance_stats.stats.users_active_month),
})}
<p>
{i18n.t("instance_comparison")}:
<ul>
<li>
<a href="https://github.com/maltfield/awesome-lemmy-instances">
Awesome-Lemmy-Instances on GitHub
</a>
</li>
<li>
<a href="https://the-federation.info/platform/73">
the-federation.info Lemmy Instances Page
</a>
</li>
<li>
<a href="https://lemmymap.feddit.de/">Feddit's Lemmymap</a>
</li>
</ul>
{i18n.t("instance_browser")}{" "}
<a href="https://browse.feddit.de/">
Feddit's Lemmy Community Browser
</a>
</p>
</i>
);
}
renderList(header: string, instances: any[]) {
return (
<div>

View file

@ -5,12 +5,12 @@ import { i18n } from "../i18next";
import { T } from "inferno-i18next";
import { isBrowser } from "../utils";
import { Icon } from "./icon";
import { SupportDonateBlock, gradientTextClasses } from "./common";
import { SupportDonateBlock, TEXT_GRADIENT } from "./common";
const TitleBlock = () => (
<div className="flex flex-col items-center">
<div className="pt-16 flex flex-col items-center">
<div className="flex flex-col items-center mb-2">
<p className={`text-6xl font-bold ${gradientTextClasses}`}>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>
</div>
<div className="flex flex-row justify-around gap-2">
@ -178,7 +178,7 @@ const DiscussionPlatformBlock = () => (
// TODO add all of these ones
const MoreFeaturesBlock = () => (
<div className="mt-16">
<div className={`text-center text-4xl mb-8 ${gradientTextClasses}`}>
<div className={`text-center text-4xl mb-8 ${TEXT_GRADIENT}`}>
{i18n.t("more_features")}
</div>
<div className="grid md:grid-cols-5 grid-cols-1 gap-4">
@ -368,7 +368,7 @@ export class Main extends Component<any, any> {
render() {
const title = i18n.t("lemmy_title");
return (
<div className="container mx-auto">
<div className="container mx-auto bg-blue">
<Helmet title={title}>
<meta property={"title"} content={title} />
</Helmet>

View file

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

View file

@ -4,7 +4,7 @@ import { Helmet } from "inferno-helmet";
import { i18n } from "../i18next";
import { isBrowser } from "../utils";
import { news_md } from "../translations/news";
import { Badge, gradientTextClasses } from "./common";
import { Badge, TEXT_GRADIENT } from "./common";
import { Icon } from "./icon";
const title = i18n.t("news");
@ -42,7 +42,7 @@ function previewMarkdown(markdown: string): string {
}
const TitleBlock = () => (
<div className="mt-16 text-center text-4xl mb-8">{title}</div>
<div className="pt-16 text-center text-4xl mb-8">{title}</div>
);
const NewsCards = () => buildNewsInfoArray().map(n => <NewsCard news={n} />);
@ -57,7 +57,7 @@ const NewsCard = ({ news }: NewsProps) => (
<div class="grid md:grid-cols-12 grid-cols-1 gap-4">
<div className="md:col-span-10">
<div className="md:flex md:flex-row md:items-baseline md:space-x-3">
<Link to={news.url} className={`text-2xl ${gradientTextClasses}`}>
<Link to={news.url} className={`text-2xl ${TEXT_GRADIENT}`}>
{title}
</Link>
<div className="text-sm text-gray-500">{news.dateStr}</div>

View file

@ -20,6 +20,15 @@ export class Symbols extends Component<any, any> {
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<defs>
<symbol id="icon-user-check" viewBox="0 0 24 24">
<path d="M17 21v-2c0-1.38-0.561-2.632-1.464-3.536s-2.156-1.464-3.536-1.464h-7c-1.38 0-2.632 0.561-3.536 1.464s-1.464 2.156-1.464 3.536v2c0 0.552 0.448 1 1 1s1-0.448 1-1v-2c0-0.829 0.335-1.577 0.879-2.121s1.292-0.879 2.121-0.879h7c0.829 0 1.577 0.335 2.121 0.879s0.879 1.292 0.879 2.121v2c0 0.552 0.448 1 1 1s1-0.448 1-1zM13.5 7c0-1.38-0.561-2.632-1.464-3.536s-2.156-1.464-3.536-1.464-2.632 0.561-3.536 1.464-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.464 1.464-2.156 1.464-3.536zM11.5 7c0 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.121zM16.293 11.707l2 2c0.391 0.391 1.024 0.391 1.414 0l4-4c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0l-3.293 3.293-1.293-1.293c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414z"></path>
</symbol>
<symbol id="icon-users" viewBox="0 0 24 24">
<path d="M18 21v-2c0-1.38-0.561-2.632-1.464-3.536s-2.156-1.464-3.536-1.464h-8c-1.38 0-2.632 0.561-3.536 1.464s-1.464 2.156-1.464 3.536v2c0 0.552 0.448 1 1 1s1-0.448 1-1v-2c0-0.829 0.335-1.577 0.879-2.121s1.292-0.879 2.121-0.879h8c0.829 0 1.577 0.335 2.121 0.879s0.879 1.292 0.879 2.121v2c0 0.552 0.448 1 1 1s1-0.448 1-1zM14 7c0-1.38-0.561-2.632-1.464-3.536s-2.156-1.464-3.536-1.464-2.632 0.561-3.536 1.464-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.464 1.464-2.156 1.464-3.536zM12 7c0 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.121zM24 21v-2c-0.001-1.245-0.457-2.385-1.215-3.261-0.652-0.753-1.528-1.311-2.529-1.576-0.534-0.141-1.081 0.177-1.222 0.711s0.177 1.081 0.711 1.222c0.607 0.161 1.136 0.498 1.528 0.952 0.454 0.526 0.726 1.206 0.727 1.952v2c0 0.552 0.448 1 1 1s1-0.448 1-1zM15.752 4.099c0.803 0.206 1.445 0.715 1.837 1.377s0.531 1.47 0.325 2.273c-0.176 0.688-0.575 1.256-1.105 1.652-0.314 0.235-0.675 0.409-1.063 0.511-0.534 0.14-0.854 0.687-0.713 1.221s0.687 0.854 1.221 0.713c0.637-0.167 1.232-0.455 1.752-0.844 0.884-0.66 1.552-1.613 1.845-2.758 0.342-1.337 0.11-2.689-0.542-3.788s-1.725-1.953-3.062-2.296c-0.535-0.137-1.080 0.186-1.217 0.721s0.186 1.080 0.721 1.217z"></path>
</symbol>
<symbol id="icon-message-circle" viewBox="0 0 24 24">
<path d="M22 11.497v-0.497c0-0.017-0.001-0.038-0.002-0.058-0.136-2.338-1.113-4.461-2.642-6.051-1.602-1.667-3.814-2.752-6.301-2.889-0.016-0.001-0.036-0.002-0.055-0.002h-0.489c-1.405-0.016-2.882 0.31-4.264 1.008-1.223 0.621-2.291 1.488-3.139 2.535-1.322 1.634-2.107 3.705-2.108 5.946-0.014 1.275 0.253 2.61 0.824 3.877l-1.772 5.317c-0.066 0.196-0.072 0.418 0 0.632 0.175 0.524 0.741 0.807 1.265 0.632l5.314-1.771c1.16 0.527 2.484 0.826 3.876 0.823 1.372-0.009 2.714-0.308 3.941-0.866 1.912-0.871 3.54-2.373 4.541-4.375 0.644-1.249 1.015-2.715 1.011-4.261zM20 11.503c0.003 1.225-0.292 2.375-0.789 3.339-0.801 1.602-2.082 2.785-3.592 3.472-0.97 0.442-2.035 0.679-3.126 0.686-1.221 0.003-2.371-0.292-3.335-0.789-0.249-0.129-0.528-0.142-0.775-0.060l-3.803 1.268 1.268-3.803c0.088-0.263 0.060-0.537-0.056-0.767-0.552-1.094-0.804-2.254-0.792-3.338 0.001-1.789 0.619-3.42 1.663-4.709 0.671-0.829 1.518-1.517 2.49-2.010 1.092-0.552 2.252-0.804 3.336-0.792h0.456c1.962 0.107 3.704 0.961 4.969 2.277 1.202 1.251 1.972 2.916 2.086 4.753z"></path>
</symbol>
<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>