About 1/3rd done with adding redux.

This commit is contained in:
Dessalines 2023-12-05 22:12:54 -05:00
parent fc234716b0
commit 4298c3fb91
75 changed files with 499 additions and 373 deletions

View file

@ -43,6 +43,7 @@
"@babel/preset-typescript": "^7.21.5",
"@babel/runtime": "^7.21.5",
"@emoji-mart/data": "^1.1.0",
"@reduxjs/toolkit": "^2.0.1",
"@shortcm/qr-image": "^9.0.2",
"autosize": "^6.0.1",
"babel-loader": "^9.1.3",
@ -67,6 +68,7 @@
"inferno-helmet": "^5.2.1",
"inferno-hydrate": "^8.2.2",
"inferno-i18next-dess": "0.0.2",
"inferno-redux": "^8.2.2",
"inferno-router": "^8.2.2",
"inferno-server": "^8.2.2",
"jwt-decode": "^4.0.0",
@ -83,6 +85,7 @@
"markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0",
"mini-css-extract-plugin": "^2.7.5",
"redux": "^5.0.0",
"register-service-worker": "^1.7.2",
"run-node-webpack-plugin": "^1.3.0",
"rxjs": "^7.8.1",

View file

@ -1,4 +1,4 @@
import { initializeSite, setupDateFns } from "@utils/app";
import { initializeSite, setupDateFns, setupRedux } from "@utils/app";
import { hydrate } from "inferno-hydrate";
import { BrowserRouter } from "inferno-router";
import { App } from "../shared/components/app/app";
@ -6,16 +6,24 @@ import { App } from "../shared/components/app/app";
import "bootstrap/js/dist/collapse";
import "bootstrap/js/dist/dropdown";
import "bootstrap/js/dist/modal";
import { Provider } from "inferno-redux";
async function startClient() {
initializeSite(window.isoData.site_res);
const windowData = window.isoData;
delete window.isoData;
initializeSite(windowData?.site_res);
await setupDateFns();
const store = setupRedux(windowData);
const wrapper = (
<BrowserRouter>
<App />
</BrowserRouter>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
const root = document.getElementById("root");

View file

@ -20,6 +20,8 @@ import { createSsrHtml } from "../utils/create-ssr-html";
import { getErrorPageData } from "../utils/get-error-page-data";
import { setForwardedHeaders } from "../utils/set-forwarded-headers";
import { getJwtCookie } from "../utils/has-jwt-cookie";
import { Provider } from "inferno-redux";
import { configureStore, createSlice } from "@reduxjs/toolkit";
export default async (req: Request, res: Response) => {
try {
@ -108,10 +110,19 @@ export default async (req: Request, res: Response) => {
errorPageData,
};
const slice = createSlice({
name: "isoData",
initialState: { value: isoData },
reducers: {},
});
const store = configureStore({ reducer: slice.reducer });
const wrapper = (
<StaticRouter location={url} context={isoData}>
<App />
</StaticRouter>
<Provider store={store}>
<StaticRouter location={url} context={{}}>
<App />
</StaticRouter>
</Provider>
);
const root = renderToString(wrapper);

View file

@ -1,4 +1,4 @@
import { isAnonymousPath, isAuthPath, setIsoData } from "@utils/app";
import { isAnonymousPath, isAuthPath } from "@utils/app";
import { dataBsTheme } from "@utils/browser";
import { Component, RefObject, createRef, linkEvent } from "inferno";
import { Provider } from "inferno-i18next-dess";
@ -17,20 +17,20 @@ import AnonymousGuard from "../common/anonymous-guard";
import { CodeTheme } from "./code-theme";
export class App extends Component<any, any> {
private isoData: IsoDataOptionalSite = setIsoData(this.context);
private readonly mainContentRef: RefObject<HTMLElement>;
constructor(props: any, context: any) {
super(props, context);
this.mainContentRef = createRef();
}
handleJumpToContent(event) {
handleJumpToContent(event: any) {
event.preventDefault();
this.mainContentRef.current?.focus();
}
render() {
const siteRes = this.isoData.site_res;
const reduxState: IsoDataOptionalSite = this.context.store.getState().value;
const siteRes = reduxState.site_res;
const siteView = siteRes?.site_view;
return (

View file

@ -1,4 +1,3 @@
import { setIsoData } from "@utils/app";
import { Component } from "inferno";
import { T } from "inferno-i18next-dess";
import { Link } from "inferno-router";
@ -6,14 +5,13 @@ import { IsoDataOptionalSite } from "../../interfaces";
import { I18NextService } from "../../services";
export class ErrorPage extends Component<any, any> {
private isoData: IsoDataOptionalSite = setIsoData(this.context);
constructor(props: any, context: any) {
super(props, context);
}
render() {
const { errorPageData } = this.isoData;
const reduxState: IsoDataOptionalSite = this.context.store.getState();
const errorPageData = reduxState.errorPageData;
return (
<div className="error-page container-lg text-center">
@ -41,8 +39,7 @@ export class ErrorPage extends Component<any, any> {
<div>
{I18NextService.i18n.t("error_page_admin_matrix", {
instance:
this.isoData.site_res?.site_view.site.name ??
"this instance",
reduxState.site_res?.site_view.site.name ?? "this instance",
})}
</div>
<ul className="mx-auto mt-2" style={{ width: "fit-content" }}>

View file

@ -1,15 +1,15 @@
import { showAvatars } from "@utils/app";
import { isBrowser } from "@utils/browser";
import { numToSI } from "@utils/helpers";
import { amAdmin, canCreateCommunity } from "@utils/roles";
import { amAdmin, canCreateCommunity, moderatesSomething } from "@utils/roles";
import { Component, createRef, linkEvent } from "inferno";
import { NavLink } from "inferno-router";
import { GetSiteResponse } from "lemmy-js-client";
import { donateLemmyUrl } from "../../config";
import {
I18NextService,
UserService,
UnreadCounterService,
HttpService,
} from "../../services";
import { toast } from "../../toast";
import { Icon } from "../common/icon";
@ -38,7 +38,7 @@ function handleCollapseClick(i: Navbar) {
}
function handleLogOut(i: Navbar) {
UserService.Instance.logout();
HttpService._Instance.logout();
handleCollapseClick(i);
}
@ -94,7 +94,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
// TODO class active corresponding to current pages
render() {
const siteView = this.props.siteRes?.site_view;
const person = UserService.Instance.myUserInfo?.local_user_view.person;
const person = this.props.siteRes?.my_user?.local_user_view.person;
return (
<div className="shadow-sm">
<nav
@ -108,9 +108,10 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
className="d-flex align-items-center navbar-brand me-md-3"
onMouseUp={linkEvent(this, handleCollapseClick)}
>
{siteView?.site.icon && showAvatars() && (
<PictrsImage src={siteView.site.icon} icon />
)}
{siteView?.site.icon &&
showAvatars(this.props.siteRes?.my_user) && (
<PictrsImage src={siteView.site.icon} icon />
)}
{siteView?.site.name}
</NavLink>
{person && (
@ -133,7 +134,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
)}
</NavLink>
</li>
{UserService.Instance.moderatesSomething && (
{moderatesSomething(this.props.siteRes?.my_user) && (
<li className="nav-item nav-item-icon">
<NavLink
to="/reports"
@ -306,7 +307,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
)}
</NavLink>
</li>
{UserService.Instance.moderatesSomething && (
{moderatesSomething(this.props.siteRes?.my_user) && (
<li id="navModeration" className="nav-item">
<NavLink
className="nav-link d-inline-flex align-items-center d-md-inline-block"
@ -467,7 +468,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
}
requestNotificationPermission() {
if (UserService.Instance.myUserInfo) {
if (this.props.siteRes?.my_user) {
document.addEventListener("DOMContentLoaded", function () {
if (!Notification) {
toast(I18NextService.i18n.t("notifications_error"), "danger");

View file

@ -1,14 +1,15 @@
import { Component } from "inferno";
import { Helmet } from "inferno-helmet";
import { UserService } from "../../services";
import { MyUserInfo } from "lemmy-js-client";
interface Props {
defaultTheme: string;
myUserInfo?: MyUserInfo;
}
export class Theme extends Component<Props> {
render() {
const user = UserService.Instance.myUserInfo;
const user = this.props.myUserInfo;
const hasTheme = user?.local_user_view.local_user.theme !== "browser";
if (user && hasTheme) {

View file

@ -4,7 +4,7 @@ import { T } from "inferno-i18next-dess";
import { Link } from "inferno-router";
import { CreateComment, EditComment, Language } from "lemmy-js-client";
import { CommentNodeI } from "../../interfaces";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import { Icon } from "../common/icon";
import { MarkdownTextArea } from "../common/markdown-textarea";
@ -13,6 +13,7 @@ interface CommentFormProps {
* Can either be the parent, or the editable comment. The right side is a postId.
*/
node: CommentNodeI | number;
loggedIn: boolean;
finished?: boolean;
edit?: boolean;
disabled?: boolean;
@ -45,7 +46,7 @@ export class CommentForm extends Component<CommentFormProps, any> {
" ",
)}
>
{UserService.Instance.myUserInfo ? (
{this.props.loggedIn ? (
<MarkdownTextArea
initialContent={initialContent}
showLanguage

View file

@ -27,6 +27,7 @@ import {
Language,
MarkCommentReplyAsRead,
MarkPersonMentionAsRead,
MyUserInfo,
PersonMentionView,
PersonView,
PurgeComment,
@ -45,7 +46,7 @@ import {
VoteContentType,
} from "../../interfaces";
import { mdToHtml, mdToHtmlNoImages } from "../../markdown";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import { setupTippy } from "../../tippy";
import { Icon, PurgeWarning, Spinner } from "../common/icon";
import { MomentTime } from "../common/moment-time";
@ -97,6 +98,7 @@ interface CommentNodeState {
interface CommentNodeProps {
node: CommentNodeI;
myUserInfo?: MyUserInfo;
moderators?: CommunityModeratorView[];
admins?: PersonView[];
noBorder?: boolean;
@ -238,14 +240,18 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
cv.creator.id,
this.props.moderators,
this.props.admins,
UserService.Instance.myUserInfo,
this.props.myUserInfo,
true,
);
const canAdmin_ = canAdmin(cv.creator.id, this.props.admins);
const canAdmin_ = canAdmin(
cv.creator.id,
this.props.admins,
this.props.myUserInfo,
);
const canAdminOnSelf = canAdmin(
cv.creator.id,
this.props.admins,
UserService.Instance.myUserInfo,
this.props.myUserInfo,
true,
);
const isMod_ = cv.creator_is_moderator;
@ -253,6 +259,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
const amCommunityCreator_ = amCommunityCreator(
cv.creator.id,
this.props.moderators,
this.props.myUserInfo,
);
const moreRepliesBorderColor = this.props.node.depth
@ -327,7 +334,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{/* This is an expanding spacer for mobile */}
<div className="me-lg-5 flex-grow-1 flex-lg-grow-0 unselectable pointer mx-2" />
{showScores() && (
{showScores(this.props.myUserInfo) && (
<>
<span
className={`me-1 fw-bold ${this.scoreColor}`}
@ -352,6 +359,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{this.state.showEdit && (
<CommentForm
node={node}
loggedIn={!!this.props.myUserInfo}
edit
onReplyCancel={this.handleReplyCancel}
disabled={this.props.locked}
@ -410,7 +418,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)}
</button>
)}
{UserService.Instance.myUserInfo && !this.props.viewOnly && (
{this.props.myUserInfo && !this.props.viewOnly && (
<>
<VoteButtonsCompact
voteContentType={VoteContentType.Comment}
@ -1067,6 +1075,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{this.state.showReply && (
<CommentForm
node={node}
loggedIn={!!this.props.myUserInfo}
onReplyCancel={this.handleReplyCancel}
disabled={this.props.locked}
finished={this.props.finished.get(
@ -1082,6 +1091,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{!this.state.collapsed && node.children.length > 0 && (
<CommentNodes
nodes={node.children}
myUserInfo={this.props.myUserInfo}
locked={this.props.locked}
moderators={this.props.moderators}
admins={this.props.admins}
@ -1166,7 +1176,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
get myComment(): boolean {
return (
UserService.Instance.myUserInfo?.local_user_view.person.id ===
this.props.myUserInfo?.local_user_view.person.id ===
this.commentView.creator.id
);
}

View file

@ -19,6 +19,7 @@ import {
Language,
MarkCommentReplyAsRead,
MarkPersonMentionAsRead,
MyUserInfo,
PersonView,
PurgeComment,
PurgePerson,
@ -31,6 +32,7 @@ import { CommentNode } from "./comment-node";
interface CommentNodesProps {
nodes: CommentNodeI[];
myUserInfo?: MyUserInfo;
moderators?: CommunityModeratorView[];
admins?: PersonView[];
maxCommentsShown?: number;
@ -99,6 +101,7 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
<CommentNode
key={node.comment_view.comment.id}
node={node}
myUserInfo={this.props.myUserInfo}
noBorder={this.props.noBorder}
isTopLevel={this.props.isTopLevel}
viewOnly={this.props.viewOnly}

View file

@ -3,6 +3,7 @@ import { T } from "inferno-i18next-dess";
import {
CommentReportView,
CommentView,
MyUserInfo,
ResolveCommentReport,
} from "lemmy-js-client";
import { CommentNodeI, CommentViewType } from "../../interfaces";
@ -14,6 +15,7 @@ import { EMPTY_REQUEST } from "../../services/HttpService";
interface CommentReportProps {
report: CommentReportView;
myUserInfo?: MyUserInfo;
onResolveReport(form: ResolveCommentReport): void;
}
@ -75,6 +77,7 @@ export class CommentReport extends Component<
<div className="comment-report">
<CommentNode
node={node}
myUserInfo={this.props.myUserInfo}
viewType={CommentViewType.Flat}
enableDownvotes={true}
viewOnly={true}

View file

@ -1,17 +1,17 @@
import { setIsoData } from "@utils/app";
import { Component } from "inferno";
import { ErrorPage } from "../app/error-page";
import { IsoDataOptionalSite } from "../../interfaces";
class ErrorGuard extends Component<any, any> {
private isoData = setIsoData(this.context);
constructor(props: any, context: any) {
super(props, context);
}
render() {
const errorPageData = this.isoData.errorPageData;
const siteRes = this.isoData.site_res;
const reduxState: IsoDataOptionalSite = this.context.store.getState().value;
const errorPageData = reduxState.errorPageData;
const siteRes = reduxState.site_res;
console.log(`error guard data: ${reduxState.path}`);
if (errorPageData || !siteRes) {
return <ErrorPage />;

View file

@ -2,12 +2,13 @@ import { selectableLanguages } from "@utils/app";
import { randomStr } from "@utils/helpers";
import classNames from "classnames";
import { Component, linkEvent } from "inferno";
import { Language } from "lemmy-js-client";
import { I18NextService, UserService } from "../../services";
import { Language, MyUserInfo } from "lemmy-js-client";
import { I18NextService } from "../../services";
import { Icon } from "./icon";
interface LanguageSelectProps {
allLanguages: Language[];
myUserInfo?: MyUserInfo;
siteLanguages: number[];
selectedLanguageIds?: number[];
multiple?: boolean;
@ -97,7 +98,7 @@ export class LanguageSelect extends Component<LanguageSelectProps, any> {
this.props.siteLanguages,
this.props.showAll,
this.props.showSite,
UserService.Instance.myUserInfo,
this.props.myUserInfo,
);
return (

View file

@ -5,7 +5,7 @@ import classNames from "classnames";
import { NoOptionI18nKeys } from "i18next";
import { Component, linkEvent } from "inferno";
import { Prompt } from "inferno-router";
import { Language } from "lemmy-js-client";
import { Language, MyUserInfo } from "lemmy-js-client";
import {
concurrentImageUpload,
markdownFieldCharacterLimit,
@ -14,7 +14,7 @@ import {
relTags,
} from "../../config";
import { customEmojisLookup, mdToHtml, setupTribute } from "../../markdown";
import { HttpService, I18NextService, UserService } from "../../services";
import { HttpService, I18NextService } from "../../services";
import { setupTippy } from "../../tippy";
import { pictrsDeleteToast, toast } from "../../toast";
import { EmojiPicker } from "./emoji-picker";
@ -52,6 +52,7 @@ interface MarkdownTextAreaProps {
onSubmit?(content: string, languageId?: number): void;
allLanguages: Language[]; // TODO should probably be nullable
siteLanguages: number[]; // TODO same
myUserInfo?: MyUserInfo;
}
interface ImageUploadStatus {
@ -168,7 +169,7 @@ export class MarkdownTextArea extends Component<
<label
htmlFor={`file-upload-${this.id}`}
className={classNames("mb-0", {
pointer: UserService.Instance.myUserInfo,
pointer: !!this.props.myUserInfo,
})}
data-tippy-content={I18NextService.i18n.t("upload_image")}
>
@ -195,7 +196,7 @@ export class MarkdownTextArea extends Component<
name="file"
className="d-none"
multiple
disabled={!UserService.Instance.myUserInfo}
disabled={!this.props.myUserInfo}
onChange={linkEvent(this, this.handleImageUpload)}
/>
{this.getFormatButton("header", this.handleInsertHeader)}
@ -276,6 +277,7 @@ export class MarkdownTextArea extends Component<
{this.props.showLanguage && (
<LanguageSelect
iconVersion
myUserInfo={this.props.myUserInfo}
allLanguages={this.props.allLanguages}
selectedLanguageIds={
languageId ? Array.of(languageId) : undefined

View file

@ -6,14 +6,16 @@ import {
CommentAggregates,
CreateCommentLike,
CreatePostLike,
MyUserInfo,
PostAggregates,
} from "lemmy-js-client";
import { VoteContentType, VoteType } from "../../interfaces";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import { Icon, Spinner } from "../common/icon";
interface VoteButtonsProps {
voteContentType: VoteContentType;
myUserInfo?: MyUserInfo;
id: number;
onVote: (i: CreateCommentLike | CreatePostLike) => void;
enableDownvotes?: boolean;
@ -113,7 +115,7 @@ export class VoteButtonsCompact extends Component<
this.props.my_vote === 1 ? "text-info" : "text-muted"
}`}
data-tippy-content={tippy(this.props.counts)}
disabled={!UserService.Instance.myUserInfo}
disabled={!this.props.myUserInfo}
onClick={linkEvent(this, handleUpvote)}
aria-label={I18NextService.i18n.t("upvote")}
aria-pressed={this.props.my_vote === 1}
@ -123,7 +125,7 @@ export class VoteButtonsCompact extends Component<
) : (
<>
<Icon icon="arrow-up1" classes="icon-inline small" />
{showScores() && (
{showScores(this.props.myUserInfo) && (
<span className="ms-2">
{numToSI(this.props.counts.upvotes)}
</span>
@ -137,7 +139,7 @@ export class VoteButtonsCompact extends Component<
className={`ms-2 btn btn-sm btn-link btn-animate btn py-0 px-1 ${
this.props.my_vote === -1 ? "text-danger" : "text-muted"
}`}
disabled={!UserService.Instance.myUserInfo}
disabled={!this.props.myUserInfo}
onClick={linkEvent(this, handleDownvote)}
data-tippy-content={tippy(this.props.counts)}
aria-label={I18NextService.i18n.t("downvote")}
@ -148,7 +150,7 @@ export class VoteButtonsCompact extends Component<
) : (
<>
<Icon icon="arrow-down1" classes="icon-inline small" />
{showScores() && (
{showScores(this.props.myUserInfo) && (
<span
className={classNames("ms-2", {
invisible: this.props.counts.downvotes === 0,
@ -193,7 +195,7 @@ export class VoteButtons extends Component<VoteButtonsProps, VoteButtonsState> {
className={`btn-animate btn btn-link p-0 ${
this.props.my_vote === 1 ? "text-info" : "text-muted"
}`}
disabled={!UserService.Instance.myUserInfo}
disabled={!this.props.myUserInfo}
onClick={linkEvent(this, handleUpvote)}
data-tippy-content={I18NextService.i18n.t("upvote")}
aria-label={I18NextService.i18n.t("upvote")}
@ -205,7 +207,7 @@ export class VoteButtons extends Component<VoteButtonsProps, VoteButtonsState> {
<Icon icon="arrow-up1" classes="upvote" />
)}
</button>
{showScores() ? (
{showScores(this.props.myUserInfo) ? (
<div
className="unselectable pointer text-muted post-score"
data-tippy-content={tippy(this.props.counts)}
@ -221,7 +223,7 @@ export class VoteButtons extends Component<VoteButtonsProps, VoteButtonsState> {
className={`btn-animate btn btn-link p-0 ${
this.props.my_vote === -1 ? "text-danger" : "text-muted"
}`}
disabled={!UserService.Instance.myUserInfo}
disabled={!this.props.myUserInfo}
onClick={linkEvent(this, handleDownvote)}
data-tippy-content={I18NextService.i18n.t("downvote")}
aria-label={I18NextService.i18n.t("downvote")}

View file

@ -1,4 +1,4 @@
import { editCommunity, setIsoData, showLocal } from "@utils/app";
import { editCommunity, showLocal } from "@utils/app";
import {
getPageFromString,
getQueryParams,
@ -17,7 +17,7 @@ import {
ListingType,
SortType,
} from "lemmy-js-client";
import { InitialFetchRequest } from "../../interfaces";
import { InitialFetchRequest, IsoData } from "../../interfaces";
import { FirstLoadService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
@ -70,7 +70,9 @@ function getCommunitiesQueryParams() {
}
export class Communities extends Component<any, CommunitiesState> {
private isoData = setIsoData<CommunitiesData>(this.context);
get isoData(): IsoData<CommunitiesData> {
return this.context.store.getState().value;
}
state: CommunitiesState = {
listCommunitiesResponse: EMPTY_REQUEST,
siteRes: this.isoData.site_res,

View file

@ -243,6 +243,7 @@ export class CommunityForm extends Component<
selectedLanguageIds={this.state.form.discussion_languages}
multiple={true}
onChange={this.handleDiscussionLanguageChange}
myUserInfo={this.props.myUserInfo}
/>
<div className="mb-3 row">
<div className="col-12">

View file

@ -2,12 +2,13 @@ import { showAvatars } from "@utils/app";
import { hostname } from "@utils/helpers";
import { Component } from "inferno";
import { Link } from "inferno-router";
import { Community } from "lemmy-js-client";
import { Community, MyUserInfo } from "lemmy-js-client";
import { relTags } from "../../config";
import { PictrsImage } from "../common/pictrs-image";
interface CommunityLinkProps {
community: Community;
myUserInfo?: MyUserInfo;
realLink?: boolean;
useApubName?: boolean;
muted?: boolean;
@ -63,7 +64,7 @@ export class CommunityLink extends Component<CommunityLinkProps, any> {
<>
{!this.props.hideAvatar &&
!this.props.community.removed &&
showAvatars() &&
showAvatars(this.props.myUserInfo) &&
icon && <PictrsImage src={icon} icon nsfw={nsfw} />}
<span className="overflow-wrap-anywhere">{displayName}</span>
</>

View file

@ -9,7 +9,6 @@ import {
getCommentParentId,
getDataTypeString,
postToCommentSortType,
setIsoData,
showLocal,
updateCommunityBlock,
updatePersonBlock,
@ -58,6 +57,7 @@ import {
LockPost,
MarkCommentReplyAsRead,
MarkPersonMentionAsRead,
MyUserInfo,
PaginationCursor,
PostResponse,
PurgeComment,
@ -78,8 +78,9 @@ import {
CommentViewType,
DataType,
InitialFetchRequest,
IsoData,
} from "../../interfaces";
import { FirstLoadService, I18NextService, UserService } from "../../services";
import { FirstLoadService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
HttpService,
@ -136,10 +137,11 @@ function getDataTypeFromQuery(type?: string): DataType {
return type ? DataType[type] : DataType.Post;
}
function getSortTypeFromQuery(type?: string): SortType {
const mySortType =
UserService.Instance.myUserInfo?.local_user_view.local_user
.default_sort_type;
function getSortTypeFromQuery(
type?: string,
myUserInfo?: MyUserInfo,
): SortType {
const mySortType = myUserInfo?.local_user_view.local_user.default_sort_type;
return type ? (type as SortType) : mySortType ?? "Active";
}
@ -148,7 +150,9 @@ export class Community extends Component<
RouteComponentProps<{ name: string }>,
State
> {
private isoData = setIsoData<CommunityData>(this.context);
get isoData(): IsoData<CommunityData> {
return this.context.store.getState().value;
}
state: State = {
communityRes: EMPTY_REQUEST,
postsRes: EMPTY_REQUEST,
@ -463,6 +467,7 @@ export class Community extends Component<
return (
<CommentNodes
nodes={commentsToFlatNodes(this.state.commentsRes.data.comments)}
myUserInfo={this.isoData.site_res.my_user}
viewType={CommentViewType.Flat}
finished={this.state.finished}
isTopLevel
@ -642,7 +647,7 @@ export class Community extends Component<
// Update myUserInfo
if (followCommunityRes.state === "success") {
const communityId = followCommunityRes.data.community_view.community.id;
const mui = UserService.Instance.myUserInfo;
const mui = this.isoData.site_res.my_user;
if (mui) {
mui.follows = mui.follows.filter(i => i.community.id !== communityId);
}
@ -672,7 +677,10 @@ export class Community extends Component<
async handleBlockCommunity(form: BlockCommunity) {
const blockCommunityRes = await HttpService.client.blockCommunity(form);
if (blockCommunityRes.state === "success") {
updateCommunityBlock(blockCommunityRes.data);
updateCommunityBlock(
blockCommunityRes.data,
this.isoData.site_res.my_user,
);
this.setState(s => {
if (s.communityRes.state === "success") {
s.communityRes.data.community_view.blocked =
@ -685,7 +693,7 @@ export class Community extends Component<
async handleBlockPerson(form: BlockPerson) {
const blockPersonRes = await HttpService.client.blockPerson(form);
if (blockPersonRes.state === "success") {
updatePersonBlock(blockPersonRes.data);
updatePersonBlock(blockPersonRes.data, this.isoData.site_res.my_user);
}
}

View file

@ -1,4 +1,4 @@
import { enableNsfw, setIsoData } from "@utils/app";
import { enableNsfw } from "@utils/app";
import { Component } from "inferno";
import {
CreateCommunity as CreateCommunityI,
@ -7,6 +7,7 @@ import {
import { HttpService, I18NextService } from "../../services";
import { HtmlTags } from "../common/html-tags";
import { CommunityForm } from "./community-form";
import { IsoData } from "../../interfaces";
interface CreateCommunityState {
siteRes: GetSiteResponse;
@ -14,7 +15,9 @@ interface CreateCommunityState {
}
export class CreateCommunity extends Component<any, CreateCommunityState> {
private isoData = setIsoData(this.context);
get isoData(): IsoData {
return this.context.store.getState().value;
}
state: CreateCommunityState = {
siteRes: this.isoData.site_res,
loading: false,

View file

@ -12,12 +12,13 @@ import {
EditCommunity,
FollowCommunity,
Language,
MyUserInfo,
PersonView,
PurgeCommunity,
RemoveCommunity,
} from "lemmy-js-client";
import { mdToHtml } from "../../markdown";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import { Badges } from "../common/badges";
import { BannerIconHeader } from "../common/banner-icon-header";
import { Icon, PurgeWarning, Spinner } from "../common/icon";
@ -28,6 +29,7 @@ import { PersonListing } from "../person/person-listing";
interface SidebarProps {
community_view: CommunityView;
myUserInfo?: MyUserInfo;
moderators: CommunityModeratorView[];
admins: PersonView[];
allLanguages: Language[];
@ -122,7 +124,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
}
sidebar() {
const myUSerInfo = UserService.Instance.myUserInfo;
const myUserInfo = this.props.myUserInfo;
const {
community: { name, actor_id },
} = this.props.community_view;
@ -140,8 +142,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
loading={this.state.followCommunityLoading}
/>
{this.canPost && this.createPost()}
{myUSerInfo && this.blockCommunity()}
{!myUSerInfo && (
{myUserInfo && this.blockCommunity()}
{!myUserInfo && (
<div className="alert alert-info" role="alert">
<T
i18nKey="community_not_logged_in_alert"
@ -268,7 +270,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
return (
<>
<ul className="list-inline mb-1 text-muted fw-bold">
{amMod(this.props.community_view.community.id) && (
{amMod(
this.props.community_view.community.id,
this.props.myUserInfo,
) && (
<>
<li className="list-inline-item-action">
<button
@ -280,7 +285,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
<Icon icon="edit" classes="icon-inline" />
</button>
</li>
{!amTopMod(this.props.moderators) &&
{!amTopMod(this.props.moderators, this.props.myUserInfo) &&
(!this.state.showConfirmLeaveModTeam ? (
<li className="list-inline-item-action">
<button
@ -319,7 +324,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
</li>
</>
))}
{amTopMod(this.props.moderators) && (
{amTopMod(this.props.moderators, this.props.myUserInfo) && (
<li className="list-inline-item-action">
<button
className="btn btn-link text-muted d-inline-block"
@ -468,8 +473,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
get canPost(): boolean {
return (
!this.props.community_view.community.posting_restricted_to_mods ||
amMod(this.props.community_view.community.id) ||
amAdmin()
amMod(this.props.community_view.community.id, this.props.myUserInfo) ||
amAdmin(this.props.myUserInfo)
);
}
@ -520,7 +525,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
}
handleLeaveModTeam(i: Sidebar) {
const myId = UserService.Instance.myUserInfo?.local_user_view.person.id;
const myId = this.props.myUserInfo?.local_user_view.person.id;
if (myId) {
i.setState({ leaveModTeamLoading: true });
i.props.onLeaveModTeam({

View file

@ -1,4 +1,4 @@
import { fetchThemeList, setIsoData, showLocal } from "@utils/app";
import { fetchThemeList, showLocal } from "@utils/app";
import { capitalizeFirstLetter } from "@utils/helpers";
import { RouteDataResponse } from "@utils/types";
import classNames from "classnames";
@ -14,7 +14,7 @@ import {
LemmyHttp,
PersonView,
} from "lemmy-js-client";
import { InitialFetchRequest } from "../../interfaces";
import { InitialFetchRequest, IsoData } from "../../interfaces";
import { removeFromEmojiDataModel, updateEmojiDataModel } from "../../markdown";
import { FirstLoadService, I18NextService } from "../../services";
import {
@ -53,7 +53,9 @@ interface AdminSettingsState {
}
export class AdminSettings extends Component<any, AdminSettingsState> {
private isoData = setIsoData<AdminSettingsData>(this.context);
get isoData(): IsoData<AdminSettingsData> {
return this.context.store.getState().value;
}
state: AdminSettingsState = {
siteRes: this.isoData.site_res,
banned: [],

View file

@ -1,4 +1,3 @@
import { setIsoData } from "@utils/app";
import { capitalizeFirstLetter } from "@utils/helpers";
import { Component, linkEvent } from "inferno";
import {
@ -13,6 +12,7 @@ import { pictrsDeleteToast, toast } from "../../toast";
import { EmojiMart } from "../common/emoji-mart";
import { Icon, Spinner } from "../common/icon";
import { Paginator } from "../common/paginator";
import { IsoData } from "../../interfaces";
interface EmojiFormProps {
onEdit(form: EditCustomEmoji): void;
@ -39,7 +39,9 @@ interface CustomEmojiViewForm {
}
export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
private isoData = setIsoData(this.context);
get isoData(): IsoData {
return this.context.store.getState().value;
}
private itemsPerPage = 15;
private emptyState: EmojiFormState = {
siteRes: this.isoData.site_res,

View file

@ -9,7 +9,6 @@ import {
getDataTypeString,
myAuth,
postToCommentSortType,
setIsoData,
showLocal,
updatePersonBlock,
} from "@utils/app";
@ -59,6 +58,7 @@ import {
LockPost,
MarkCommentReplyAsRead,
MarkPersonMentionAsRead,
MyUserInfo,
PaginationCursor,
PostResponse,
PurgeComment,
@ -77,9 +77,10 @@ import {
CommentViewType,
DataType,
InitialFetchRequest,
IsoData,
} from "../../interfaces";
import { mdToHtml } from "../../markdown";
import { FirstLoadService, I18NextService, UserService } from "../../services";
import { FirstLoadService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
HttpService,
@ -168,7 +169,7 @@ function getDataTypeFromQuery(type?: string): DataType {
function getListingTypeFromQuery(
type?: string,
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
): ListingType | undefined {
const myListingType =
myUserInfo?.local_user_view?.local_user?.default_listing_type;
@ -178,7 +179,7 @@ function getListingTypeFromQuery(
function getSortTypeFromQuery(
type?: string,
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
): SortType {
const mySortType = myUserInfo?.local_user_view?.local_user?.default_sort_type;
@ -225,7 +226,9 @@ const LinkButton = ({
);
export class Home extends Component<any, HomeState> {
private isoData = setIsoData<HomeData>(this.context);
get isoData(): IsoData<HomeData> {
return this.context.store.getState().value;
}
state: HomeState = {
postsRes: EMPTY_REQUEST,
commentsRes: EMPTY_REQUEST,
@ -242,6 +245,7 @@ export class Home extends Component<any, HomeState> {
constructor(props: any, context: any) {
super(props, context);
console.log(this.isoData);
this.handleSortChange = this.handleSortChange.bind(this);
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
@ -416,7 +420,7 @@ export class Home extends Component<any, HomeState> {
}
get hasFollows(): boolean {
const mui = UserService.Instance.myUserInfo;
const mui = this.isoData.site_res.my_user;
return !!mui && mui.follows.length > 0;
}
@ -606,7 +610,7 @@ export class Home extends Component<any, HomeState> {
>
<div className="card-body">
<ul className="list-inline mb-0">
{UserService.Instance.myUserInfo?.follows.map(cfv => (
{this.isoData.site_res.my_user?.follows.map(cfv => (
<li
key={cfv.community.id}
className="list-inline-item d-inline-block"
@ -734,6 +738,7 @@ export class Home extends Component<any, HomeState> {
return (
<CommentNodes
nodes={commentsToFlatNodes(comments)}
myUserInfo={this.isoData.site_res.my_user}
viewType={CommentViewType.Flat}
finished={this.state.finished}
isTopLevel
@ -909,7 +914,7 @@ export class Home extends Component<any, HomeState> {
async handleBlockPerson(form: BlockPerson) {
const blockPersonRes = await HttpService.client.blockPerson(form);
if (blockPersonRes.state === "success") {
updatePersonBlock(blockPersonRes.data);
updatePersonBlock(blockPersonRes.data, this.isoData.site_res.my_user);
}
}

View file

@ -1,4 +1,3 @@
import { setIsoData } from "@utils/app";
import { RouteDataResponse } from "@utils/types";
import { Component } from "inferno";
import {
@ -9,7 +8,7 @@ import {
} from "lemmy-js-client";
import classNames from "classnames";
import { relTags } from "../../config";
import { InitialFetchRequest } from "../../interfaces";
import { InitialFetchRequest, IsoData } from "../../interfaces";
import { FirstLoadService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
@ -34,7 +33,9 @@ interface InstancesState {
}
export class Instances extends Component<any, InstancesState> {
private isoData = setIsoData<InstancesData>(this.context);
get isoData(): IsoData<InstancesData> {
return this.context.store.getState().value;
}
state: InstancesState = {
instancesRes: EMPTY_REQUEST,
siteRes: this.isoData.site_res,

View file

@ -1,16 +1,18 @@
import { setIsoData } from "@utils/app";
import { Component } from "inferno";
import { GetSiteResponse } from "lemmy-js-client";
import { mdToHtml } from "../../markdown";
import { I18NextService } from "../../services";
import { HtmlTags } from "../common/html-tags";
import { IsoData } from "../../interfaces";
interface LegalState {
siteRes: GetSiteResponse;
}
export class Legal extends Component<any, LegalState> {
private isoData = setIsoData(this.context);
get isoData(): IsoData {
return this.context.store.getState().value;
}
state: LegalState = {
siteRes: this.isoData.site_res,
};

View file

@ -1,4 +1,3 @@
import { setIsoData } from "@utils/app";
import { capitalizeFirstLetter, validEmail } from "@utils/helpers";
import { Component, linkEvent } from "inferno";
import { GetSiteResponse } from "lemmy-js-client";
@ -6,6 +5,7 @@ import { HttpService, I18NextService } from "../../services";
import { toast } from "../../toast";
import { HtmlTags } from "../common/html-tags";
import { Spinner } from "../common/icon";
import { IsoData } from "../../interfaces";
interface State {
form: {
@ -16,7 +16,9 @@ interface State {
}
export class LoginReset extends Component<any, State> {
private isoData = setIsoData(this.context);
get isoData(): IsoData {
return this.context.store.getState().value;
}
state: State = {
form: {

View file

@ -46,6 +46,7 @@ async function handleLoginSuccess(i: Login, loginRes: LoginResponse) {
const site = await HttpService.client.getSite();
if (site.state === "success") {
// TODO this is the key, you need to update the redux store here
UserService.Instance.myUserInfo = site.data.my_user;
updateDataBsTheme(site.data);
}

View file

@ -1,4 +1,3 @@
import { setIsoData } from "@utils/app";
import { isBrowser } from "@utils/browser";
import { validEmail } from "@utils/helpers";
import { Component, linkEvent } from "inferno";
@ -12,7 +11,7 @@ import {
} from "lemmy-js-client";
import { joinLemmyUrl } from "../../config";
import { mdToHtml } from "../../markdown";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import {
EMPTY_REQUEST,
HttpService,
@ -24,6 +23,7 @@ import { HtmlTags } from "../common/html-tags";
import { Icon, Spinner } from "../common/icon";
import { MarkdownTextArea } from "../common/markdown-textarea";
import PasswordInput from "../common/password-input";
import { IsoData } from "../../interfaces";
interface State {
registerRes: RequestState<LoginResponse>;
@ -44,7 +44,9 @@ interface State {
}
export class Signup extends Component<any, State> {
private isoData = setIsoData(this.context);
get isoData(): IsoData {
return this.context.store.getState().value;
}
private audio?: HTMLAudioElement;
state: State = {

View file

@ -498,6 +498,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
selectedLanguageIds={this.state.siteForm.discussion_languages}
multiple={true}
onChange={this.handleDiscussionLanguageChange}
myUserInfo={this.props.siteRes.my_user}
showAll
/>
<div className="mb-3 row">

View file

@ -1,9 +1,4 @@
import {
fetchUsers,
getUpdatedSearchId,
personToChoice,
setIsoData,
} from "@utils/app";
import { fetchUsers, getUpdatedSearchId, personToChoice } from "@utils/app";
import {
debounce,
formatPastDate,
@ -46,7 +41,7 @@ import {
Person,
} from "lemmy-js-client";
import { fetchLimit } from "../config";
import { InitialFetchRequest } from "../interfaces";
import { InitialFetchRequest, IsoData } from "../interfaces";
import { FirstLoadService, I18NextService } from "../services";
import {
EMPTY_REQUEST,
@ -636,7 +631,9 @@ export class Modlog extends Component<
RouteComponentProps<{ communityId?: string }>,
ModlogState
> {
private isoData = setIsoData<ModlogData>(this.context);
get isoData(): IsoData<ModlogData> {
return this.context.store.getState().value;
}
state: ModlogState = {
res: EMPTY_REQUEST,

View file

@ -7,7 +7,6 @@ import {
enableDownvotes,
getCommentParentId,
myAuth,
setIsoData,
updatePersonBlock,
} from "@utils/app";
import { capitalizeFirstLetter, randomStr } from "@utils/helpers";
@ -61,8 +60,12 @@ import {
TransferCommunity,
} from "lemmy-js-client";
import { fetchLimit, relTags } from "../../config";
import { CommentViewType, InitialFetchRequest } from "../../interfaces";
import { FirstLoadService, I18NextService, UserService } from "../../services";
import {
CommentViewType,
InitialFetchRequest,
IsoData,
} from "../../interfaces";
import { FirstLoadService, I18NextService } from "../../services";
import { UnreadCounterService } from "../../services";
import {
EMPTY_REQUEST,
@ -127,7 +130,9 @@ interface InboxState {
}
export class Inbox extends Component<any, InboxState> {
private isoData = setIsoData<InboxData>(this.context);
get isoData(): IsoData<InboxData> {
return this.context.store.getState().value;
}
state: InboxState = {
unreadOrAll: UnreadOrAll.Unread,
messageType: MessageType.All,
@ -194,7 +199,7 @@ export class Inbox extends Component<any, InboxState> {
}
get documentTitle(): string {
const mui = UserService.Instance.myUserInfo;
const mui = this.isoData.site_res.my_user;
return mui
? `@${mui.local_user_view.person.name} ${I18NextService.i18n.t(
"inbox",
@ -483,6 +488,7 @@ export class Inbox extends Component<any, InboxState> {
nodes={[
{ comment_view: i.view as CommentView, children: [], depth: 0 },
]}
myUserInfo={this.isoData.site_res.my_user}
viewType={CommentViewType.Flat}
finished={this.state.finished}
markable
@ -848,7 +854,7 @@ export class Inbox extends Component<any, InboxState> {
async handleBlockPerson(form: BlockPerson) {
const blockPersonRes = await HttpService.client.blockPerson(form);
if (blockPersonRes.state === "success") {
updatePersonBlock(blockPersonRes.data);
updatePersonBlock(blockPersonRes.data, this.isoData.site_res.my_user);
}
}

View file

@ -4,13 +4,14 @@ import { hostname, isCakeDay } from "@utils/helpers";
import classNames from "classnames";
import { Component } from "inferno";
import { Link } from "inferno-router";
import { Person } from "lemmy-js-client";
import { MyUserInfo, Person } from "lemmy-js-client";
import { relTags } from "../../config";
import { PictrsImage } from "../common/pictrs-image";
import { CakeDay } from "./cake-day";
interface PersonListingProps {
person: Person;
myUserInfo?: MyUserInfo;
realLink?: boolean;
useApubName?: boolean;
muted?: boolean;
@ -87,7 +88,7 @@ export class PersonListing extends Component<PersonListingProps, any> {
<>
{!this.props.hideAvatar &&
!this.props.person.banned &&
showAvatars() && (
showAvatars(this.props.myUserInfo) && (
<PictrsImage
src={avatar ?? `${getStaticDir()}/assets/icons/icon-96x96.png`}
icon

View file

@ -5,7 +5,6 @@ import {
enableDownvotes,
enableNsfw,
getCommentParentId,
setIsoData,
updatePersonBlock,
} from "@utils/app";
import { restoreScrollPosition, saveScrollPosition } from "@utils/browser";
@ -59,6 +58,7 @@ import {
LockPost,
MarkCommentReplyAsRead,
MarkPersonMentionAsRead,
MyUserInfo,
PersonView,
PostResponse,
PurgeComment,
@ -73,9 +73,13 @@ import {
TransferCommunity,
} from "lemmy-js-client";
import { fetchLimit, relTags } from "../../config";
import { InitialFetchRequest, PersonDetailsView } from "../../interfaces";
import {
InitialFetchRequest,
IsoData,
PersonDetailsView,
} from "../../interfaces";
import { mdToHtml } from "../../markdown";
import { FirstLoadService, I18NextService, UserService } from "../../services";
import { FirstLoadService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
HttpService,
@ -159,13 +163,16 @@ const getCommunitiesListing = (
const Moderates = ({ moderates }: { moderates?: CommunityModeratorView[] }) =>
getCommunitiesListing("moderates", moderates);
const Follows = () =>
getCommunitiesListing("subscribed", UserService.Instance.myUserInfo?.follows);
const Follows = ({ myUserInfo }: { myUserInfo?: MyUserInfo }) =>
getCommunitiesListing("subscribed", myUserInfo?.follows);
function isPersonBlocked(personRes: RequestState<GetPersonDetailsResponse>) {
function isPersonBlocked(
personRes: RequestState<GetPersonDetailsResponse>,
myUserInfo?: MyUserInfo,
) {
return (
(personRes.state === "success" &&
UserService.Instance.myUserInfo?.person_blocks.some(
myUserInfo?.person_blocks.some(
({ target: { id } }) => id === personRes.data.person_view.person.id,
)) ??
false
@ -176,7 +183,9 @@ export class Profile extends Component<
RouteComponentProps<{ username: string }>,
ProfileState
> {
private isoData = setIsoData<ProfileData>(this.context);
get isoData(): IsoData<ProfileData> {
return this.context.store.getState().value;
}
state: ProfileState = {
personRes: EMPTY_REQUEST,
personBlocked: false,
@ -269,7 +278,7 @@ export class Profile extends Component<
get amCurrentUser() {
if (this.state.personRes.state === "success") {
return (
UserService.Instance.myUserInfo?.local_user_view.person.id ===
this.isoData.site_res.my_user?.local_user_view.person.id ===
this.state.personRes.data.person_view.person.id
);
} else {
@ -388,7 +397,9 @@ export class Profile extends Component<
<div className="col-12 col-md-4">
<Moderates moderates={personRes.moderates} />
{this.amCurrentUser && <Follows />}
{this.amCurrentUser && (
<Follows myUserInfo={this.isoData.site_res.my_user} />
)}
</div>
</div>
);
@ -511,7 +522,7 @@ export class Profile extends Component<
</div>
{this.banDialog(pv)}
<div className="flex-grow-1 unselectable pointer mx-2"></div>
{!this.amCurrentUser && UserService.Instance.myUserInfo && (
{!this.amCurrentUser && this.isoData.site_res.my_user && (
<>
<a
className={`d-flex align-self-start btn btn-secondary me-2 ${
@ -622,7 +633,7 @@ export class Profile extends Component<
{format(parseISO(pv.person.published), "PPP")}
</span>
</div>
{!UserService.Instance.myUserInfo && (
{!this.isoData.site_res.my_user && (
<div className="alert alert-info" role="alert">
{I18NextService.i18n.t("profile_not_logged_in_alert")}
</div>
@ -799,7 +810,7 @@ export class Profile extends Component<
block,
});
if (res.state === "success") {
updatePersonBlock(res.data);
updatePersonBlock(res.data, this.isoData.site_res.my_user);
this.setState({ personBlocked: res.data.blocked });
}
}

View file

@ -1,4 +1,4 @@
import { editRegistrationApplication, setIsoData } from "@utils/app";
import { editRegistrationApplication } from "@utils/app";
import { randomStr } from "@utils/helpers";
import { RouteDataResponse } from "@utils/types";
import classNames from "classnames";
@ -11,8 +11,8 @@ import {
RegistrationApplicationView,
} from "lemmy-js-client";
import { fetchLimit } from "../../config";
import { InitialFetchRequest } from "../../interfaces";
import { FirstLoadService, I18NextService, UserService } from "../../services";
import { InitialFetchRequest, IsoData } from "../../interfaces";
import { FirstLoadService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
HttpService,
@ -49,7 +49,9 @@ export class RegistrationApplications extends Component<
any,
RegistrationApplicationsState
> {
private isoData = setIsoData<RegistrationApplicationsData>(this.context);
get isoData(): IsoData<RegistrationApplicationsData> {
return this.context.store.getState().value;
}
state: RegistrationApplicationsState = {
appsRes: EMPTY_REQUEST,
siteRes: this.isoData.site_res,
@ -82,7 +84,7 @@ export class RegistrationApplications extends Component<
}
get documentTitle(): string {
const mui = UserService.Instance.myUserInfo;
const mui = this.isoData.site_res.my_user;
return mui
? `@${mui.local_user_view.person.name} ${I18NextService.i18n.t(
"registration_applications",

View file

@ -2,7 +2,6 @@ import {
editCommentReport,
editPostReport,
editPrivateMessageReport,
setIsoData,
} from "@utils/app";
import { randomStr } from "@utils/helpers";
import { amAdmin } from "@utils/roles";
@ -29,13 +28,8 @@ import {
ResolvePrivateMessageReport,
} from "lemmy-js-client";
import { fetchLimit } from "../../config";
import { InitialFetchRequest } from "../../interfaces";
import {
FirstLoadService,
HttpService,
I18NextService,
UserService,
} from "../../services";
import { InitialFetchRequest, IsoData } from "../../interfaces";
import { FirstLoadService, HttpService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
LOADING_REQUEST,
@ -94,7 +88,9 @@ interface ReportsState {
}
export class Reports extends Component<any, ReportsState> {
private isoData = setIsoData<ReportsData>(this.context);
get isoData(): IsoData<ReportsData> {
return this.context.store.getState().value;
}
state: ReportsState = {
commentReportsRes: EMPTY_REQUEST,
postReportsRes: EMPTY_REQUEST,
@ -144,7 +140,7 @@ export class Reports extends Component<any, ReportsState> {
}
get documentTitle(): string {
const mui = UserService.Instance.myUserInfo;
const mui = this.isoData.site_res.my_user;
return mui
? `@${mui.local_user_view.person.name} ${I18NextService.i18n.t(
"reports",
@ -397,6 +393,7 @@ export class Reports extends Component<any, ReportsState> {
return (
<CommentReport
key={i.id}
myUserInfo={this.isoData.site_res.my_user}
report={i.view as CommentReportView}
onResolveReport={this.handleResolveCommentReport}
/>

View file

@ -6,7 +6,6 @@ import {
instanceToChoice,
myAuth,
personToChoice,
setIsoData,
setTheme,
showLocal,
updateCommunityBlock,
@ -37,7 +36,7 @@ import {
UpdateTotpResponse,
} from "lemmy-js-client";
import { elementUrl, emDash, fetchLimit, relTags } from "../../config";
import { FirstLoadService, UserService } from "../../services";
import { FirstLoadService } from "../../services";
import {
EMPTY_REQUEST,
HttpService,
@ -60,7 +59,7 @@ import { SortSelect } from "../common/sort-select";
import Tabs from "../common/tabs";
import { CommunityLink } from "../community/community-link";
import { PersonListing } from "./person-listing";
import { InitialFetchRequest } from "../../interfaces";
import { InitialFetchRequest, IsoData } from "../../interfaces";
import TotpModal from "../common/totp-modal";
import { LoadingEllipses } from "../common/loading-ellipses";
import { updateDataBsTheme } from "../../utils/browser";
@ -190,7 +189,9 @@ function handleClose2faModal(i: Settings) {
}
export class Settings extends Component<any, SettingsState> {
private isoData = setIsoData<SettingsData>(this.context);
get isoData(): IsoData<SettingsData> {
return this.context.store.getState().value;
}
exportSettingsLink = createRef<HTMLAnchorElement>();
state: SettingsState = {
@ -246,7 +247,7 @@ export class Settings extends Component<any, SettingsState> {
this.handleEnable2fa = this.handleEnable2fa.bind(this);
this.handleDisable2fa = this.handleDisable2fa.bind(this);
const mui = UserService.Instance.myUserInfo;
const mui = this.isoData.site_res.my_user;
if (mui) {
const {
local_user: {
@ -818,6 +819,7 @@ export class Settings extends Component<any, SettingsState> {
showAll={true}
showSite
onChange={this.handleDiscussionLanguageChange}
myUserInfo={this.isoData.site_res.my_user}
/>
<div className="mb-3 row">
<label className="col-sm-3 col-form-label" htmlFor="user-theme">
@ -1125,7 +1127,7 @@ export class Settings extends Component<any, SettingsState> {
totpSection() {
const totpEnabled =
!!UserService.Instance.myUserInfo?.local_user_view.local_user
!!this.isoData.site_res.my_user?.local_user_view.local_user
.totp_2fa_enabled;
const { generateTotpRes } = this.state;
@ -1181,7 +1183,7 @@ export class Settings extends Component<any, SettingsState> {
const siteRes = await HttpService.client.getSite();
UserService.Instance.myUserInfo!.local_user_view.local_user.totp_2fa_enabled =
this.isoData.site_res.my_user!.local_user_view.local_user.totp_2fa_enabled =
enabled;
if (siteRes.state === "success") {
@ -1349,7 +1351,7 @@ export class Settings extends Component<any, SettingsState> {
}
handleShowAvatarsChange(i: Settings, event: any) {
const mui = UserService.Instance.myUserInfo;
const mui = this.isoData.site_res.my_user;
if (mui) {
mui.local_user_view.local_user.show_avatars = event.target.checked;
}
@ -1395,7 +1397,7 @@ export class Settings extends Component<any, SettingsState> {
}
handleShowScoresChange(i: Settings, event: any) {
const mui = UserService.Instance.myUserInfo;
const mui = this.isoData.site_res.my_user;
if (mui) {
mui.local_user_view.local_user.show_scores = event.target.checked;
}
@ -1528,6 +1530,7 @@ export class Settings extends Component<any, SettingsState> {
siteRes: siteRes.data,
});
// TODO need to update this
UserService.Instance.myUserInfo = siteRes.data.my_user;
}
@ -1628,6 +1631,7 @@ export class Settings extends Component<any, SettingsState> {
},
} = siteRes.data.my_user!.local_user_view;
// TODO need to update redux
UserService.Instance.myUserInfo = siteRes.data.my_user;
updateDataBsTheme(siteRes.data);
@ -1705,8 +1709,8 @@ export class Settings extends Component<any, SettingsState> {
personBlock(res: RequestState<BlockPersonResponse>) {
if (res.state === "success") {
updatePersonBlock(res.data);
const mui = UserService.Instance.myUserInfo;
updatePersonBlock(res.data, this.isoData.site_res.my_user);
const mui = this.isoData.site_res.my_user;
if (mui) {
this.setState({ personBlocks: mui.person_blocks });
}
@ -1715,8 +1719,8 @@ export class Settings extends Component<any, SettingsState> {
communityBlock(res: RequestState<BlockCommunityResponse>) {
if (res.state === "success") {
updateCommunityBlock(res.data);
const mui = UserService.Instance.myUserInfo;
updateCommunityBlock(res.data, this.isoData.site_res.my_user);
const mui = this.isoData.site_res.my_user;
if (mui) {
this.setState({ communityBlocks: mui.community_blocks });
}
@ -1730,8 +1734,13 @@ export class Settings extends Component<any, SettingsState> {
) {
const linkedInstances =
this.state.instancesRes.data.federated_instances?.linked ?? [];
updateInstanceBlock(res.data, id, linkedInstances);
const mui = UserService.Instance.myUserInfo;
updateInstanceBlock(
res.data,
id,
linkedInstances,
this.isoData.site_res.my_user,
);
const mui = this.isoData.site_res.my_user;
if (mui) {
this.setState({ instanceBlocks: mui.instance_blocks });
}

View file

@ -1,4 +1,3 @@
import { setIsoData } from "@utils/app";
import { Component } from "inferno";
import { GetSiteResponse, SuccessResponse } from "lemmy-js-client";
import { I18NextService } from "../../services";
@ -11,6 +10,7 @@ import {
import { toast } from "../../toast";
import { HtmlTags } from "../common/html-tags";
import { Spinner } from "../common/icon";
import { IsoData } from "../../interfaces";
interface State {
verifyRes: RequestState<SuccessResponse>;
@ -18,7 +18,9 @@ interface State {
}
export class VerifyEmail extends Component<any, State> {
private isoData = setIsoData(this.context);
get isoData(): IsoData {
return this.context.store.getState().value;
}
state: State = {
verifyRes: EMPTY_REQUEST,

View file

@ -1,4 +1,4 @@
import { enableDownvotes, enableNsfw, setIsoData } from "@utils/app";
import { enableDownvotes, enableNsfw } from "@utils/app";
import { getIdFromString, getQueryParams } from "@utils/helpers";
import type { QueryParams } from "@utils/types";
import { Choice, RouteDataResponse } from "@utils/types";
@ -12,7 +12,7 @@ import {
LemmyHttp,
ListCommunitiesResponse,
} from "lemmy-js-client";
import { InitialFetchRequest, PostFormParams } from "../../interfaces";
import { InitialFetchRequest, IsoData, PostFormParams } from "../../interfaces";
import { FirstLoadService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
@ -57,7 +57,9 @@ export class CreatePost extends Component<
RouteComponentProps<Record<string, never>>,
CreatePostState
> {
private isoData = setIsoData<CreatePostData>(this.context);
get isoData(): IsoData<CreatePostData> {
return this.context.store.getState().value;
}
state: CreatePostState = {
siteRes: this.isoData.site_res,
loading: true,

View file

@ -17,6 +17,7 @@ import {
EditPost,
GetSiteMetadataResponse,
Language,
MyUserInfo,
PostView,
SearchResponse,
} from "lemmy-js-client";
@ -28,7 +29,7 @@ import {
webArchiveUrl,
} from "../../config";
import { PostFormParams } from "../../interfaces";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import {
EMPTY_REQUEST,
HttpService,
@ -47,6 +48,7 @@ const MAX_POST_TITLE_LENGTH = 200;
interface PostFormProps {
post_view?: PostView; // If a post is given, that means this is an edit
myUserInfo?: MyUserInfo;
crossPosts?: PostView[];
allLanguages: Language[];
siteLanguages: number[];
@ -423,7 +425,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
accept="image/*,video/*"
name="file"
className="small col-sm-10 form-control"
disabled={!UserService.Instance.myUserInfo}
disabled={!this.props.myUserInfo}
onChange={linkEvent(this, handleImageUpload)}
/>
{this.state.imageLoading && <Spinner />}
@ -496,6 +498,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
selectedLanguageIds={selectedLangs}
multiple={false}
onChange={this.handleLanguageChange}
myUserInfo={this.props.myUserInfo}
/>
{!this.props.post_view && (
<div className="mb-3 row">

View file

@ -33,6 +33,7 @@ import {
Language,
LockPost,
MarkPostAsRead,
MyUserInfo,
PersonView,
PostView,
PurgePerson,
@ -49,7 +50,7 @@ import {
VoteContentType,
} from "../../interfaces";
import { mdToHtml, mdToHtmlInline } from "../../markdown";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import { setupTippy } from "../../tippy";
import { Icon, PurgeWarning, Spinner } from "../common/icon";
import { MomentTime } from "../common/moment-time";
@ -98,6 +99,7 @@ interface PostListingState {
interface PostListingProps {
post_view: PostView;
myUserInfo?: MyUserInfo;
crossPosts?: PostView[];
moderators?: CommunityModeratorView[];
admins?: PersonView[];
@ -171,11 +173,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
}
componentDidMount(): void {
if (UserService.Instance.myUserInfo) {
if (this.props.myUserInfo) {
this.setState({
imageExpanded:
UserService.Instance.myUserInfo.local_user_view.local_user
.auto_expand,
this.props.myUserInfo.local_user_view.local_user.auto_expand,
});
}
}
@ -629,6 +630,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
{mobile && !this.props.viewOnly && (
<VoteButtonsCompact
voteContentType={VoteContentType.Post}
loggedIn={!!this.props.myUserInfo}
id={this.postView.post.id}
onVote={this.props.onPostVote}
enableDownvotes={this.props.enableDownvotes}
@ -639,9 +641,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
{this.props.showBody && pv.post.body && this.viewSourceButton}
{UserService.Instance.myUserInfo &&
!this.props.viewOnly &&
this.postActions()}
{this.props.myUserInfo && !this.props.viewOnly && this.postActions()}
</div>
);
}
@ -687,7 +687,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
)}
{/* Any mod can do these, not limited to hierarchy*/}
{(amMod(this.postView.community.id) || amAdmin()) && (
{(amMod(this.postView.community.id, this.props.myUserInfo) ||
amAdmin(this.props.myUserInfo)) && (
<>
<li>
<hr className="dropdown-divider" />
@ -718,7 +719,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</>
)}
{(amCommunityCreator(pv.creator.id, this.props.moderators) ||
{(amCommunityCreator(
pv.creator.id,
this.props.moderators,
this.props.myUserInfo,
) ||
this.canAdmin_) &&
pv.creator_is_moderator && (
<li>{this.transferCommunityButton}</li>
@ -753,7 +758,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
}
public get linkTarget(): string {
return UserService.Instance.myUserInfo?.local_user_view.local_user
return this.props.myUserInfo?.local_user_view.local_user
.open_links_in_new_tab
? "_blank"
: // _self is the default target on links when the field is not specified
@ -1378,6 +1383,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<div className="col flex-grow-0">
<VoteButtons
voteContentType={VoteContentType.Post}
loggedIn={!!this.props.myUserInfo}
id={this.postView.post.id}
onVote={this.props.onPostVote}
enableDownvotes={this.props.enableDownvotes}
@ -1409,7 +1415,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
private get myPost(): boolean {
return (
this.postView.creator.id ===
UserService.Instance.myUserInfo?.local_user_view.person.id
this.props.myUserInfo?.local_user_view.person.id
);
}
@ -1774,10 +1780,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this.postView.creator.id,
this.props.moderators,
this.props.admins,
this.props.myUserInfo,
);
}
get canAdmin_(): boolean {
return canAdmin(this.postView.creator.id, this.props.admins);
return canAdmin(
this.postView.creator.id,
this.props.admins,
this.props.myUserInfo,
);
}
}

View file

@ -9,7 +9,6 @@ import {
getCommentParentId,
getDepthFromComment,
getIdFromProps,
setIsoData,
updateCommunityBlock,
updatePersonBlock,
} from "@utils/app";
@ -80,8 +79,9 @@ import {
CommentNodeI,
CommentViewType,
InitialFetchRequest,
IsoData,
} from "../../interfaces";
import { FirstLoadService, I18NextService, UserService } from "../../services";
import { FirstLoadService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
HttpService,
@ -123,7 +123,9 @@ interface PostState {
}
export class Post extends Component<any, PostState> {
private isoData = setIsoData<PostData>(this.context);
get isoData(): IsoData<PostData> {
return this.context.store.getState().value;
}
private commentScrollDebounced: () => void;
state: PostState = {
postRes: EMPTY_REQUEST,
@ -402,6 +404,7 @@ export class Post extends Component<any, PostState> {
{!this.state.commentId && (
<CommentForm
node={res.post_view.post.id}
loggedIn={!!this.isoData.site_res.my_user}
disabled={res.post_view.post.locked}
allLanguages={this.state.siteRes.all_languages}
siteLanguages={this.state.siteRes.discussion_languages}
@ -761,7 +764,7 @@ export class Post extends Component<any, PostState> {
// Update myUserInfo
if (followCommunityRes.state === "success") {
const communityId = followCommunityRes.data.community_view.community.id;
const mui = UserService.Instance.myUserInfo;
const mui = this.isoData.site_res.my_user;
if (mui) {
mui.follows = mui.follows.filter(i => i.community.id !== communityId);
}
@ -791,7 +794,10 @@ export class Post extends Component<any, PostState> {
async handleBlockCommunity(form: BlockCommunity) {
const blockCommunityRes = await HttpService.client.blockCommunity(form);
if (blockCommunityRes.state === "success") {
updateCommunityBlock(blockCommunityRes.data);
updateCommunityBlock(
blockCommunityRes.data,
this.isoData.site_res.my_user,
);
this.setState(s => {
if (s.postRes.state === "success") {
s.postRes.data.community_view.blocked =
@ -804,7 +810,7 @@ export class Post extends Component<any, PostState> {
async handleBlockPerson(form: BlockPerson) {
const blockPersonRes = await HttpService.client.blockPerson(form);
if (blockPersonRes.state === "success") {
updatePersonBlock(blockPersonRes.data);
updatePersonBlock(blockPersonRes.data, this.isoData.site_res.my_user);
}
}

View file

@ -1,4 +1,4 @@
import { getRecipientIdFromProps, setIsoData } from "@utils/app";
import { getRecipientIdFromProps } from "@utils/app";
import { RouteDataResponse } from "@utils/types";
import { Component } from "inferno";
import {
@ -8,7 +8,7 @@ import {
GetSiteResponse,
LemmyHttp,
} from "lemmy-js-client";
import { InitialFetchRequest } from "../../interfaces";
import { InitialFetchRequest, IsoData } from "../../interfaces";
import { FirstLoadService, I18NextService } from "../../services";
import {
EMPTY_REQUEST,
@ -38,7 +38,9 @@ export class CreatePrivateMessage extends Component<
any,
CreatePrivateMessageState
> {
private isoData = setIsoData<CreatePrivateMessageData>(this.context);
get isoData(): IsoData<CreatePrivateMessageData> {
return this.context.store.getState().value;
}
state: CreatePrivateMessageState = {
siteRes: this.isoData.site_res,
recipientRes: EMPTY_REQUEST,

View file

@ -1,4 +1,3 @@
import { setIsoData } from "@utils/app";
import { getQueryParams } from "@utils/helpers";
import { QueryParams, RouteDataResponse } from "@utils/types";
import { Component, linkEvent } from "inferno";
@ -7,7 +6,7 @@ import {
LemmyHttp,
ResolveObjectResponse,
} from "lemmy-js-client";
import { InitialFetchRequest } from "../interfaces";
import { InitialFetchRequest, IsoData } from "../interfaces";
import { FirstLoadService, HttpService, I18NextService } from "../services";
import {
EMPTY_REQUEST,
@ -84,7 +83,9 @@ 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);
get isoData(): IsoData<RemoteFetchData> {
return this.context.store.getState().value;
}
state: RemoteFetchState = {
resolveObjectRes: EMPTY_REQUEST,
isIsomorphic: false,

View file

@ -8,7 +8,6 @@ import {
getUpdatedSearchId,
myAuth,
personToChoice,
setIsoData,
showLocal,
} from "@utils/app";
import { restoreScrollPosition, saveScrollPosition } from "@utils/browser";
@ -47,7 +46,7 @@ import {
SortType,
} from "lemmy-js-client";
import { fetchLimit } from "../config";
import { CommentViewType, InitialFetchRequest } from "../interfaces";
import { CommentViewType, InitialFetchRequest, IsoData } from "../interfaces";
import { FirstLoadService, I18NextService } from "../services";
import {
EMPTY_REQUEST,
@ -241,7 +240,9 @@ function getListing(
}
export class Search extends Component<any, SearchState> {
private isoData = setIsoData<SearchData>(this.context);
get isoData(): IsoData<SearchData> {
return this.context.store.getState().value;
}
searchInput = createRef<HTMLInputElement>();
state: SearchState = {

View file

@ -20,7 +20,7 @@ export type IsoDataOptionalSite<T extends RouteData = any> = Partial<
declare global {
interface Window {
isoData: IsoData;
isoData?: IsoDataOptionalSite;
}
}

View file

@ -1,7 +1,11 @@
import { getHttpBase } from "@utils/env";
import { LemmyHttp } from "lemmy-js-client";
import { LemmyHttp, LoginResponse } from "lemmy-js-client";
import { toast } from "../toast";
import { I18NextService } from "./I18NextService";
import { clearAuthCookie, isBrowser, setAuthCookie } from "@utils/browser";
import { isAuthPath } from "@utils/app";
import cookie from "cookie";
import { authCookieName } from "../config";
export const EMPTY_REQUEST = {
state: "empty",
@ -56,7 +60,7 @@ class WrappedLemmyHttpClient {
Object.getPrototypeOf(this.rawClient),
)) {
if (key !== "constructor") {
this[key] = async (...args) => {
this[key] = async (...args: any) => {
try {
const res = await this.rawClient[key](...args);
@ -88,6 +92,18 @@ export function wrapClient(client: LemmyHttp, silent = false) {
) as unknown as WrappedLemmyHttp;
}
// TODO These are unused for now
// interface Claims {
// sub: number;
// iss: string;
// iat: number;
// }
// interface AuthInfo {
// claims: Claims;
// auth: string;
// }
export class HttpService {
static #_instance: HttpService;
#silent_client: WrappedLemmyHttp;
@ -95,10 +111,42 @@ export class HttpService {
private constructor() {
const lemmyHttp = new LemmyHttp(getHttpBase());
const auth = cookie.parse(document.cookie)[authCookieName];
if (auth) {
HttpService.client.setHeaders({ Authorization: `Bearer ${auth}` });
}
this.#client = wrapClient(lemmyHttp);
this.#silent_client = wrapClient(lemmyHttp, true);
}
public login({
res,
showToast = true,
}: {
res: LoginResponse;
showToast?: boolean;
}) {
if (isBrowser() && res.jwt) {
showToast && toast(I18NextService.i18n.t("logged_in"));
setAuthCookie(res.jwt);
}
}
public logout() {
if (isBrowser()) {
clearAuthCookie();
}
this.#client.logout();
if (isAuthPath(location.pathname)) {
location.replace("/");
} else {
location.reload();
}
}
static get #Instance() {
return this.#_instance ?? (this.#_instance = new this());
}

View file

@ -1,4 +1,4 @@
import { UserService, HttpService } from "../services";
import { HttpService } from "../services";
import { updateUnreadCountsInterval } from "../config";
import { poll } from "@utils/helpers";
import { myAuth } from "@utils/app";

View file

@ -1,99 +0,0 @@
import { isAuthPath } from "@utils/app";
import { clearAuthCookie, isBrowser, setAuthCookie } from "@utils/browser";
import * as cookie from "cookie";
import { jwtDecode } from "jwt-decode";
import { LoginResponse, MyUserInfo } from "lemmy-js-client";
import { toast } from "../toast";
import { I18NextService } from "./I18NextService";
import { amAdmin } from "@utils/roles";
import { HttpService } from ".";
import { authCookieName } from "../config";
interface Claims {
sub: number;
iss: string;
iat: number;
}
interface AuthInfo {
claims: Claims;
auth: string;
}
export class UserService {
static #instance: UserService;
public myUserInfo?: MyUserInfo;
public authInfo?: AuthInfo;
private constructor() {
this.#setAuthInfo();
}
public login({
res,
showToast = true,
}: {
res: LoginResponse;
showToast?: boolean;
}) {
if (isBrowser() && res.jwt) {
showToast && toast(I18NextService.i18n.t("logged_in"));
setAuthCookie(res.jwt);
this.#setAuthInfo();
}
}
public logout() {
this.authInfo = undefined;
this.myUserInfo = undefined;
if (isBrowser()) {
clearAuthCookie();
}
HttpService.client.logout();
if (isAuthPath(location.pathname)) {
location.replace("/");
} else {
location.reload();
}
}
public auth(throwErr = false): string | undefined {
const auth = this.authInfo?.auth;
if (auth) {
return auth;
} else {
const msg = "No JWT cookie found";
if (throwErr && isBrowser()) {
console.error(msg);
toast(I18NextService.i18n.t("not_logged_in"), "danger");
}
return undefined;
// throw msg;
}
}
#setAuthInfo() {
if (isBrowser()) {
const auth = cookie.parse(document.cookie)[authCookieName];
if (auth) {
HttpService.client.setHeaders({ Authorization: `Bearer ${auth}` });
this.authInfo = { auth, claims: jwtDecode(auth) };
}
}
}
public get moderatesSomething(): boolean {
return amAdmin() || (this.myUserInfo?.moderates?.length ?? 0) > 0;
}
public static get Instance() {
return this.#instance || (this.#instance = new this());
}
}

View file

@ -1,5 +1,4 @@
export { FirstLoadService } from "./FirstLoadService";
export { HttpService } from "./HttpService";
export { I18NextService } from "./I18NextService";
export { UserService } from "./UserService";
export { UnreadCounterService } from "./UnreadCounterService";

View file

@ -43,7 +43,6 @@ import personToChoice from "./person-to-choice";
import postToCommentSortType from "./post-to-comment-sort-type";
import searchCommentTree from "./search-comment-tree";
import selectableLanguages from "./selectable-languages";
import setIsoData from "./set-iso-data";
import setTheme from "./set-theme";
import setupDateFns from "./setup-date-fns";
import showAvatars from "./show-avatars";
@ -55,6 +54,7 @@ import updatePersonBlock from "./update-person-block";
import instanceToChoice from "./instance-to-choice";
import updateInstanceBlock from "./update-instance-block";
import isAnonymousPath from "./is-anonymous-path";
import setupRedux from "./setup-redux";
export {
buildCommentsTree,
@ -102,11 +102,11 @@ export {
postToCommentSortType,
searchCommentTree,
selectableLanguages,
setIsoData,
setTheme,
setupDateFns,
showAvatars,
showLocal,
setupRedux,
showScores,
siteBannerCss,
updateCommunityBlock,

View file

@ -4,6 +4,7 @@ import { I18NextService, UserService } from "../../services";
import { updateDataBsTheme } from "@utils/browser";
export default function initializeSite(site?: GetSiteResponse) {
// TODO Should already be in siteRes
UserService.Instance.myUserInfo = site?.my_user;
updateDataBsTheme(site);
I18NextService.i18n.changeLanguage();

View file

@ -1,9 +1,8 @@
import { MyUserInfo, PostView } from "lemmy-js-client";
import { UserService } from "../../services";
export default function isPostBlocked(
pv: PostView,
myUserInfo: MyUserInfo | undefined = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
): boolean {
return (
(myUserInfo?.community_blocks

View file

@ -1,6 +1,18 @@
import { UserService } from "../../services";
import { isBrowser } from "@utils/browser";
import { toast } from "../../../shared/toast";
import { I18NextService } from "../../services";
// Warning, do not use this in fetchInitialData
export default function myAuth(): string | undefined {
return UserService.Instance.auth();
export default function myAuth(throwErr = false): string | undefined {
if (auth) {
return auth;
} else {
const msg = "No JWT cookie found";
if (throwErr && isBrowser()) {
console.error(msg);
toast(I18NextService.i18n.t("not_logged_in"), "danger");
}
return undefined;
}
}

View file

@ -1,9 +1,8 @@
import { PostView } from "lemmy-js-client";
import { UserService } from "../../services";
import { MyUserInfo, PostView } from "lemmy-js-client";
export default function nsfwCheck(
pv: PostView,
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
): boolean {
const nsfw = pv.post.nsfw || pv.community.nsfw;
const myShowNsfw = myUserInfo?.local_user_view.local_user.show_nsfw ?? false;

View file

@ -1,5 +1,4 @@
import { Language } from "lemmy-js-client";
import { UserService } from "../../services";
import { Language, MyUserInfo } from "lemmy-js-client";
/**
* This shows what language you can select
@ -13,7 +12,7 @@ export default function selectableLanguages(
siteLanguages: number[],
showAll?: boolean,
showSite?: boolean,
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
): Language[] {
const allLangIds = allLanguages.map(l => l.id);
let myLangs = myUserInfo?.discussion_languages ?? allLangIds;

View file

@ -1,11 +0,0 @@
import { isBrowser } from "@utils/browser";
import { IsoData, RouteData } from "../../interfaces";
export default function setIsoData<T extends RouteData>(
context: any,
): IsoData<T> {
// If its the browser, you need to deserialize the data from the window
if (isBrowser()) {
return window.isoData;
} else return context.router.staticContext;
}

View file

@ -0,0 +1,13 @@
import { configureStore, createSlice } from "@reduxjs/toolkit";
import { IsoDataOptionalSite } from "../../../shared/interfaces";
// TODO add reducer function here
export default function setupRedux(isoData?: IsoDataOptionalSite) {
const slice = createSlice({
name: "isoData",
initialState: { value: isoData },
reducers: {},
});
const store = configureStore({ reducer: slice.reducer });
return store;
}

View file

@ -1,7 +1,5 @@
import { UserService } from "../../services";
import { MyUserInfo } from "lemmy-js-client";
export default function showAvatars(
myUserInfo = UserService.Instance.myUserInfo,
): boolean {
export default function showAvatars(myUserInfo?: MyUserInfo): boolean {
return myUserInfo?.local_user_view.local_user.show_avatars ?? true;
}

View file

@ -1,7 +1,5 @@
import { UserService } from "../../services";
import { MyUserInfo } from "lemmy-js-client";
export default function showScores(
myUserInfo = UserService.Instance.myUserInfo,
): boolean {
export default function showScores(myUserInfo?: MyUserInfo): boolean {
return myUserInfo?.local_user_view.local_user.show_scores ?? true;
}

View file

@ -1,10 +1,10 @@
import { BlockCommunityResponse, MyUserInfo } from "lemmy-js-client";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import { toast } from "../../toast";
export default function updateCommunityBlock(
data: BlockCommunityResponse,
myUserInfo: MyUserInfo | undefined = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
) {
if (myUserInfo) {
if (data.blocked) {

View file

@ -1,12 +1,12 @@
import { BlockInstanceResponse, Instance, MyUserInfo } from "lemmy-js-client";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import { toast } from "../../toast";
export default function updateInstanceBlock(
data: BlockInstanceResponse,
id: number,
linkedInstances: Instance[],
myUserInfo: MyUserInfo | undefined = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
) {
if (myUserInfo) {
const instance = linkedInstances.find(i => i.id === id)!;

View file

@ -1,10 +1,10 @@
import { BlockPersonResponse, MyUserInfo } from "lemmy-js-client";
import { I18NextService, UserService } from "../../services";
import { I18NextService } from "../../services";
import { toast } from "../../toast";
export default function updatePersonBlock(
data: BlockPersonResponse,
myUserInfo: MyUserInfo | undefined = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
) {
if (myUserInfo) {
if (data.blocked) {

View file

View file

@ -1,7 +1,5 @@
import { UserService } from "../../services";
import { MyUserInfo } from "lemmy-js-client";
export default function amAdmin(
myUserInfo = UserService.Instance.myUserInfo,
): boolean {
export default function amAdmin(myUserInfo?: MyUserInfo): boolean {
return myUserInfo?.local_user_view.local_user.admin ?? false;
}

View file

@ -1,10 +1,9 @@
import { CommunityModeratorView } from "lemmy-js-client";
import { UserService } from "../../services";
import { CommunityModeratorView, MyUserInfo } from "lemmy-js-client";
export default function amCommunityCreator(
creator_id: number,
mods?: CommunityModeratorView[],
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
): boolean {
const myId = myUserInfo?.local_user_view.person.id;
// Don't allow mod actions on yourself

View file

@ -1,9 +1,8 @@
import { CommunityId } from "lemmy-js-client";
import { UserService } from "../../services";
import { CommunityId, MyUserInfo } from "lemmy-js-client";
export default function amMod(
communityId: CommunityId,
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
): boolean {
return myUserInfo
? myUserInfo.moderates.map(cmv => cmv.community.id).includes(communityId)

View file

@ -1,10 +1,9 @@
import { PersonView } from "lemmy-js-client";
import { UserService } from "../../services";
import { MyUserInfo, PersonView } from "lemmy-js-client";
export default function amSiteCreator(
creator_id: number,
admins?: PersonView[],
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
): boolean {
const myId = myUserInfo?.local_user_view.person.id;
return myId === admins?.at(0)?.person.id && myId !== creator_id;

View file

@ -1,9 +1,8 @@
import { CommunityModeratorView } from "lemmy-js-client";
import { UserService } from "../../services";
import { CommunityModeratorView, MyUserInfo } from "lemmy-js-client";
export default function amTopMod(
mods: CommunityModeratorView[],
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
): boolean {
return mods.at(0)?.moderator.id === myUserInfo?.local_user_view.person.id;
}

View file

@ -1,11 +1,10 @@
import { canMod } from "@utils/roles";
import { PersonView } from "lemmy-js-client";
import { UserService } from "../../services";
import { MyUserInfo, PersonView } from "lemmy-js-client";
export default function canAdmin(
creatorId: number,
admins?: PersonView[],
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
onSelf = false,
): boolean {
return canMod(creatorId, undefined, admins, myUserInfo, onSelf);

View file

@ -1,12 +1,8 @@
import { amAdmin } from "@utils/roles";
import { GetSiteResponse } from "lemmy-js-client";
import { UserService } from "../../services";
export default function canCreateCommunity(
siteRes: GetSiteResponse,
myUserInfo = UserService.Instance.myUserInfo,
): boolean {
export default function canCreateCommunity(siteRes: GetSiteResponse): boolean {
const adminOnly = siteRes.site_view.local_site.community_creation_admin_only;
// TODO: Make this check if user is logged on as well
return !adminOnly || amAdmin(myUserInfo);
return !adminOnly || amAdmin(siteRes.my_user);
}

View file

@ -1,11 +1,14 @@
import { CommunityModeratorView, PersonView } from "lemmy-js-client";
import { UserService } from "../../services";
import {
CommunityModeratorView,
MyUserInfo,
PersonView,
} from "lemmy-js-client";
export default function canMod(
creator_id: number,
mods?: CommunityModeratorView[],
admins?: PersonView[],
myUserInfo = UserService.Instance.myUserInfo,
myUserInfo?: MyUserInfo,
onSelf = false,
): boolean {
// You can do moderator actions only on the mods added after you.

View file

@ -7,6 +7,7 @@ import canAdmin from "./can-admin";
import canCreateCommunity from "./can-create-community";
import canMod from "./can-mod";
import isBanned from "./is-banned";
import moderatesSomething from "./moderates-something";
export {
amAdmin,
@ -18,4 +19,5 @@ export {
canCreateCommunity,
canMod,
isBanned,
moderatesSomething,
};

View file

@ -0,0 +1,6 @@
import { MyUserInfo } from "lemmy-js-client";
import amAdmin from "./am-admin";
export default function moderatesSomething(myUserInfo?: MyUserInfo): boolean {
return amAdmin(myUserInfo) || (myUserInfo?.moderates?.length ?? 0) > 0;
}

View file

@ -1217,6 +1217,16 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@reduxjs/toolkit@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.0.1.tgz#0a5233c1e35c1941b03aece39cceade3467a1062"
integrity sha512-fxIjrR9934cmS8YXIGd9e7s1XRsEU++aFc9DVNMFMRTM5Vtsg2DCRMj21eslGtDt43IUf9bJL3h5bwUlZleibA==
dependencies:
immer "^10.0.3"
redux "^5.0.0"
redux-thunk "^3.1.0"
reselect "^5.0.1"
"@rollup/plugin-babel@^5.2.0":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
@ -4829,6 +4839,11 @@ ignore@^5.2.0, ignore@^5.2.4:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78"
integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==
immer@^10.0.3:
version "10.0.3"
resolved "https://registry.yarnpkg.com/immer/-/immer-10.0.3.tgz#a8de42065e964aa3edf6afc282dfc7f7f34ae3c9"
integrity sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==
immutable@^4.0.0:
version "4.3.4"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f"
@ -4969,6 +4984,14 @@ inferno-i18next-dess@0.0.2:
inferno-shared "^8.0.3"
inferno-vnode-flags "^8.0.3"
inferno-redux@^8.2.2:
version "8.2.2"
resolved "https://registry.yarnpkg.com/inferno-redux/-/inferno-redux-8.2.2.tgz#1df3520f169def67d616d3ba6b54e5b70a59948f"
integrity sha512-2H+lYaunRh9jJ92m4JWEDDaM86SeexMg68lf4iJcjs47rJU0GnLYyme508EW3WsIfO9f1say9j9EDzoeHrNTNQ==
dependencies:
hoist-non-inferno-statics "^1.1.3"
inferno "8.2.2"
inferno-router@^8.2.2:
version "8.2.2"
resolved "https://registry.yarnpkg.com/inferno-router/-/inferno-router-8.2.2.tgz#3d13e2610ecb8f6bb3e36421ab569c21777a0d23"
@ -7719,6 +7742,16 @@ rechoir@^0.8.0:
dependencies:
resolve "^1.20.0"
redux-thunk@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3"
integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==
redux@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.0.tgz#29572e29a439e094ff8fec46883fc45053f6736d"
integrity sha512-blLIYmYetpZMET6Q6uCY7Jtl/Im5OBldy+vNPauA8vvsdqyt66oep4EUpAMWNHauTC6xa9JuRPhRB72rY82QGA==
reflect.getprototypeof@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3"
@ -7849,6 +7882,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
reselect@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.0.1.tgz#587cdaaeb4e0e8927cff80ebe2bbef05f74b1648"
integrity sha512-D72j2ubjgHpvuCiORWkOUxndHJrxDaSolheiz5CO+roz8ka97/4msh2E8F5qay4GawR5vzBt5MkbDHT+Rdy/Wg==
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"