Add card to federation success page

This commit is contained in:
SleeplessOne1917 2023-07-21 21:50:13 -04:00
parent b88de28f2e
commit 235843f96f
6 changed files with 200 additions and 85 deletions

View file

@ -249,7 +249,6 @@ hr {
.fl-1 {
flex: 1;
display: flex;
}
.img-blur {

View file

@ -72,7 +72,7 @@ export class App extends Component<AppProps, any> {
return (
<ErrorGuard>
<div tabIndex={-1} className="fl-1">
<div tabIndex={-1}>
{RouteComponent &&
(isAuthPath(path ?? "") ? (
<AuthGuard {...routeProps}>

View file

@ -13,6 +13,7 @@ interface PictrsImageProps {
nsfw?: boolean;
iconOverlay?: boolean;
pushup?: boolean;
cardTop?: boolean;
}
export class PictrsImage extends Component<PictrsImageProps, any> {
@ -21,28 +22,30 @@ export class PictrsImage extends Component<PictrsImageProps, any> {
}
render() {
const { src, icon, iconOverlay, banner, thumbnail, nsfw, pushup, cardTop } =
this.props;
return (
<picture>
<source srcSet={this.src("webp")} type="image/webp" />
<source srcSet={this.props.src} />
<source srcSet={src} />
<source srcSet={this.src("jpg")} type="image/jpeg" />
<img
src={this.props.src}
src={src}
alt={this.alt()}
title={this.alt()}
loading="lazy"
className={classNames("overflow-hidden pictrs-image", {
"img-fluid": !this.props.icon && !this.props.iconOverlay,
banner: this.props.banner,
"thumbnail rounded object-fit-cover":
this.props.thumbnail && !this.props.icon && !this.props.banner,
"img-expanded slight-radius":
!this.props.thumbnail && !this.props.icon,
"img-blur": this.props.thumbnail && this.props.nsfw,
"object-fit-cover img-icon me-1": this.props.icon,
"img-fluid": !icon && !iconOverlay,
banner,
"thumbnail rounded object-fit-cover": thumbnail && !icon && !banner,
"img-expanded slight-radius": !thumbnail && !icon,
"img-blur": thumbnail && nsfw,
"object-fit-cover img-icon me-1": icon,
"ms-2 mb-0 rounded-circle object-fit-cover avatar-overlay":
this.props.iconOverlay,
"avatar-pushup": this.props.pushup,
iconOverlay,
"avatar-pushup": pushup,
"card-img-top": cardTop,
})}
/>
</picture>

View file

@ -0,0 +1,68 @@
import { NoOptionI18nKeys } from "i18next";
import { MouseEventHandler } from "inferno";
import { SubscribedType } from "lemmy-js-client";
import { I18NextService, UserService } from "../../services";
import { Icon, Spinner } from "./icon";
interface SubscribeButtonProps {
subscribed: SubscribedType;
onFollow: MouseEventHandler;
onUnFollow: MouseEventHandler;
loading: boolean;
}
export function SubscribeButton({
subscribed,
onFollow,
onUnFollow,
loading,
}: SubscribeButtonProps) {
let i18key: NoOptionI18nKeys;
switch (subscribed) {
case "NotSubscribed": {
i18key = "subscribe";
break;
}
case "Subscribed": {
i18key = "joined";
break;
}
default: {
i18key = "subscribe_pending";
break;
}
}
if (!UserService.Instance.myUserInfo) {
return (
<button type="button" className="btn btn-secondary d-block mb-2 w-100">
{I18NextService.i18n.t("subscribe")}
</button>
);
}
return (
<button
type="button"
className={`btn btn-${
subscribed === "Pending" ? "warning" : "secondary"
} d-block mb-2 w-100`}
onClick={subscribed === "NotSubscribed" ? onFollow : onUnFollow}
>
{loading ? (
<Spinner />
) : (
<>
{subscribed === "Subscribed" && (
<Icon icon="check" classes="icon-inline me-1" />
)}
{I18NextService.i18n.t(i18key)}
</>
)}
</button>
);
}

View file

@ -1,7 +1,6 @@
import { myAuthRequired } from "@utils/app";
import { getUnixTime, hostname } from "@utils/helpers";
import { amAdmin, amMod, amTopMod } from "@utils/roles";
import { NoOptionI18nKeys } from "i18next";
import { Component, InfernoNode, linkEvent } from "inferno";
import { T } from "inferno-i18next-dess";
import { Link } from "inferno-router";
@ -23,6 +22,7 @@ import { I18NextService, UserService } from "../../services";
import { Badges } from "../common/badges";
import { BannerIconHeader } from "../common/banner-icon-header";
import { Icon, PurgeWarning, Spinner } from "../common/icon";
import { SubscribeButton } from "../common/subscribe-button";
import { CommunityForm } from "../community/community-form";
import { CommunityLink } from "../community/community-link";
import { PersonListing } from "../person/person-listing";
@ -124,7 +124,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
sidebar() {
const myUSerInfo = UserService.Instance.myUserInfo;
const { name, actor_id } = this.props.community_view.community;
const {
community: { name, actor_id },
subscribed,
} = this.props.community_view;
return (
<aside className="mb-3">
<div id="sidebarContainer">
@ -132,7 +135,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
<div className="card-body">
{this.communityTitle()}
{this.props.editable && this.adminButtons()}
{this.subscribeButton}
<SubscribeButton
subscribed={subscribed}
onFollow={linkEvent(this, this.handleFollowCommunity)}
onUnFollow={linkEvent(this, this.handleUnfollowCommunity)}
loading={this.state.followCommunityLoading}
/>
{this.canPost && this.createPost()}
{myUSerInfo && this.blockCommunity()}
{!myUSerInfo && (
@ -231,64 +239,6 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
);
}
get subscribeButton() {
const community_view = this.props.community_view;
let i18key: NoOptionI18nKeys;
switch (community_view.subscribed) {
case "NotSubscribed": {
i18key = "subscribe";
break;
}
case "Subscribed": {
i18key = "joined";
break;
}
case "Pending": {
i18key = "subscribe_pending";
break;
}
}
if (!UserService.Instance.myUserInfo) {
return (
<button type="button" className="btn btn-secondary d-block mb-2 w-100">
{I18NextService.i18n.t("subscribe")}
</button>
);
}
return (
<button
type="button"
className={`btn btn-${
community_view.subscribed === "Pending" ? "warning" : "secondary"
} d-block mb-2 w-100`}
onClick={linkEvent(
this,
community_view.subscribed === "NotSubscribed"
? this.handleFollowCommunity
: this.handleUnfollowCommunity
)}
>
{this.state.followCommunityLoading ? (
<Spinner />
) : (
<>
{community_view.subscribed === "Subscribed" && (
<Icon icon="check" classes="icon-inline me-1" />
)}
{I18NextService.i18n.t(i18key)}
</>
)}
</button>
);
}
blockCommunity() {
const { subscribed, blocked } = this.props.community_view;

View file

@ -1,15 +1,17 @@
import { myAuthRequired, setIsoData } from "@utils/app";
import { getQueryParams } from "@utils/helpers";
import { QueryParams, RouteDataResponse } from "@utils/types";
import { Component } from "inferno";
import { Link } from "inferno-router";
import { ResolveObjectResponse } from "lemmy-js-client";
import { Component, linkEvent } from "inferno";
import { CommunityView, ResolveObjectResponse } from "lemmy-js-client";
import { InitialFetchRequest } from "../interfaces";
import { FirstLoadService, HttpService, I18NextService } from "../services";
import { RequestState } from "../services/HttpService";
import { HtmlTags } from "./common/html-tags";
import { Spinner } from "./common/icon";
import { LoadingEllipses } from "./common/loading-ellipses";
import { PictrsImage } from "./common/pictrs-image";
import { SubscribeButton } from "./common/subscribe-button";
import { CommunityLink } from "./community/community-link";
interface RemoteFetchProps {
uri?: string;
@ -22,6 +24,7 @@ type RemoteFetchData = RouteDataResponse<{
interface RemoteFetchState {
resolveObjectRes: RequestState<ResolveObjectResponse>;
isIsomorphic: boolean;
followCommunityLoading: boolean;
}
const getUriFromQuery = (uri?: string): string | undefined =>
@ -38,18 +41,92 @@ function uriToQuery(uri: string) {
return match ? `!${match[2]}@${match[1]}` : "";
}
const _test_com_view: CommunityView = {
community: {
id: 2,
name: "test",
title: "Testy",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
removed: false,
published: "2023-07-08T03:12:46.323277",
updated: "2023-07-22T00:29:46.425846",
deleted: false,
nsfw: false,
actor_id: "https://localhost/c/test",
local: true,
icon: "http://localhost:1236/pictrs/image/dc71f5e8-fdb2-48ae-8a49-f504e4adaf26.png",
banner:
"http://localhost:1236/pictrs/image/fdf28def-5c4d-49a6-84bc-fd6b5d901cc7.jpeg",
hidden: false,
posting_restricted_to_mods: false,
instance_id: 1,
followers_url: "dwdwdwd",
inbox_url: "dwdwdwdw",
},
subscribed: "Subscribed",
blocked: false,
counts: {
id: 1,
community_id: 2,
subscribers: 1,
posts: 1,
comments: 30,
published: "2023-07-08T03:12:46.323277",
users_active_day: 1,
users_active_week: 1,
users_active_month: 1,
users_active_half_year: 1,
hot_rank: 0,
},
};
async function handleToggleFollow(i: RemoteFetch, follow: boolean) {
const { resolveObjectRes } = i.state;
if (resolveObjectRes.state === "success" && resolveObjectRes.data.community) {
i.setState({
followCommunityLoading: true,
});
const communityRes = await HttpService.client.followCommunity({
auth: myAuthRequired(),
community_id: resolveObjectRes.data.community.community.id,
follow,
});
i.setState(prev => {
if (
communityRes.state === "success" &&
prev.resolveObjectRes.state === "success" &&
prev.resolveObjectRes.data.community
) {
prev.resolveObjectRes.data.community = communityRes.data.community_view;
}
return {
...prev,
followCommunityLoading: false,
};
});
}
}
const handleFollow = (i: RemoteFetch) => handleToggleFollow(i, true);
const handleUnfollow = (i: RemoteFetch) => handleToggleFollow(i, false);
export class RemoteFetch extends Component<any, RemoteFetchState> {
private isoData = setIsoData<RemoteFetchData>(this.context);
state: RemoteFetchState = {
resolveObjectRes: { state: "empty" },
isIsomorphic: false,
followCommunityLoading: false,
};
constructor(props: any, context: any) {
super(props, context);
if (FirstLoadService.isFirstLoad) {
// const { resolveObjectRes } = this.isoData.routeData;
// const { resolveObjectRes } = this.isoData.routeData;
this.state = {
...this.state,
@ -77,12 +154,12 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
render() {
return (
<div className="remote-fetch container-lg d-flex">
<div className="remote-fetch container-lg">
<HtmlTags
title={this.documentTitle}
path={this.context.router.route.match.url}
/>
<div className="row flex-grow-1 align-items-center">
<div className="row">
<div className="col-12 col-lg-6 offset-lg-3 text-center">
{this.content}
</div>
@ -92,7 +169,7 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
}
get content() {
const status = "loading" as "success" | "loading" | "empty";
const status = "success" as "success" | "loading" | "empty";
const { uri } = getRemoteFetchQueryParams();
const remoteCommunityName = uri ? uriToQuery(uri) : "remote community";
@ -102,9 +179,27 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
return (
<>
<h1>Community Federated!</h1>
<Link href="/" className="btn btn-lg bt-link btn-primary mt-5">
Click to visit com
</Link>
<div className="card mt-5">
{_test_com_view.community.banner && (
<PictrsImage src={_test_com_view.community.banner} cardTop />
)}
<div className="card-body">
<h2 className="card-title">
<CommunityLink community={_test_com_view.community} />
</h2>
{_test_com_view.community.description && (
<div className="card-text mb-3 preview-lines">
{_test_com_view.community.description}
</div>
)}
<SubscribeButton
subscribed={_test_com_view.subscribed}
onFollow={linkEvent(this, handleFollow)}
onUnFollow={linkEvent(this, handleUnfollow)}
loading={this.state.followCommunityLoading}
/>
</div>
</div>
</>
);
}