commit 91ad818ad2be4c0f55d2d31613ba8a9eafb17e30
parent b53580e256030d0d6ea50cc1e202ce6ee0f7257e
Author: MTRNord <mtrnord1@gmail.com>
Date: Tue, 4 Jan 2022 02:43:38 +0100
Improve profile page header
Diffstat:
7 files changed, 147 insertions(+), 43 deletions(-)
diff --git a/helpers/event_types.ts b/helpers/event_types.ts
@@ -52,5 +52,15 @@ export type ImageGalleryEvent = MatrixEventBase & {
content: ImageGalleryContent;
};
+export type BannerEventContent = {
+ "m.text": string;
+ "m.file": FileEvent;
+ "m.image": ImageFields;
+};
+
+export type BannerEvent = MatrixEventBase & {
+ content: BannerEventContent;
+};
+
export type MatrixImageEvents = ImageEvent | ImageGalleryEvent;
-export type MatrixEvent = MatrixImageEvents;
-\ No newline at end of file
+export type MatrixEvent = MatrixImageEvents | BannerEvent;
+\ No newline at end of file
diff --git a/pages/Home.tsx b/pages/Home.tsx
@@ -1,7 +1,6 @@
import React, { Component } from 'react';
import { RingLoader } from 'react-spinners';
-import { ImageEvent, ImageGalleryEvent, MatrixEventBase, MatrixImageEvents } from '../helpers/event_types';
-import Link from 'next/link';
+import { MatrixEventBase, MatrixImageEvents } from '../helpers/event_types';
import Head from 'next/head';
import Header from '../components/Header';
import { ClientContext } from '../components/ClientContext';
@@ -68,6 +67,7 @@ export default class Home extends Component<Props, State>{
try {
let serverUrl = constMatrixArtServer + "/_matrix/client";
await this.context.client?.registerAsGuest(serverUrl);
+ await this.context.guest_client?.registerAsGuest(serverUrl);
if (typeof window !== "undefined") {
window.location.reload();
}
diff --git a/pages/api/directory.tsx b/pages/api/directory.tsx
@@ -20,9 +20,9 @@ export const get_data = async () => {
console.log("Running in tests!");
return [
{
- "user_id": "@MTRNord:art.midnightthoughts.space",
- "user_room": "#@MTRNord:art.midnightthoughts.space",
- "_id": "@MTRNord:art.midnightthoughts.space"
+ "user_id": "@mtrnord:art.midnightthoughts.space",
+ "user_room": "#@mtrnord:art.midnightthoughts.space",
+ "_id": "@mtrnord:art.midnightthoughts.space"
}
];
}
@@ -34,10 +34,6 @@ export const get_data = async () => {
// TODO this is fully insecured. Make sure to use OpenID or something to verify the user of this.
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
- db.info().then(function (info) {
- console.log(info);
- });
-
// Run cors
await cors(req, res);
@@ -71,6 +67,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const data: {
user_id: string;
} = req.body;
+ console.log(data.user_id);
try {
const db_data = await db.get(data.user_id);
await db.remove(db_data);
diff --git a/pages/login.tsx b/pages/login.tsx
@@ -1,11 +1,9 @@
import Head from "next/head";
import { NextRouter, withRouter } from "next/router";
import { Component, ReactNode } from "react";
-import { RingLoader } from "react-spinners";
import { ClientContext } from "../components/ClientContext";
import Header from "../components/Header";
import { constMatrixArtServer } from "../helpers/matrix_client";
-import { centerSpinner } from "./Home";
type Props = {
router: NextRouter;
diff --git a/pages/post/[id].tsx b/pages/post/[id].tsx
@@ -53,6 +53,7 @@ class Post extends Component<Props, State> {
try {
let serverUrl = constMatrixArtServer + "/_matrix/client";
await this.context.client?.registerAsGuest(serverUrl);
+ await this.context.guest_client?.registerAsGuest(serverUrl);
} catch (err) {
console.error("Failed to register as guest:", err);
}
@@ -83,13 +84,13 @@ class Post extends Component<Props, State> {
try {
const profile = await this.context.client.getProfile(image_event[0].sender);
this.setState({
- image_event: image_event[0],
+ image_event: image_event[0] as MatrixImageEvents,
displayname: profile.displayname,
});
} catch (ex) {
console.debug(`Failed to fetch profile for user ${image_event[0].sender}:`, ex);
this.setState({
- image_event: image_event[0],
+ image_event: image_event[0] as MatrixImageEvents,
displayname: image_event[0].sender,
});
}
@@ -163,7 +164,7 @@ class Post extends Component<Props, State> {
<div className="grow bg-[#f8f8f8] dark:bg-[#06070D] min-h-[400px] flex flex-col items-center">
<div className="flex flex-col items-start lg:min-w-[60rem] lg:w-[60rem]">
<h1 className="my-4 text-6xl text-gray-900 dark:text-gray-200 font-bold">{post_title}</h1>
- <Link href={"/profile/" + encodeURIComponent(image_event.sender)}><h3 className="cursor-pointer mt-0 mb-4 text-l text-gray-900 dark:text-gray-200 font-normal">{this.state.displayname}</h3></Link>
+ <h3 className="cursor-pointer mt-0 mb-4 text-l text-gray-900 dark:text-gray-200 font-normal"><Link href={"/profile/" + encodeURIComponent(image_event.sender)}>{this.state.displayname}</Link></h3>
{isImageGalleryEvent(image_event) ? this.renderImageGalleryTags(image_event) : isImageEvent(image_event) ? this.renderSingleImageTags(image_event) : <div key={(image_event as MatrixEventBase).event_id + "tags"}></div>}
</div>
</div>
diff --git a/pages/profile/[userid].tsx b/pages/profile/[userid].tsx
@@ -4,6 +4,8 @@ import { NextRouter, withRouter } from "next/router";
import { Component } from "react";
import { ClientContext } from "../../components/ClientContext";
import Header from "../../components/Header";
+import { BannerEvent, MatrixEvent } from "../../helpers/event_types";
+import { constMatrixArtServer } from "../../helpers/matrix_client";
type Props = InferGetServerSidePropsType<typeof getServerSideProps> & {
router: NextRouter;
@@ -11,6 +13,11 @@ type Props = InferGetServerSidePropsType<typeof getServerSideProps> & {
type State = {
displayname: string;
+ avatar_url: string;
+ events: MatrixEvent[] | [];
+ error?: any;
+ isLoadingImages: boolean;
+ hasFullyLoaded: boolean;
};
class Profile extends Component<Props, State> {
@@ -20,42 +27,104 @@ class Profile extends Component<Props, State> {
super(props);
this.state = {
- displayname: this.props.mxid
+ displayname: this.props.mxid,
+ events: []
} as State;
}
async componentDidMount() {
- if (!this.props.mxid) {
+ // auto-register as a guest if not logged in
+ if (!this.context.client?.accessToken) {
+ this.registerAsGuest();
+ } else {
+ console.log("Already logged in");
+ if (!this.props.mxid) {
+ return;
+ }
+ try {
+ const profile = await this.context.client.getProfile(this.props.mxid);
+ this.setState({
+ displayname: profile.displayname,
+ avatar_url: profile.avatar_url,
+ });
+ } catch (ex) {
+ console.debug(`Failed to fetch profile for user ${this.props.mxid}:`, ex);
+ }
+ await this.loadEvents();
+ }
+ }
+
+ async registerAsGuest() {
+ try {
+ let serverUrl = constMatrixArtServer + "/_matrix/client";
+ await this.context.client?.registerAsGuest(serverUrl);
+ await this.context.guest_client?.registerAsGuest(serverUrl);
+ if (typeof window !== "undefined") {
+ window.location.reload();
+ }
+ } catch (err) {
+ console.error("Failed to register as guest:", err);
+ this.setState({
+ error: "Failed to register as guest: " + JSON.stringify(err),
+ });
+ }
+ }
+
+ async loadEvents() {
+ const { isLoadingImages, hasFullyLoaded } = this.state;
+ if (isLoadingImages || hasFullyLoaded) {
return;
}
+ const client = this.context.client.isGuest ? this.context.client : this.context.guest_client;
+ this.setState({
+ isLoadingImages: true,
+ });
try {
- const profile = await this.context.client.getProfile(this.props.mxid);
+ const roomId = await client?.followUser("#" + this.props.mxid);
+ await client?.getTimeline(roomId, 100, (events) => {
+ const filtered_events = events.filter(event => event.type !== "m.room.member" && event.type !== "m.room.topic" && event.type !== "m.room.name" && event.type !== "m.room.power_levels");
+ console.log("Adding ", filtered_events.length, " items");
+ this.setState({
+ events: filtered_events,
+ });
+ });
+ } catch (err) {
this.setState({
- displayname: profile.displayname,
+ error: JSON.stringify(err),
+ });
+ } finally {
+ this.setState({
+ hasFullyLoaded: true,
+ isLoadingImages: false
});
- } catch (ex) {
- console.debug(`Failed to fetch profile for user ${this.props.mxid}:`, ex);
}
}
+ renderNotFound() {
+ return (
+ <div className="h-full bg-[#fefefe]/[.95] dark:bg-[#14181E]/[.95]">
+ <Head>
+ <title key="title">Matrix Art | User not Found</title>
+ <meta property="og:title" content="Matrix Art | User not Found" key="og-title" />
+ <meta name="twitter:card" content="summary_large_image" key="og-twitter" />
+ <meta name="twitter:title" content="Matrix Art | User not Found" key="og-twitter-title" />
+ <meta property="og:type" content="website" key="og-type" />
+ </Head>
+ <Header></Header>
+ <main className='h-full lg:pt-[108px] pt-[216px] z-0 flex items-center justify-center'>
+ <h1 className="text-6xl text-gray-900 dark:text-gray-200 font-bold">The User you wanted does not exist!</h1>
+ </main>
+ </div>
+ );
+ }
+
render() {
if (!this.props.mxid || !this.props.mxid?.startsWith("@")) {
- return (
- <div className="h-full bg-[#fefefe]/[.95] dark:bg-[#14181E]/[.95]">
- <Head>
- <title key="title">Matrix Art | User not Found</title>
- <meta property="og:title" content="Matrix Art | User not Found" key="og-title" />
- <meta name="twitter:card" content="summary_large_image" key="og-twitter" />
- <meta name="twitter:title" content="Matrix Art | User not Found" key="og-twitter-title" />
- <meta property="og:type" content="website" key="og-type" />
- </Head>
- <Header></Header>
- <main className='h-full lg:pt-[108px] pt-[216px] z-0 flex items-center justify-center'>
- <h1 className="text-6xl text-gray-900 dark:text-gray-200 font-bold">The User you wanted does not exist!</h1>
- </main>
- </div>
- );
+ return this.renderNotFound();
}
+
+ const banner_event = this.state.events.filter(event => event.type === "matrixart.profile_banner")[0];
+ const avatar_url = this.state.avatar_url;
return (
<div className="h-full bg-[#f8f8f8] dark:bg-[#06070D]">
<Head>
@@ -67,9 +136,29 @@ class Profile extends Component<Props, State> {
</Head>
<Header></Header>
<main className='lg:pt-[108px] pt-[216px] z-0'>
- <div className='z-[100] sticky lg:top-[108px] top-[216px] bg-[#fefefe]/[.95] dark:bg-[#14181E]/[.95]'>
- <div className='h-[72px] px-10 w-full relative grid grid-cols-[1fr_auto_1fr] items-center' id='section-grid'>
- <h1 className='text-xl text-gray-900 dark:text-gray-200 font-bold'>{"User – " + this.state.displayname}</h1>
+ {banner_event ? <div style={{
+ backgroundImage: `url(${this.context.client.downloadLink((banner_event as BannerEvent).content["m.file"].url)})`
+ }}
+ className="fixed top-[54px] w-full h-[520px] bg-cover bg-[position:50%]"
+ ></div> : null}
+ <div className="relative">
+ <div className="relative mb-0 min-h-[700px]">
+ <div id="transparent_gradient" className="absolute left-0 right-0 top-[626px] bottom-0"></div>
+ <div className="px-[60px] relative pt-[250px]">
+ <div className="relative mt-0">
+ <div className="relative inline-flex z-[1] mb-[40px]">
+ <span>
+ <div className="block relative">
+ {/* TODO fallback*/}
+ <img className="block object-cover rounded-[4px]" src={avatar_url} alt={this.state.displayname} title={this.state.displayname} />
+ </div>
+ </span>
+ <div className="ml-[20px] flex flex-col justify-center">
+ <h1 className="text-bold text-5xl text-gray-900 dark:text-gray-200 mt-[-17px] flex items-[flex-end]">{this.state.displayname}</h1>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
</div>
</main>
@@ -90,7 +179,6 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
);
if (mxid && mxid.startsWith("@")) {
try {
-
return {
props: {
mxid: mxid
@@ -104,4 +192,4 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
};
-export default withRouter(Profile);
-\ No newline at end of file
+export default withRouter(Profile);;
+\ No newline at end of file
diff --git a/styles/globals.css b/styles/globals.css
@@ -5,4 +5,14 @@
html, body, #__next {
margin: 0;
@apply h-full bg-[#f8f8f8] dark:bg-[#06070D];
+}
+
+#transparent_gradient:after {
+ background-image: linear-gradient(180deg,rgba(6,7,13,0) 0,#06070D 70%,#06070D);
+ content: "";
+ display: block;
+ position: absolute;
+ width: 100%;
+ height: 500px;
+ transform: translateY(-100%);
}
\ No newline at end of file