Merge branch 'main' into comment-border-tweak

This commit is contained in:
SleeplessOne1917 2023-07-05 02:42:33 +00:00 committed by GitHub
commit 96e216975a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 433 additions and 296 deletions

View file

@ -2,7 +2,7 @@ import { initializeSite, setupDateFns } from "@utils/app";
import { hydrate } from "inferno-hydrate"; import { hydrate } from "inferno-hydrate";
import { Router } from "inferno-router"; import { Router } from "inferno-router";
import { App } from "../shared/components/app/app"; import { App } from "../shared/components/app/app";
import { HistoryService } from "../shared/services"; import { HistoryService, UserService } from "../shared/services";
import "bootstrap/js/dist/collapse"; import "bootstrap/js/dist/collapse";
import "bootstrap/js/dist/dropdown"; import "bootstrap/js/dist/dropdown";
@ -14,7 +14,7 @@ async function startClient() {
const wrapper = ( const wrapper = (
<Router history={HistoryService.history}> <Router history={HistoryService.history}>
<App /> <App user={UserService.Instance.myUserInfo} />
</Router> </Router>
); );

View file

@ -114,7 +114,7 @@ export default async (req: Request, res: Response) => {
const wrapper = ( const wrapper = (
<StaticRouter location={url} context={isoData}> <StaticRouter location={url} context={isoData}>
<App /> <App user={site?.my_user} />
</StaticRouter> </StaticRouter>
); );

View file

@ -3,6 +3,7 @@ import { dataBsTheme } from "@utils/browser";
import { Component, RefObject, createRef, linkEvent } from "inferno"; import { Component, RefObject, createRef, linkEvent } from "inferno";
import { Provider } from "inferno-i18next-dess"; import { Provider } from "inferno-i18next-dess";
import { Route, Switch } from "inferno-router"; import { Route, Switch } from "inferno-router";
import { MyUserInfo } from "lemmy-js-client";
import { IsoDataOptionalSite } from "../../interfaces"; import { IsoDataOptionalSite } from "../../interfaces";
import { routes } from "../../routes"; import { routes } from "../../routes";
import { FirstLoadService, I18NextService, UserService } from "../../services"; import { FirstLoadService, I18NextService, UserService } from "../../services";
@ -14,10 +15,14 @@ import { Navbar } from "./navbar";
import "./styles.scss"; import "./styles.scss";
import { Theme } from "./theme"; import { Theme } from "./theme";
export class App extends Component<any, any> { interface AppProps {
user?: MyUserInfo;
}
export class App extends Component<AppProps, any> {
private isoData: IsoDataOptionalSite = setIsoData(this.context); private isoData: IsoDataOptionalSite = setIsoData(this.context);
private readonly mainContentRef: RefObject<HTMLElement>; private readonly mainContentRef: RefObject<HTMLElement>;
constructor(props: any, context: any) { constructor(props: AppProps, context: any) {
super(props, context); super(props, context);
this.mainContentRef = createRef(); this.mainContentRef = createRef();
} }
@ -29,10 +34,6 @@ export class App extends Component<any, any> {
user = UserService.Instance.myUserInfo; user = UserService.Instance.myUserInfo;
componentDidMount() {
this.setState({ bsTheme: dataBsTheme(this.user) });
}
render() { render() {
const siteRes = this.isoData.site_res; const siteRes = this.isoData.site_res;
const siteView = siteRes?.site_view; const siteView = siteRes?.site_view;
@ -43,7 +44,7 @@ export class App extends Component<any, any> {
<div <div
id="app" id="app"
className="lemmy-site" className="lemmy-site"
data-bs-theme={this.state?.bsTheme} data-bs-theme={dataBsTheme(this.props.user)}
> >
<button <button
type="button" type="button"

View file

@ -1,3 +1,5 @@
import { randomStr } from "@utils/helpers";
import classNames from "classnames";
import { Component, linkEvent } from "inferno"; import { Component, linkEvent } from "inferno";
import { DataType } from "../../interfaces"; import { DataType } from "../../interfaces";
import { I18NextService } from "../../services"; import { I18NextService } from "../../services";
@ -15,6 +17,8 @@ export class DataTypeSelect extends Component<
DataTypeSelectProps, DataTypeSelectProps,
DataTypeSelectState DataTypeSelectState
> { > {
private id = `listing-type-input-${randomStr()}`;
state: DataTypeSelectState = { state: DataTypeSelectState = {
type_: this.props.type_, type_: this.props.type_,
}; };
@ -31,33 +35,41 @@ export class DataTypeSelect extends Component<
render() { render() {
return ( return (
<div className="data-type-select btn-group btn-group-toggle flex-wrap"> <div
<label className="data-type-select btn-group btn-group-toggle flex-wrap"
className={`pointer btn btn-outline-secondary role="group"
${this.state.type_ == DataType.Post && "active"}
`}
> >
<input <input
id={`${this.id}-posts`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={DataType.Post} value={DataType.Post}
checked={this.state.type_ == DataType.Post} checked={this.state.type_ === DataType.Post}
onChange={linkEvent(this, this.handleTypeChange)} onChange={linkEvent(this, this.handleTypeChange)}
/> />
<label
htmlFor={`${this.id}-posts`}
className={classNames("pointer btn btn-outline-secondary", {
active: this.state.type_ === DataType.Post,
})}
>
{I18NextService.i18n.t("posts")} {I18NextService.i18n.t("posts")}
</label> </label>
<label
className={`pointer btn btn-outline-secondary ${
this.state.type_ == DataType.Comment && "active"
}`}
>
<input <input
id={`${this.id}-comments`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={DataType.Comment} value={DataType.Comment}
checked={this.state.type_ == DataType.Comment} checked={this.state.type_ === DataType.Comment}
onChange={linkEvent(this, this.handleTypeChange)} onChange={linkEvent(this, this.handleTypeChange)}
/> />
<label
htmlFor={`${this.id}-comments`}
className={classNames("pointer btn btn-outline-secondary", {
active: this.state.type_ === DataType.Comment,
})}
>
{I18NextService.i18n.t("comments")} {I18NextService.i18n.t("comments")}
</label> </label>
</div> </div>

View file

@ -1,4 +1,5 @@
import { randomStr } from "@utils/helpers"; import { randomStr } from "@utils/helpers";
import classNames from "classnames";
import { Component, linkEvent } from "inferno"; import { Component, linkEvent } from "inferno";
import { ListingType } from "lemmy-js-client"; import { ListingType } from "lemmy-js-client";
import { I18NextService, UserService } from "../../services"; import { I18NextService, UserService } from "../../services";
@ -38,60 +39,72 @@ export class ListingTypeSelect extends Component<
render() { render() {
return ( return (
<div className="listing-type-select btn-group btn-group-toggle flex-wrap"> <div
{this.props.showSubscribed && ( className="listing-type-select btn-group btn-group-toggle flex-wrap"
<label role="group"
title={I18NextService.i18n.t("subscribed_description")}
className={`btn btn-outline-secondary
${this.state.type_ == "Subscribed" && "active"}
${!UserService.Instance.myUserInfo ? "disabled" : "pointer"}
`}
> >
{this.props.showSubscribed && (
<>
<input <input
id={`${this.id}-subscribed`} id={`${this.id}-subscribed`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={"Subscribed"} value={"Subscribed"}
checked={this.state.type_ == "Subscribed"} checked={this.state.type_ === "Subscribed"}
onChange={linkEvent(this, this.handleTypeChange)} onChange={linkEvent(this, this.handleTypeChange)}
disabled={!UserService.Instance.myUserInfo} disabled={!UserService.Instance.myUserInfo}
/> />
<label
htmlFor={`${this.id}-subscribed`}
title={I18NextService.i18n.t("subscribed_description")}
className={classNames("btn btn-outline-secondary", {
active: this.state.type_ === "Subscribed",
disabled: !UserService.Instance.myUserInfo,
pointer: UserService.Instance.myUserInfo,
})}
>
{I18NextService.i18n.t("subscribed")} {I18NextService.i18n.t("subscribed")}
</label> </label>
</>
)} )}
{this.props.showLocal && ( {this.props.showLocal && (
<label <>
title={I18NextService.i18n.t("local_description")}
className={`pointer btn btn-outline-secondary ${
this.state.type_ == "Local" && "active"
}`}
>
<input <input
id={`${this.id}-local`} id={`${this.id}-local`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={"Local"} value={"Local"}
checked={this.state.type_ == "Local"} checked={this.state.type_ === "Local"}
onChange={linkEvent(this, this.handleTypeChange)} onChange={linkEvent(this, this.handleTypeChange)}
/> />
<label
htmlFor={`${this.id}-local`}
title={I18NextService.i18n.t("local_description")}
className={classNames("pointer btn btn-outline-secondary", {
active: this.state.type_ === "Local",
})}
>
{I18NextService.i18n.t("local")} {I18NextService.i18n.t("local")}
</label> </label>
</>
)} )}
<label
title={I18NextService.i18n.t("all_description")}
className={`pointer btn btn-outline-secondary ${
(this.state.type_ == "All" && "active") ||
(!this.props.showLocal && this.state.type_ == "Local" && "active")
}`}
>
<input <input
id={`${this.id}-all`} id={`${this.id}-all`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={"All"} value={"All"}
checked={this.state.type_ == "All"} checked={this.state.type_ === "All"}
onChange={linkEvent(this, this.handleTypeChange)} onChange={linkEvent(this, this.handleTypeChange)}
/> />
<label
title={I18NextService.i18n.t("all_description")}
htmlFor={`${this.id}-all`}
className={classNames("pointer btn btn-outline-secondary", {
active:
this.state.type_ === "All" ||
(!this.props.showLocal && this.state.type_) === "Local",
})}
>
{I18NextService.i18n.t("all")} {I18NextService.i18n.t("all")}
</label> </label>
</div> </div>

View file

@ -53,7 +53,7 @@ export class UserBadges extends Component<UserBadgesProps> {
{getRoleLabelPill({ {getRoleLabelPill({
label: I18NextService.i18n.t("banned"), label: I18NextService.i18n.t("banned"),
tooltip: I18NextService.i18n.t("banned"), tooltip: I18NextService.i18n.t("banned"),
classes: "text-bg-danger", classes: "text-danger border border-danger",
shrink: false, shrink: false,
})} })}
</span> </span>
@ -63,7 +63,7 @@ export class UserBadges extends Component<UserBadgesProps> {
{getRoleLabelPill({ {getRoleLabelPill({
label: I18NextService.i18n.t("deleted"), label: I18NextService.i18n.t("deleted"),
tooltip: I18NextService.i18n.t("deleted"), tooltip: I18NextService.i18n.t("deleted"),
classes: "text-bg-danger", classes: "text-danger border border-danger",
shrink: false, shrink: false,
})} })}
</span> </span>
@ -74,7 +74,7 @@ export class UserBadges extends Component<UserBadgesProps> {
{getRoleLabelPill({ {getRoleLabelPill({
label: I18NextService.i18n.t("op").toUpperCase(), label: I18NextService.i18n.t("op").toUpperCase(),
tooltip: I18NextService.i18n.t("creator"), tooltip: I18NextService.i18n.t("creator"),
classes: "text-bg-info", classes: "text-info border border-info",
shrink: false, shrink: false,
})} })}
</span> </span>
@ -84,7 +84,7 @@ export class UserBadges extends Component<UserBadgesProps> {
{getRoleLabelPill({ {getRoleLabelPill({
label: I18NextService.i18n.t("mod"), label: I18NextService.i18n.t("mod"),
tooltip: I18NextService.i18n.t("mod"), tooltip: I18NextService.i18n.t("mod"),
classes: "text-bg-primary", classes: "text-primary border border-primary",
})} })}
</span> </span>
)} )}
@ -93,7 +93,7 @@ export class UserBadges extends Component<UserBadgesProps> {
{getRoleLabelPill({ {getRoleLabelPill({
label: I18NextService.i18n.t("admin"), label: I18NextService.i18n.t("admin"),
tooltip: I18NextService.i18n.t("admin"), tooltip: I18NextService.i18n.t("admin"),
classes: "text-bg-danger", classes: "text-danger border border-danger",
})} })}
</span> </span>
)} )}

View file

@ -114,7 +114,7 @@ interface HomeState {
} }
interface HomeProps { interface HomeProps {
listingType: ListingType; listingType?: ListingType;
dataType: DataType; dataType: DataType;
sort: SortType; sort: SortType;
page: number; page: number;
@ -163,12 +163,12 @@ function getDataTypeFromQuery(type?: string): DataType {
return type ? DataType[type] : DataType.Post; return type ? DataType[type] : DataType.Post;
} }
function getListingTypeFromQuery(type?: string): ListingType { function getListingTypeFromQuery(type?: string): ListingType | undefined {
const myListingType = const myListingType =
UserService.Instance.myUserInfo?.local_user_view?.local_user UserService.Instance.myUserInfo?.local_user_view?.local_user
?.default_listing_type; ?.default_listing_type;
return (type ? (type as ListingType) : myListingType) ?? "Local"; return type ? (type as ListingType) : myListingType;
} }
function getSortTypeFromQuery(type?: string): SortType { function getSortTypeFromQuery(type?: string): SortType {
@ -311,11 +311,12 @@ export class Home extends Component<any, HomeState> {
client, client,
auth, auth,
query: { dataType: urlDataType, listingType, page: urlPage, sort: urlSort }, query: { dataType: urlDataType, listingType, page: urlPage, sort: urlSort },
site,
}: InitialFetchRequest<QueryParams<HomeProps>>): Promise<HomeData> { }: InitialFetchRequest<QueryParams<HomeProps>>): Promise<HomeData> {
const dataType = getDataTypeFromQuery(urlDataType); const dataType = getDataTypeFromQuery(urlDataType);
const type_ =
// TODO figure out auth default_listingType, default_sort_type getListingTypeFromQuery(listingType) ??
const type_ = getListingTypeFromQuery(listingType); site.site_view.local_site.default_post_listing_type;
const sort = getSortTypeFromQuery(urlSort); const sort = getSortTypeFromQuery(urlSort);
const page = urlPage ? Number(urlPage) : 1; const page = urlPage ? Number(urlPage) : 1;
@ -761,7 +762,10 @@ export class Home extends Component<any, HomeState> {
</div> </div>
<div className="col-auto"> <div className="col-auto">
<ListingTypeSelect <ListingTypeSelect
type_={listingType} type_={
listingType ??
this.state.siteRes.site_view.local_site.default_post_listing_type
}
showLocal={showLocal(this.isoData)} showLocal={showLocal(this.isoData)}
showSubscribed showSubscribed
onChange={this.handleListingTypeChange} onChange={this.handleListingTypeChange}
@ -770,7 +774,12 @@ export class Home extends Component<any, HomeState> {
<div className="col-auto"> <div className="col-auto">
<SortSelect sort={sort} onChange={this.handleSortChange} /> <SortSelect sort={sort} onChange={this.handleSortChange} />
</div> </div>
<div className="col-auto ps-0">{getRss(listingType)}</div> <div className="col-auto ps-0">
{getRss(
listingType ??
this.state.siteRes.site_view.local_site.default_post_listing_type
)}
</div>
</div> </div>
); );
} }

View file

@ -175,7 +175,9 @@ export class Login extends Component<any, State> {
} }
case "success": { case "success": {
UserService.Instance.login(loginRes.data); UserService.Instance.login({
res: loginRes.data,
});
const site = await HttpService.client.getSite({ const site = await HttpService.client.getSite({
auth: myAuth(), auth: myAuth(),
}); });

View file

@ -206,7 +206,7 @@ export class Setup extends Component<any, State> {
if (i.state.registerRes.state == "success") { if (i.state.registerRes.state == "success") {
const data = i.state.registerRes.data; const data = i.state.registerRes.data;
UserService.Instance.login(data); UserService.Instance.login({ res: data });
i.setState({ doneRegisteringUser: true }); i.setState({ doneRegisteringUser: true });
} }
} }

View file

@ -469,7 +469,9 @@ export class Signup extends Component<any, State> {
// Only log them in if a jwt was set // Only log them in if a jwt was set
if (data.jwt) { if (data.jwt) {
UserService.Instance.login(data); UserService.Instance.login({
res: data,
});
const site = await HttpService.client.getSite({ auth: myAuth() }); const site = await HttpService.client.getSite({ auth: myAuth() });

View file

@ -11,8 +11,9 @@ import {
setIsoData, setIsoData,
updatePersonBlock, updatePersonBlock,
} from "@utils/app"; } from "@utils/app";
import { capitalizeFirstLetter } from "@utils/helpers"; import { capitalizeFirstLetter, randomStr } from "@utils/helpers";
import { RouteDataResponse } from "@utils/types"; import { RouteDataResponse } from "@utils/types";
import classNames from "classnames";
import { Component, linkEvent } from "inferno"; import { Component, linkEvent } from "inferno";
import { import {
AddAdmin, AddAdmin,
@ -283,34 +284,41 @@ export class Inbox extends Component<any, InboxState> {
} }
unreadOrAllRadios() { unreadOrAllRadios() {
const radioId = randomStr();
return ( return (
<div className="btn-group btn-group-toggle flex-wrap"> <div className="btn-group btn-group-toggle flex-wrap" role="group">
<label
className={`btn btn-outline-secondary pointer
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
`}
>
<input <input
id={`${radioId}-unread`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={UnreadOrAll.Unread} value={UnreadOrAll.Unread}
checked={this.state.unreadOrAll == UnreadOrAll.Unread} checked={this.state.unreadOrAll === UnreadOrAll.Unread}
onChange={linkEvent(this, this.handleUnreadOrAllChange)} onChange={linkEvent(this, this.handleUnreadOrAllChange)}
/> />
<label
htmlFor={`${radioId}-unread`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.unreadOrAll === UnreadOrAll.Unread,
})}
>
{I18NextService.i18n.t("unread")} {I18NextService.i18n.t("unread")}
</label> </label>
<label
className={`btn btn-outline-secondary pointer
${this.state.unreadOrAll == UnreadOrAll.All && "active"}
`}
>
<input <input
id={`${radioId}-all`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={UnreadOrAll.All} value={UnreadOrAll.All}
checked={this.state.unreadOrAll == UnreadOrAll.All} checked={this.state.unreadOrAll === UnreadOrAll.All}
onChange={linkEvent(this, this.handleUnreadOrAllChange)} onChange={linkEvent(this, this.handleUnreadOrAllChange)}
/> />
<label
htmlFor={`${radioId}-all`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.unreadOrAll === UnreadOrAll.All,
})}
>
{I18NextService.i18n.t("all")} {I18NextService.i18n.t("all")}
</label> </label>
</div> </div>
@ -318,62 +326,75 @@ export class Inbox extends Component<any, InboxState> {
} }
messageTypeRadios() { messageTypeRadios() {
const radioId = randomStr();
return ( return (
<div className="btn-group btn-group-toggle flex-wrap"> <div className="btn-group btn-group-toggle flex-wrap" role="group">
<label
className={`btn btn-outline-secondary pointer
${this.state.messageType == MessageType.All && "active"}
`}
>
<input <input
id={`${radioId}-all`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={MessageType.All} value={MessageType.All}
checked={this.state.messageType == MessageType.All} checked={this.state.messageType === MessageType.All}
onChange={linkEvent(this, this.handleMessageTypeChange)} onChange={linkEvent(this, this.handleMessageTypeChange)}
/> />
<label
htmlFor={`${radioId}-all`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.messageType === MessageType.All,
})}
>
{I18NextService.i18n.t("all")} {I18NextService.i18n.t("all")}
</label> </label>
<label
className={`btn btn-outline-secondary pointer
${this.state.messageType == MessageType.Replies && "active"}
`}
>
<input <input
id={`${radioId}-replies`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={MessageType.Replies} value={MessageType.Replies}
checked={this.state.messageType == MessageType.Replies} checked={this.state.messageType === MessageType.Replies}
onChange={linkEvent(this, this.handleMessageTypeChange)} onChange={linkEvent(this, this.handleMessageTypeChange)}
/> />
<label
htmlFor={`${radioId}-replies`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.messageType === MessageType.Replies,
})}
>
{I18NextService.i18n.t("replies")} {I18NextService.i18n.t("replies")}
</label> </label>
<label
className={`btn btn-outline-secondary pointer
${this.state.messageType == MessageType.Mentions && "active"}
`}
>
<input <input
id={`${radioId}-mentions`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={MessageType.Mentions} value={MessageType.Mentions}
checked={this.state.messageType == MessageType.Mentions} checked={this.state.messageType === MessageType.Mentions}
onChange={linkEvent(this, this.handleMessageTypeChange)} onChange={linkEvent(this, this.handleMessageTypeChange)}
/> />
<label
htmlFor={`${radioId}-mentions`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.messageType === MessageType.Mentions,
})}
>
{I18NextService.i18n.t("mentions")} {I18NextService.i18n.t("mentions")}
</label> </label>
<label
className={`btn btn-outline-secondary pointer
${this.state.messageType == MessageType.Messages && "active"}
`}
>
<input <input
id={`${radioId}-messages`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={MessageType.Messages} value={MessageType.Messages}
checked={this.state.messageType == MessageType.Messages} checked={this.state.messageType === MessageType.Messages}
onChange={linkEvent(this, this.handleMessageTypeChange)} onChange={linkEvent(this, this.handleMessageTypeChange)}
/> />
<label
htmlFor={`${radioId}-messages`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.messageType === MessageType.Messages,
})}
>
{I18NextService.i18n.t("messages")} {I18NextService.i18n.t("messages")}
</label> </label>
</div> </div>

View file

@ -135,7 +135,9 @@ export class PasswordChange extends Component<any, State> {
if (i.state.passwordChangeRes.state === "success") { if (i.state.passwordChangeRes.state === "success") {
const data = i.state.passwordChangeRes.data; const data = i.state.passwordChangeRes.data;
UserService.Instance.login(data); UserService.Instance.login({
res: data,
});
const site = await HttpService.client.getSite({ auth: myAuth() }); const site = await HttpService.client.getSite({ auth: myAuth() });
if (site.state === "success") { if (site.state === "success") {

View file

@ -18,6 +18,7 @@ import {
getQueryParams, getQueryParams,
getQueryString, getQueryString,
numToSI, numToSI,
randomStr,
} from "@utils/helpers"; } from "@utils/helpers";
import { canMod, isAdmin, isBanned } from "@utils/roles"; import { canMod, isAdmin, isBanned } from "@utils/roles";
import type { QueryParams } from "@utils/types"; import type { QueryParams } from "@utils/types";
@ -397,7 +398,7 @@ export class Profile extends Component<
get viewRadios() { get viewRadios() {
return ( return (
<div className="btn-group btn-group-toggle flex-wrap mb-2"> <div className="btn-group btn-group-toggle flex-wrap mb-2" role="group">
{this.getRadio(PersonDetailsView.Overview)} {this.getRadio(PersonDetailsView.Overview)}
{this.getRadio(PersonDetailsView.Comments)} {this.getRadio(PersonDetailsView.Comments)}
{this.getRadio(PersonDetailsView.Posts)} {this.getRadio(PersonDetailsView.Posts)}
@ -409,22 +410,27 @@ export class Profile extends Component<
getRadio(view: PersonDetailsView) { getRadio(view: PersonDetailsView) {
const { view: urlView } = getProfileQueryParams(); const { view: urlView } = getProfileQueryParams();
const active = view === urlView; const active = view === urlView;
const radioId = randomStr();
return ( return (
<label <>
className={classNames("btn btn-outline-secondary pointer", {
active,
})}
>
<input <input
id={radioId}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={view} value={view}
checked={active} checked={active}
onChange={linkEvent(this, this.handleViewChange)} onChange={linkEvent(this, this.handleViewChange)}
/> />
<label
htmlFor={radioId}
className={classNames("btn btn-outline-secondary pointer", {
active,
})}
>
{I18NextService.i18n.t(view.toLowerCase() as NoOptionI18nKeys)} {I18NextService.i18n.t(view.toLowerCase() as NoOptionI18nKeys)}
</label> </label>
</>
); );
} }

View file

@ -3,7 +3,9 @@ import {
myAuthRequired, myAuthRequired,
setIsoData, setIsoData,
} from "@utils/app"; } from "@utils/app";
import { randomStr } from "@utils/helpers";
import { RouteDataResponse } from "@utils/types"; import { RouteDataResponse } from "@utils/types";
import classNames from "classnames";
import { Component, linkEvent } from "inferno"; import { Component, linkEvent } from "inferno";
import { import {
ApproveRegistrationApplication, ApproveRegistrationApplication,
@ -125,34 +127,41 @@ export class RegistrationApplications extends Component<
} }
unreadOrAllRadios() { unreadOrAllRadios() {
const radioId = randomStr();
return ( return (
<div className="btn-group btn-group-toggle flex-wrap mb-2"> <div className="btn-group btn-group-toggle flex-wrap mb-2" role="group">
<label
className={`btn btn-outline-secondary pointer
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
`}
>
<input <input
id={`${radioId}-unread`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={UnreadOrAll.Unread} value={UnreadOrAll.Unread}
checked={this.state.unreadOrAll == UnreadOrAll.Unread} checked={this.state.unreadOrAll === UnreadOrAll.Unread}
onChange={linkEvent(this, this.handleUnreadOrAllChange)} onChange={linkEvent(this, this.handleUnreadOrAllChange)}
/> />
<label
htmlFor={`${radioId}-unread`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.unreadOrAll === UnreadOrAll.Unread,
})}
>
{I18NextService.i18n.t("unread")} {I18NextService.i18n.t("unread")}
</label> </label>
<label
className={`btn btn-outline-secondary pointer
${this.state.unreadOrAll == UnreadOrAll.All && "active"}
`}
>
<input <input
id={`${radioId}-all`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={UnreadOrAll.All} value={UnreadOrAll.All}
checked={this.state.unreadOrAll == UnreadOrAll.All} checked={this.state.unreadOrAll === UnreadOrAll.All}
onChange={linkEvent(this, this.handleUnreadOrAllChange)} onChange={linkEvent(this, this.handleUnreadOrAllChange)}
/> />
<label
htmlFor={`${radioId}-all`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.unreadOrAll === UnreadOrAll.All,
})}
>
{I18NextService.i18n.t("all")} {I18NextService.i18n.t("all")}
</label> </label>
</div> </div>

View file

@ -5,8 +5,10 @@ import {
myAuthRequired, myAuthRequired,
setIsoData, setIsoData,
} from "@utils/app"; } from "@utils/app";
import { randomStr } from "@utils/helpers";
import { amAdmin } from "@utils/roles"; import { amAdmin } from "@utils/roles";
import { RouteDataResponse } from "@utils/types"; import { RouteDataResponse } from "@utils/types";
import classNames from "classnames";
import { Component, linkEvent } from "inferno"; import { Component, linkEvent } from "inferno";
import { import {
CommentReportResponse, CommentReportResponse,
@ -187,34 +189,41 @@ export class Reports extends Component<any, ReportsState> {
} }
unreadOrAllRadios() { unreadOrAllRadios() {
const radioId = randomStr();
return ( return (
<div className="btn-group btn-group-toggle flex-wrap mb-2"> <div className="btn-group btn-group-toggle flex-wrap mb-2" role="group">
<label
className={`btn btn-outline-secondary pointer
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
`}
>
<input <input
id={`${radioId}-unread`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={UnreadOrAll.Unread} value={UnreadOrAll.Unread}
checked={this.state.unreadOrAll == UnreadOrAll.Unread} checked={this.state.unreadOrAll === UnreadOrAll.Unread}
onChange={linkEvent(this, this.handleUnreadOrAllChange)} onChange={linkEvent(this, this.handleUnreadOrAllChange)}
/> />
<label
htmlFor={`${radioId}-unread`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.unreadOrAll === UnreadOrAll.Unread,
})}
>
{I18NextService.i18n.t("unread")} {I18NextService.i18n.t("unread")}
</label> </label>
<label
className={`btn btn-outline-secondary pointer
${this.state.unreadOrAll == UnreadOrAll.All && "active"}
`}
>
<input <input
id={`${radioId}-all`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={UnreadOrAll.All} value={UnreadOrAll.All}
checked={this.state.unreadOrAll == UnreadOrAll.All} checked={this.state.unreadOrAll === UnreadOrAll.All}
onChange={linkEvent(this, this.handleUnreadOrAllChange)} onChange={linkEvent(this, this.handleUnreadOrAllChange)}
/> />
<label
htmlFor={`${radioId}-all`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.unreadOrAll === UnreadOrAll.All,
})}
>
{I18NextService.i18n.t("all")} {I18NextService.i18n.t("all")}
</label> </label>
</div> </div>
@ -222,70 +231,83 @@ export class Reports extends Component<any, ReportsState> {
} }
messageTypeRadios() { messageTypeRadios() {
const radioId = randomStr();
return ( return (
<div className="btn-group btn-group-toggle flex-wrap mb-2"> <div className="btn-group btn-group-toggle flex-wrap mb-2" role="group">
<label
className={`btn btn-outline-secondary pointer
${this.state.messageType == MessageType.All && "active"}
`}
>
<input <input
id={`${radioId}-all`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={MessageType.All} value={MessageType.All}
checked={this.state.messageType == MessageType.All} checked={this.state.messageType === MessageType.All}
onChange={linkEvent(this, this.handleMessageTypeChange)} onChange={linkEvent(this, this.handleMessageTypeChange)}
/> />
<label
htmlFor={`${radioId}-all`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.messageType === MessageType.All,
})}
>
{I18NextService.i18n.t("all")} {I18NextService.i18n.t("all")}
</label> </label>
<label
className={`btn btn-outline-secondary pointer
${this.state.messageType == MessageType.CommentReport && "active"}
`}
>
<input <input
id={`${radioId}-comments`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={MessageType.CommentReport} value={MessageType.CommentReport}
checked={this.state.messageType == MessageType.CommentReport} checked={this.state.messageType === MessageType.CommentReport}
onChange={linkEvent(this, this.handleMessageTypeChange)} onChange={linkEvent(this, this.handleMessageTypeChange)}
/> />
<label
htmlFor={`${radioId}-comments`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.messageType === MessageType.CommentReport,
})}
>
{I18NextService.i18n.t("comments")} {I18NextService.i18n.t("comments")}
</label> </label>
<label
className={`btn btn-outline-secondary pointer
${this.state.messageType == MessageType.PostReport && "active"}
`}
>
<input <input
id={`${radioId}-posts`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={MessageType.PostReport} value={MessageType.PostReport}
checked={this.state.messageType == MessageType.PostReport} checked={this.state.messageType === MessageType.PostReport}
onChange={linkEvent(this, this.handleMessageTypeChange)} onChange={linkEvent(this, this.handleMessageTypeChange)}
/> />
<label
htmlFor={`${radioId}-posts`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.messageType === MessageType.PostReport,
})}
>
{I18NextService.i18n.t("posts")} {I18NextService.i18n.t("posts")}
</label> </label>
{amAdmin() && ( {amAdmin() && (
<label <>
className={`btn btn-outline-secondary pointer
${
this.state.messageType == MessageType.PrivateMessageReport &&
"active"
}
`}
>
<input <input
id={`${radioId}-messages`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={MessageType.PrivateMessageReport} value={MessageType.PrivateMessageReport}
checked={ checked={
this.state.messageType == MessageType.PrivateMessageReport this.state.messageType === MessageType.PrivateMessageReport
} }
onChange={linkEvent(this, this.handleMessageTypeChange)} onChange={linkEvent(this, this.handleMessageTypeChange)}
/> />
<label
htmlFor={`${radioId}-messages`}
className={classNames("btn btn-outline-secondary pointer", {
active:
this.state.messageType === MessageType.PrivateMessageReport,
})}
>
{I18NextService.i18n.t("messages")} {I18NextService.i18n.t("messages")}
</label> </label>
</>
)} )}
</div> </div>
); );

View file

@ -1175,8 +1175,12 @@ export class Settings extends Component<any, SettingsState> {
...i.state.saveUserSettingsForm, ...i.state.saveUserSettingsForm,
auth: myAuthRequired(), auth: myAuthRequired(),
}); });
if (saveRes.state === "success") { if (saveRes.state === "success") {
UserService.Instance.login(saveRes.data); UserService.Instance.login({
res: saveRes.data,
showToast: false,
});
toast(I18NextService.i18n.t("saved")); toast(I18NextService.i18n.t("saved"));
window.scrollTo(0, 0); window.scrollTo(0, 0);
} }
@ -1198,7 +1202,10 @@ export class Settings extends Component<any, SettingsState> {
auth: myAuthRequired(), auth: myAuthRequired(),
}); });
if (changePasswordRes.state === "success") { if (changePasswordRes.state === "success") {
UserService.Instance.login(changePasswordRes.data); UserService.Instance.login({
res: changePasswordRes.data,
showToast: false,
});
window.scrollTo(0, 0); window.scrollTo(0, 0);
toast(I18NextService.i18n.t("password_changed")); toast(I18NextService.i18n.t("password_changed"));
} }

View file

@ -19,10 +19,11 @@ import {
restoreScrollPosition, restoreScrollPosition,
saveScrollPosition, saveScrollPosition,
} from "@utils/browser"; } from "@utils/browser";
import { debounce } from "@utils/helpers"; import { debounce, randomStr } from "@utils/helpers";
import { isImage } from "@utils/media"; import { isImage } from "@utils/media";
import { RouteDataResponse } from "@utils/types"; import { RouteDataResponse } from "@utils/types";
import autosize from "autosize"; import autosize from "autosize";
import classNames from "classnames";
import { Component, RefObject, createRef, linkEvent } from "inferno"; import { Component, RefObject, createRef, linkEvent } from "inferno";
import { import {
AddAdmin, AddAdmin,
@ -430,80 +431,98 @@ export class Post extends Component<any, PostState> {
} }
sortRadios() { sortRadios() {
const radioId =
this.state.postRes.state === "success"
? this.state.postRes.data.post_view.post.id
: randomStr();
return ( return (
<> <>
<div className="btn-group btn-group-toggle flex-wrap me-3 mb-2"> <div
<label className="btn-group btn-group-toggle flex-wrap me-3 mb-2"
className={`btn btn-outline-secondary pointer ${ role="group"
this.state.commentSort === "Hot" && "active"
}`}
> >
{I18NextService.i18n.t("hot")}
<input <input
id={`${radioId}-hot`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={"Hot"} value={"Hot"}
checked={this.state.commentSort === "Hot"} checked={this.state.commentSort === "Hot"}
onChange={linkEvent(this, this.handleCommentSortChange)} onChange={linkEvent(this, this.handleCommentSortChange)}
/> />
</label>
<label <label
className={`btn btn-outline-secondary pointer ${ htmlFor={`${radioId}-hot`}
this.state.commentSort === "Top" && "active" className={classNames("btn btn-outline-secondary pointer", {
}`} active: this.state.commentSort === "Hot",
})}
> >
{I18NextService.i18n.t("top")} {I18NextService.i18n.t("hot")}
</label>
<input <input
id={`${radioId}-top`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={"Top"} value={"Top"}
checked={this.state.commentSort === "Top"} checked={this.state.commentSort === "Top"}
onChange={linkEvent(this, this.handleCommentSortChange)} onChange={linkEvent(this, this.handleCommentSortChange)}
/> />
</label>
<label <label
className={`btn btn-outline-secondary pointer ${ htmlFor={`${radioId}-top`}
this.state.commentSort === "New" && "active" className={classNames("btn btn-outline-secondary pointer", {
}`} active: this.state.commentSort === "Top",
})}
> >
{I18NextService.i18n.t("new")} {I18NextService.i18n.t("top")}
</label>
<input <input
id={`${radioId}-new`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={"New"} value={"New"}
checked={this.state.commentSort === "New"} checked={this.state.commentSort === "New"}
onChange={linkEvent(this, this.handleCommentSortChange)} onChange={linkEvent(this, this.handleCommentSortChange)}
/> />
</label>
<label <label
className={`btn btn-outline-secondary pointer ${ htmlFor={`${radioId}-new`}
this.state.commentSort === "Old" && "active" className={classNames("btn btn-outline-secondary pointer", {
}`} active: this.state.commentSort === "New",
})}
> >
{I18NextService.i18n.t("old")} {I18NextService.i18n.t("new")}
</label>
<input <input
id={`${radioId}-old`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={"Old"} value={"Old"}
checked={this.state.commentSort === "Old"} checked={this.state.commentSort === "Old"}
onChange={linkEvent(this, this.handleCommentSortChange)} onChange={linkEvent(this, this.handleCommentSortChange)}
/> />
<label
htmlFor={`${radioId}-old`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.commentSort === "Old",
})}
>
{I18NextService.i18n.t("old")}
</label> </label>
</div> </div>
<div className="btn-group btn-group-toggle flex-wrap mb-2"> <div className="btn-group btn-group-toggle flex-wrap mb-2" role="group">
<label
className={`btn btn-outline-secondary pointer ${
this.state.commentViewType === CommentViewType.Flat && "active"
}`}
>
{I18NextService.i18n.t("chat")}
<input <input
id={`${radioId}-chat`}
type="radio" type="radio"
className="btn-check" className="btn-check"
value={CommentViewType.Flat} value={CommentViewType.Flat}
checked={this.state.commentViewType === CommentViewType.Flat} checked={this.state.commentViewType === CommentViewType.Flat}
onChange={linkEvent(this, this.handleCommentViewTypeChange)} onChange={linkEvent(this, this.handleCommentViewTypeChange)}
/> />
<label
htmlFor={`${radioId}-chat`}
className={classNames("btn btn-outline-secondary pointer", {
active: this.state.commentViewType === CommentViewType.Flat,
})}
>
{I18NextService.i18n.t("chat")}
</label> </label>
</div> </div>
</> </>

View file

@ -26,12 +26,18 @@ export class UserService {
this.#setJwtInfo(); this.#setJwtInfo();
} }
public login(res: LoginResponse) { public login({
res,
showToast = true,
}: {
res: LoginResponse;
showToast?: boolean;
}) {
const expires = new Date(); const expires = new Date();
expires.setDate(expires.getDate() + 365); expires.setDate(expires.getDate() + 365);
if (isBrowser() && res.jwt) { if (isBrowser() && res.jwt) {
toast(I18NextService.i18n.t("logged_in")); showToast && toast(I18NextService.i18n.t("logged_in"));
setAuthCookie(res.jwt); setAuthCookie(res.jwt);
this.#setJwtInfo(); this.#setJwtInfo();
} }

View file

@ -1,11 +1,17 @@
import { MyUserInfo } from "lemmy-js-client";
import isDark from "./is-dark"; import isDark from "./is-dark";
export default function dataBsTheme(user) { export default function dataBsTheme(user?: MyUserInfo) {
return (isDark() && user?.local_user_view.local_user.theme === "browser") || return (isDark() && user?.local_user_view.local_user.theme === "browser") ||
(user && (user &&
["darkly", "darkly-red", "darkly-pureblack"].includes( [
user.local_user_view.local_user.theme "darkly",
)) "darkly-red",
"darkly-pureblack",
"darkly-compact",
"i386",
"vaporwave-dark",
].includes(user.local_user_view.local_user.theme))
? "dark" ? "dark"
: "light"; : "light";
} }