mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-23 12:51:13 +00:00
Add card to federation success page
This commit is contained in:
parent
b88de28f2e
commit
235843f96f
6 changed files with 200 additions and 85 deletions
|
@ -249,7 +249,6 @@ hr {
|
|||
|
||||
.fl-1 {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.img-blur {
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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>
|
||||
|
|
68
src/shared/components/common/subscribe-button.tsx
Normal file
68
src/shared/components/common/subscribe-button.tsx
Normal 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>
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,11 +41,85 @@ 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) {
|
||||
|
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue