mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-25 07:32:19 -05:00
Support page
This commit is contained in:
parent
318f7a5043
commit
057c4cf18d
12
app/components/icons/Heart.tsx
Normal file
12
app/components/icons/Heart.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export function HeartIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className={className}
|
||||
>
|
||||
<path d="M11.645 20.91l-.007-.003-.022-.012a15.247 15.247 0 01-.383-.218 25.18 25.18 0 01-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0112 5.052 5.5 5.5 0 0116.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 01-4.244 3.17 15.247 15.247 0 01-.383.219l-.022.012-.007.004-.003.001a.752.752 0 01-.704 0l-.003-.001z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -7,10 +7,10 @@ import {
|
|||
FAQ_PAGE,
|
||||
SENDOU_INK_DISCORD_URL,
|
||||
SENDOU_INK_GITHUB_URL,
|
||||
SENDOU_INK_PATREON_URL,
|
||||
SENDOU_INK_TWITTER_URL,
|
||||
SENDOU_LOVE_EMOJI_PATH,
|
||||
SPLATOON_2_SENDOU_IN_URL,
|
||||
SUPPORT_PAGE,
|
||||
userPage,
|
||||
} from "~/utils/urls";
|
||||
import { DiscordIcon } from "../icons/Discord";
|
||||
|
|
@ -69,17 +69,12 @@ export function Footer({
|
|||
</div>{" "}
|
||||
<DiscordIcon className="layout__footer__social-icon discord" />
|
||||
</a>
|
||||
<a
|
||||
className="layout__footer__social-link"
|
||||
href={SENDOU_INK_PATREON_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Link className="layout__footer__social-link" to={SUPPORT_PAGE}>
|
||||
<div className="layout__footer__social-header">
|
||||
Patreon<p>{t("footer.patreon.subtitle")}</p>
|
||||
</div>{" "}
|
||||
<PatreonIcon className="layout__footer__social-icon patreon" />
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
{patrons.length > 0 ? (
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ import { SideNav } from "./SideNav";
|
|||
import { UserItem } from "./UserItem";
|
||||
import { LanguageChanger } from "./LanguageChanger";
|
||||
import { ThemeChanger } from "./ThemeChanger";
|
||||
import { LinkButton } from "../Button";
|
||||
import { SUPPORT_PAGE } from "~/utils/urls";
|
||||
import { HeartIcon } from "../icons/Heart";
|
||||
import { useUser } from "~/modules/auth";
|
||||
|
||||
function useBreadcrumbs() {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -39,6 +43,7 @@ export const Layout = React.memo(function Layout({
|
|||
patrons?: RootLoaderData["patrons"];
|
||||
isCatchBoundary?: boolean;
|
||||
}) {
|
||||
const user = useUser();
|
||||
const { t } = useTranslation(["common"]);
|
||||
const location = useLocation();
|
||||
const breadcrumbs = useBreadcrumbs();
|
||||
|
|
@ -65,14 +70,37 @@ export const Layout = React.memo(function Layout({
|
|||
})}
|
||||
{isFrontPage ? (
|
||||
<>
|
||||
<div className="layout__breadcrumb-separator">-</div>
|
||||
<div className="layout__breadcrumb">
|
||||
<div className="layout__breadcrumb-separator mobile-hidden">
|
||||
-
|
||||
</div>
|
||||
<div className="layout__breadcrumb mobile-hidden">
|
||||
{t("common:websiteSubtitle")}
|
||||
</div>
|
||||
{typeof user?.patronTier !== "number" ? (
|
||||
<LinkButton
|
||||
to={SUPPORT_PAGE}
|
||||
size="tiny"
|
||||
icon={<HeartIcon />}
|
||||
variant="outlined"
|
||||
className="ml-auto desktop-hidden"
|
||||
>
|
||||
{t("common:pages.support")}
|
||||
</LinkButton>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="layout__header__right-container">
|
||||
{typeof user?.patronTier !== "number" ? (
|
||||
<LinkButton
|
||||
to={SUPPORT_PAGE}
|
||||
size="tiny"
|
||||
icon={<HeartIcon />}
|
||||
variant="outlined"
|
||||
>
|
||||
{t("common:pages.support")}
|
||||
</LinkButton>
|
||||
) : null}
|
||||
<LanguageChanger />
|
||||
<ThemeChanger />
|
||||
{!isCatchBoundary ? <UserItem /> : null}
|
||||
|
|
|
|||
167
app/features/info/routes/support.tsx
Normal file
167
app/features/info/routes/support.tsx
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import type { LinksFunction, MetaFunction } from "@remix-run/node";
|
||||
import { Main } from "~/components/Main";
|
||||
import styles from "../support.css";
|
||||
import * as React from "react";
|
||||
import { CheckmarkIcon } from "~/components/icons/Checkmark";
|
||||
import { useTranslation } from "~/hooks/useTranslation";
|
||||
import { Badge } from "~/components/Badge";
|
||||
import { LinkButton } from "~/components/Button";
|
||||
import {
|
||||
PATREON_HOW_TO_CONNECT_DISCORD_URL,
|
||||
SENDOU_INK_PATREON_URL,
|
||||
} from "~/utils/urls";
|
||||
import { Popover } from "~/components/Popover";
|
||||
import { Trans } from "react-i18next";
|
||||
import { makeTitle } from "~/utils/strings";
|
||||
import { useSetTitle } from "~/hooks/useSetTitle";
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
return {
|
||||
title: makeTitle("Support"),
|
||||
};
|
||||
};
|
||||
|
||||
export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
};
|
||||
|
||||
// 1 = support
|
||||
// 2 = supporter
|
||||
// 3 = supporter+
|
||||
const PERKS = [
|
||||
{
|
||||
tier: 1,
|
||||
name: "supportMyWork",
|
||||
extraInfo: false,
|
||||
},
|
||||
{
|
||||
tier: 1,
|
||||
name: "nameInFooter",
|
||||
extraInfo: false,
|
||||
},
|
||||
{
|
||||
tier: 2,
|
||||
name: "badge",
|
||||
extraInfo: false,
|
||||
},
|
||||
{
|
||||
tier: 2,
|
||||
name: "discordColorRole",
|
||||
extraInfo: true,
|
||||
},
|
||||
{
|
||||
tier: 2,
|
||||
name: "seePlusPercentage",
|
||||
extraInfo: true,
|
||||
},
|
||||
{
|
||||
tier: 2,
|
||||
name: "autoValidatePictures",
|
||||
extraInfo: true,
|
||||
},
|
||||
] as const;
|
||||
|
||||
export default function SupportPage() {
|
||||
const { t } = useTranslation();
|
||||
useSetTitle(t("pages.support"));
|
||||
|
||||
return (
|
||||
<Main className="stack lg">
|
||||
<div className="stack md">
|
||||
<p>{t("support.intro.first")}</p>
|
||||
<p>{t("support.intro.second")}</p>
|
||||
<SupportTable />
|
||||
</div>
|
||||
<LinkButton
|
||||
size="big"
|
||||
to={SENDOU_INK_PATREON_URL}
|
||||
isExternal
|
||||
className="mx-auto"
|
||||
>
|
||||
{t("support.action")}
|
||||
</LinkButton>
|
||||
<p className="text-sm text-lighter">
|
||||
<Trans t={t} i18nKey="support.footer">
|
||||
After becoming a new patron you should connect{" "}
|
||||
<a
|
||||
href={PATREON_HOW_TO_CONNECT_DISCORD_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
your Discord on Patreon.com
|
||||
</a>
|
||||
. Afterwards the perks will take effect within 2 hours. If any
|
||||
questions or problems contact Sendou for support.
|
||||
</Trans>
|
||||
</p>
|
||||
</Main>
|
||||
);
|
||||
}
|
||||
|
||||
function SupportTable() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="support__table">
|
||||
<div />
|
||||
<div>Support</div>
|
||||
<div>Supporter</div>
|
||||
<div>Supporter+</div>
|
||||
{PERKS.map((perk) => {
|
||||
return (
|
||||
<React.Fragment key={perk.name}>
|
||||
<div className="justify-self-start">
|
||||
{t(`support.perk.${perk.name}`)}
|
||||
{perk.extraInfo ? (
|
||||
<Popover
|
||||
containerClassName="support__popover"
|
||||
triggerClassName="support__popover-trigger"
|
||||
buttonChildren={<>?</>}
|
||||
>
|
||||
{t(`support.perk.${perk.name}.extra` as any)}
|
||||
</Popover>
|
||||
) : null}
|
||||
</div>
|
||||
<div>
|
||||
{perk.tier === 1 ? (
|
||||
<CheckmarkIcon className="support__checkmark" />
|
||||
) : null}
|
||||
</div>
|
||||
{perk.name === "badge" ? (
|
||||
<div>
|
||||
<Badge
|
||||
isAnimated
|
||||
badge={{ code: "patreon", displayName: "" }}
|
||||
size={32}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{perk.tier <= 2 ? (
|
||||
<CheckmarkIcon className="support__checkmark" />
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
{perk.name === "badge" ? (
|
||||
<div>
|
||||
<Badge
|
||||
isAnimated
|
||||
badge={{
|
||||
code: "patreon_plus",
|
||||
displayName: "",
|
||||
}}
|
||||
size={32}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{perk.tier <= 3 ? (
|
||||
<CheckmarkIcon className="support__checkmark" />
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
29
app/features/info/support.css
Normal file
29
app/features/info/support.css
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
.support__table {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
place-items: center;
|
||||
font-size: var(--fonts-sm);
|
||||
row-gap: var(--s-2);
|
||||
}
|
||||
|
||||
.support__checkmark {
|
||||
color: var(--theme-success);
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
.support__popover {
|
||||
display: inline;
|
||||
margin-inline-start: var(--s-2);
|
||||
}
|
||||
|
||||
.support__popover-trigger {
|
||||
display: inline;
|
||||
height: 1rem;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: var(--theme);
|
||||
font-size: var(--fonts-md);
|
||||
font-weight: var(--bold);
|
||||
outline: initial;
|
||||
}
|
||||
|
|
@ -84,6 +84,7 @@ export interface RootLoaderData {
|
|||
| "plusTier"
|
||||
| "customUrl"
|
||||
| "discordName"
|
||||
| "patronTier"
|
||||
>;
|
||||
}
|
||||
|
||||
|
|
@ -108,6 +109,7 @@ export const loader: LoaderFunction = async ({ request }) => {
|
|||
id: user.id,
|
||||
plusTier: user.plusTier,
|
||||
customUrl: user.customUrl,
|
||||
patronTier: user.patronTier,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -310,3 +310,9 @@
|
|||
transform: translateX(-0.3rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.layout__breadcrumb-container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,6 +206,10 @@
|
|||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.justify-self-start {
|
||||
justify-self: flex-start;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
|
@ -221,3 +225,21 @@
|
|||
.label-no-spacing {
|
||||
--label-margin: 0;
|
||||
}
|
||||
|
||||
.mobile-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.desktop-hidden {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 480px) {
|
||||
.mobile-hidden {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.desktop-hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ export const SENDOU_INK_DISCORD_URL = "https://discord.gg/sendou";
|
|||
export const SENDOU_TWITTER_URL = "https://twitter.com/sendouc";
|
||||
export const SENDOU_INK_TWITTER_URL = "https://twitter.com/sendouink";
|
||||
export const SENDOU_INK_PATREON_URL = "https://patreon.com/sendou";
|
||||
export const PATREON_HOW_TO_CONNECT_DISCORD_URL =
|
||||
"https://support.patreon.com/hc/en-us/articles/212052266-How-do-I-connect-Discord-to-Patreon-Patron-";
|
||||
export const SENDOU_INK_GITHUB_URL = "https://github.com/Sendouc/sendou.ink";
|
||||
export const GITHUB_CONTRIBUTORS_URL =
|
||||
"https://github.com/Sendouc/sendou.ink/graphs/contributors";
|
||||
|
|
@ -63,6 +65,7 @@ export const LOG_OUT_URL = "/auth/logout";
|
|||
export const ADMIN_PAGE = "/admin";
|
||||
export const ARTICLES_MAIN_PAGE = "/a";
|
||||
export const FAQ_PAGE = "/faq";
|
||||
export const SUPPORT_PAGE = "/support";
|
||||
export const CONTRIBUTIONS_PAGE = "/contributions";
|
||||
export const BADGES_PAGE = "/badges";
|
||||
export const BUILDS_PAGE = "/builds";
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"pages.myPage": "My Page",
|
||||
"pages.u": "User Search",
|
||||
"pages.t": "Teams",
|
||||
"pages.support": "Support",
|
||||
|
||||
"header.profile": "Profile",
|
||||
"header.logout": "Log out",
|
||||
|
|
@ -133,5 +134,19 @@
|
|||
"upload.type.team-banner": "team picture banner",
|
||||
"upload.commonExplanation": "Before the image is publicly displayed a moderator will validate it. Images uploaded by patrons are shown without validation.",
|
||||
"upload.afterExplanation_one": "You have {{count}} image pending. The image will show up automatically after validation.",
|
||||
"upload.afterExplanation_other": "You have {{count}} images pending. The images will show up automatically after validation."
|
||||
"upload.afterExplanation_other": "You have {{count}} images pending. The images will show up automatically after validation.",
|
||||
|
||||
"support.intro.first": "Hello! I'm Sendou and sendou.ink is my project to provide tools and resources for the Splatoon community. The goal is to help everyone to improve and enjoy Splatoon whether you are brand new to the game or a seasoned veteran.",
|
||||
"support.intro.second": "If you like what I'm doing this page details how you can support my work and gain perks. Your support helps me pay for the hosting as well as sponsor my time spent on continuously impoving the project.",
|
||||
"support.action": "Support on Patreon",
|
||||
"support.footer": "After becoming a new patron you should connect <2>your Discord on Patreon.com</2>. Afterwards the perks will take effect within 2 hours. If any questions or problems contact Sendou for support.",
|
||||
"support.perk.supportMyWork": "Support my work",
|
||||
"support.perk.nameInFooter": "Name in footer",
|
||||
"support.perk.discordColorRole": "Discord color role",
|
||||
"support.perk.discordColorRole.extra": "You can have custom color role on Plus Server and sendou.ink's Discord server. Use the /color command of the Lohi bot.",
|
||||
"support.perk.badge": "Profile badge",
|
||||
"support.perk.seePlusPercentage": "See Plus Server voting percentage",
|
||||
"support.perk.seePlusPercentage.extra": "Normally only failed suggests see their voting percentage. With this you can see your own percentage.",
|
||||
"support.perk.autoValidatePictures": "Auto validate uploaded pictures",
|
||||
"support.perk.autoValidatePictures.extra": "Any images you upload (e.g. team profile/banner picture) get automatically validated. Normally it needs a moderator approval first. Note that this doesn't apply to images that were already submitted."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ module.exports = {
|
|||
route("/to/:id/join", "features/tournament/routes/to.$id.join.tsx");
|
||||
});
|
||||
|
||||
route("/support", "features/info/routes/support.tsx");
|
||||
|
||||
route("/t", "features/team/routes/t.tsx");
|
||||
route("/t/:customUrl", "features/team/routes/t.$customUrl.tsx");
|
||||
route("/t/:customUrl/edit", "features/team/routes/t.$customUrl.edit.tsx");
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user