diff --git a/package.json b/package.json index a76700b9..f6ea01cb 100644 --- a/package.json +++ b/package.json @@ -52,11 +52,13 @@ }, "devDependencies": { "@babel/core": "^7.17.9", + "@babel/plugin-proposal-decorators": "^7.18.2", "@babel/plugin-transform-runtime": "^7.17.0", "@babel/plugin-transform-typescript": "^7.16.1", "@babel/preset-env": "7.16.11", "@babel/preset-typescript": "^7.16.0", "@babel/runtime": "^7.17.9", + "@sniptt/monads": "^0.5.10", "@types/autosize": "^4.0.0", "@types/express": "^4.17.13", "@types/node": "^17.0.29", @@ -67,6 +69,7 @@ "babel-plugin-inferno": "^6.4.0", "bootstrap": "^5.1.3", "bootswatch": "^5.1.3", + "class-transformer": "^0.5.1", "clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^10.2.4", "css-loader": "^6.7.1", @@ -74,7 +77,7 @@ "eslint-plugin-prettier": "^4.0.0", "husky": "^7.0.4", "import-sort-style-module": "^6.0.0", - "lemmy-js-client": "0.16.4", + "lemmy-js-client": "0.17.0-rc.30", "lint-staged": "^12.4.1", "mini-css-extract-plugin": "^2.6.0", "node-fetch": "^2.6.1", @@ -82,6 +85,7 @@ "prettier-plugin-import-sort": "^0.0.7", "prettier-plugin-organize-imports": "^2.3.4", "prettier-plugin-packagejson": "^2.2.17", + "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "run-node-webpack-plugin": "^1.3.0", "sass-loader": "^12.6.0", diff --git a/src/client/index.tsx b/src/client/index.tsx index d5773b44..3838dca7 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -1,14 +1,15 @@ import { hydrate } from "inferno-hydrate"; import { BrowserRouter } from "inferno-router"; +import { GetSiteResponse } from "lemmy-js-client"; import { App } from "../shared/components/app/app"; -import { initializeSite } from "../shared/utils"; +import { convertWindowJson, initializeSite } from "../shared/utils"; -const site = window.isoData.site_res; +const site = convertWindowJson(GetSiteResponse, window.isoData.site_res); initializeSite(site); const wrapper = ( - + ); diff --git a/src/server/index.tsx b/src/server/index.tsx index 65f7308c..374fb031 100644 --- a/src/server/index.tsx +++ b/src/server/index.tsx @@ -1,3 +1,5 @@ +import { None, Option } from "@sniptt/monads"; +import { serialize as serializeO } from "class-transformer"; import express from "express"; import fs from "fs"; import { IncomingHttpHeaders } from "http"; @@ -5,7 +7,7 @@ import { Helmet } from "inferno-helmet"; import { matchPath, StaticRouter } from "inferno-router"; import { renderToString } from "inferno-server"; import IsomorphicCookie from "isomorphic-cookie"; -import { GetSite, GetSiteResponse, LemmyHttp } from "lemmy-js-client"; +import { GetSite, GetSiteResponse, LemmyHttp, toOption } from "lemmy-js-client"; import path from "path"; import process from "process"; import serialize from "serialize-javascript"; @@ -18,7 +20,7 @@ import { IsoData, } from "../shared/interfaces"; import { routes } from "../shared/routes"; -import { initializeSite, setOptionalAuth } from "../shared/utils"; +import { initializeSite } from "../shared/utils"; const server = express(); const [hostname, port] = process.env["LEMMY_UI_HOST"] @@ -115,10 +117,9 @@ server.get("/*", async (req, res) => { try { const activeRoute = routes.find(route => matchPath(req.path, route)) || {}; const context = {} as any; - let auth: string = IsomorphicCookie.load("jwt", req); + let auth: Option = toOption(IsomorphicCookie.load("jwt", req)); - let getSiteForm: GetSite = {}; - setOptionalAuth(getSiteForm, auth); + let getSiteForm = new GetSite({ auth }); let promises: Promise[] = []; @@ -138,8 +139,8 @@ server.get("/*", async (req, res) => { console.error( "Incorrect JWT token, skipping auth so frontend can remove jwt cookie" ); - delete getSiteForm.auth; - delete initialFetchReq.auth; + getSiteForm.auth = None; + initialFetchReq.auth = None; try_site = await initialFetchReq.client.getSite(getSiteForm); } let site: GetSiteResponse = try_site; @@ -170,7 +171,7 @@ server.get("/*", async (req, res) => { const wrapper = ( - + ); if (context.url) { @@ -194,7 +195,7 @@ server.get("/*", async (req, res) => { - + diff --git a/src/shared/components/app/app.tsx b/src/shared/components/app/app.tsx index 9ddff69f..72119a8d 100644 --- a/src/shared/components/app/app.tsx +++ b/src/shared/components/app/app.tsx @@ -2,53 +2,46 @@ import { Component } from "inferno"; import { Helmet } from "inferno-helmet"; import { Provider } from "inferno-i18next-dess"; import { Route, Switch } from "inferno-router"; -import { GetSiteResponse } from "lemmy-js-client"; import { i18n } from "../../i18next"; import { routes } from "../../routes"; -import { favIconPngUrl, favIconUrl } from "../../utils"; +import { favIconPngUrl, favIconUrl, setIsoData } from "../../utils"; import { Footer } from "./footer"; import { Navbar } from "./navbar"; import { NoMatch } from "./no-match"; import "./styles.scss"; import { Theme } from "./theme"; -export interface AppProps { - siteRes: GetSiteResponse; -} - -export class App extends Component { +export class App extends Component { + private isoData = setIsoData(this.context); constructor(props: any, context: any) { super(props, context); } render() { - let siteRes = this.props.siteRes; + let siteRes = this.isoData.site_res; + let siteView = siteRes.site_view; + return ( <>
- - {siteRes && - siteRes.site_view && - this.props.siteRes.site_view.site.icon && ( - - - - - )} - + s.site.default_theme)} /> + {siteView + .andThen(s => s.site.icon) + .match({ + some: icon => ( + + + + + ), + none: <>, + })} +
{routes.map(({ path, exact, component: C, ...rest }) => ( @@ -62,7 +55,7 @@ export class App extends Component { } />
-
+
diff --git a/src/shared/components/app/footer.tsx b/src/shared/components/app/footer.tsx index 601551b5..e5e1db5a 100644 --- a/src/shared/components/app/footer.tsx +++ b/src/shared/components/app/footer.tsx @@ -32,7 +32,9 @@ export class Footer extends Component { {i18n.t("modlog")} - {this.props.site.site_view?.site.legal_information && ( + {this.props.site.site_view + .andThen(s => s.site.legal_information) + .isSome() && (
  • {i18n.t("legal_information")} diff --git a/src/shared/components/app/navbar.tsx b/src/shared/components/app/navbar.tsx index 9ab3b656..604a90a5 100644 --- a/src/shared/components/app/navbar.tsx +++ b/src/shared/components/app/navbar.tsx @@ -1,3 +1,4 @@ +import { None, Some } from "@sniptt/monads"; import { Component, createRef, linkEvent, RefObject } from "inferno"; import { NavLink } from "inferno-router"; import { @@ -11,12 +12,15 @@ import { GetUnreadRegistrationApplicationCountResponse, PrivateMessageResponse, UserOperation, + wsJsonToRes, + wsUserOp, } from "lemmy-js-client"; import { Subscription } from "rxjs"; import { i18n } from "../../i18next"; import { UserService, WebSocketService } from "../../services"; import { - authField, + amAdmin, + auth, donateLemmyUrl, getLanguages, isBrowser, @@ -27,19 +31,16 @@ import { showAvatars, toast, wsClient, - wsJsonToRes, wsSubscribe, - wsUserOp, } from "../../utils"; import { Icon } from "../common/icon"; import { PictrsImage } from "../common/pictrs-image"; interface NavbarProps { - site_res: GetSiteResponse; + siteRes: GetSiteResponse; } interface NavbarState { - isLoggedIn: boolean; expanded: boolean; unreadInboxCount: number; unreadReportCount: number; @@ -58,7 +59,6 @@ export class Navbar extends Component { private unreadApplicationCountSub: Subscription; private searchTextField: RefObject; emptyState: NavbarState = { - isLoggedIn: !!this.props.site_res.my_user, unreadInboxCount: 0, unreadReportCount: 0, unreadApplicationCount: 0, @@ -81,18 +81,13 @@ export class Navbar extends Component { // Subscribe to jwt changes if (isBrowser()) { this.searchTextField = createRef(); - console.log(`isLoggedIn = ${this.state.isLoggedIn}`); // On the first load, check the unreads - if (this.state.isLoggedIn == false) { - // setTheme(data.my_user.theme, true); - // i18n.changeLanguage(getLanguage()); - // i18n.changeLanguage('de'); - } else { + if (UserService.Instance.myUserInfo.isSome()) { this.requestNotificationPermission(); WebSocketService.Instance.send( wsClient.userJoin({ - auth: authField(), + auth: auth().unwrap(), }) ); this.fetchUnreads(); @@ -100,13 +95,11 @@ export class Navbar extends Component { this.userSub = UserService.Instance.jwtSub.subscribe(res => { // A login - if (res !== undefined) { + if (res.isSome()) { this.requestNotificationPermission(); WebSocketService.Instance.send( - wsClient.getSite({ auth: authField() }) + wsClient.getSite({ auth: res.map(r => r.jwt) }) ); - } else { - this.setState({ isLoggedIn: false }); } }); @@ -157,32 +150,28 @@ export class Navbar extends Component { // TODO class active corresponding to current page navbar() { - let myUserInfo = - UserService.Instance.myUserInfo || this.props.site_res.my_user; - let person = myUserInfo?.local_user_view.person; return ( -
  • - - )} - - + + ), + none: <>, + })} ) : (