commit bca8e2b93a005018fb5b7579cfe0a8857b4f1b4b
parent 0c33d4cdcc79b3f5b93dce1719ad5d3dc66782a3
Author: MTRNord <mtrnord1@gmail.com>
Date: Mon, 3 Jan 2022 21:56:54 +0100
Calculate the displayname of posters
Diffstat:
4 files changed, 158 insertions(+), 75 deletions(-)
diff --git a/components/FrontPageImage.tsx b/components/FrontPageImage.tsx
@@ -0,0 +1,129 @@
+import Link from "next/link";
+import { Component } from "react";
+import { ImageEvent, ImageGalleryEvent, MatrixEventBase, MatrixImageEvents } from "../helpers/event_types";
+import { constMatrixArtServer } from "../helpers/matrix_client";
+import { ClientContext } from "./ClientContext";
+
+type Props = {
+ event: MatrixImageEvents;
+};
+
+type State = {
+ displayname: string;
+ error: any;
+};
+
+export default class FrontPageImage extends Component<Props, State> {
+ declare context: React.ContextType<typeof ClientContext>;
+
+ constructor(props: Props | Readonly<Props>) {
+ super(props);
+
+ this.state = {
+ displayname: this.props.event.sender
+ } as State;
+ }
+
+ async componentDidMount() {
+ // 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.event) {
+ return;
+ }
+ try {
+ const profile = await this.context.client.getProfile(this.props.event.sender);
+ this.setState({
+ displayname: profile.displayname,
+ });
+ } catch (ex) {
+ console.debug(`Failed to fetch profile for user ${this.props.event.sender}:`, ex);
+ }
+ }
+
+ }
+
+ async registerAsGuest() {
+ try {
+ let serverUrl = constMatrixArtServer + "/_matrix/client";
+ await this.context.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),
+ });
+ }
+ }
+
+ render() {
+ const event = this.props.event;
+ return (
+ <>
+ {isImageGalleryEvent(event) ? this.render_gallery(event) : isImageEvent(event) ? this.render_image(event) : <div key={(event as MatrixEventBase).event_id}></div>}
+ </>
+ );
+ }
+
+
+ render_gallery(event: ImageGalleryEvent) {
+ const caption = event.content['m.caption'].filter((cap) => {
+ const possible_html_caption = (cap as { body: string; mimetype: string; });
+ return possible_html_caption.body !== undefined && possible_html_caption.mimetype === "text/html";
+ });
+ let caption_text = "";
+ if (caption.length != 0) {
+ caption_text = (caption[0] as { body: string; mimetype: string; }).body;
+ }
+ return event.content['m.image_gallery'].map(image => {
+ return this.render_image_box(image['m.thumbnail'][0].url, event.event_id + image['m.file'].url, event.event_id, caption_text);
+ });
+ }
+
+
+ render_image(event: ImageEvent) {
+ const caption = event.content['m.caption'].filter((cap) => {
+ const possible_html_caption = (cap as { body: string; mimetype: string; });
+ return possible_html_caption.body !== undefined && possible_html_caption.mimetype === "text/html";
+ });
+ let caption_text = "";
+ if (caption.length != 0) {
+ caption_text = (caption[0] as { body: string; mimetype: string; }).body;
+ }
+ return this.render_image_box(event.content['m.thumbnail'][0].url, event.event_id, event.event_id, caption_text);
+ }
+
+ render_image_box(thumbnail_url: string, id: string, post_id: string, caption: string) {
+ // TODO show creators display name instead of mxid and show avatar image
+ // TODO proper alt text
+ const direct_link = `/post/${encodeURIComponent(post_id)}`;
+ return (
+ <li className='h-[270px]' key={id}>
+ <Link href={direct_link} passHref>
+ <div className='relative h-[270px] cursor-pointer'>
+ <img alt={caption} title={caption} className='relative max-w-full h-[270px] object-cover align-bottom z-0' src={this.context.client?.thumbnailLink(thumbnail_url, "scale", 270, 270)}></img>
+ <div className="flex-col max-w-full h-[270px] opacity-0 hover:opacity-100 duration-300 absolute bg-gradient-to-b from-transparent to-black/[.25] inset-0 z-10 flex justify-end items-start text-white p-4">
+ <h2 className='truncate max-w-full text-base font-semibold'>{caption}</h2>
+ <p className='truncate max-w-full text-sm'>{this.state.displayname}</p>
+ </div>
+ </div>
+ </Link>
+ </li>
+ );
+ }
+}
+FrontPageImage.contextType = ClientContext;
+
+// TODO also render the edits properly later on
+export function isImageGalleryEvent(event: MatrixImageEvents): event is ImageGalleryEvent {
+ return event.type === "m.image_gallery" && event.redacted_because === undefined;
+}
+
+
+export function isImageEvent(event: MatrixImageEvents): event is ImageEvent {
+ return event.type === "m.image" && event.redacted_because === undefined;
+};
+\ No newline at end of file
diff --git a/helpers/matrix_client.ts b/helpers/matrix_client.ts
@@ -362,7 +362,7 @@ export default class MatrixClient {
return info;
}
- async getTimeline(roomId: string, limit: number, callback: (arg0: MatrixEvent[]) => void) {
+ async getTimeline(roomId: string, limit: number, callback: (arg0: MatrixEvent[]) => void | Promise<void>) {
if (!this.accessToken) {
console.error("No access token");
return [];
@@ -386,7 +386,7 @@ export default class MatrixClient {
data.chunk.forEach((ev: any) => {
msgs.push(ev);
});
- callback(msgs);
+ Promise.resolve(callback(msgs));
seenEvents += msgs.length;
if (data.chunk.length < limit) {
break;
diff --git a/pages/Home.tsx b/pages/Home.tsx
@@ -1,13 +1,12 @@
import React, { Component } from 'react';
import { RingLoader } from 'react-spinners';
-import { ImageEvent, ImageGalleryEvent, MatrixEventBase } from '../helpers/event_types';
+import { ImageEvent, ImageGalleryEvent, MatrixEventBase, MatrixImageEvents } from '../helpers/event_types';
import Link from 'next/link';
import Head from 'next/head';
import Header from '../components/Header';
import { ClientContext } from '../components/ClientContext';
import { constMatrixArtServer } from '../helpers/matrix_client';
-
-type ImageEvents = ImageEvent | ImageGalleryEvent;
+import FrontPageImage from '../components/FrontPageImage';
type Props = {
};
@@ -19,7 +18,7 @@ type State = {
isLoadingImages: boolean;
hasFullyLoaded: boolean;
// TODO make sure we parse both extev variants properly
- image_events: ImageEvents[] | [];
+ image_events: MatrixImageEvents[] | [];
};
export const centerSpinner = `
@@ -146,7 +145,7 @@ export default class Home extends Component<Props, State>{
</div>
<div className='m-10'>
<ul className='flex flex-wrap gap-1'>
- {image_events.map(event => isImageGalleryEvent(event) ? this.render_gallery(event) : isImageEvent(event) ? this.render_image(event) : <div key={(event as MatrixEventBase).event_id}></div>)}
+ {image_events.map(event => <FrontPageImage event={event} />)}
<li className='grow-[10]'></li>
</ul>
</div>
@@ -158,63 +157,5 @@ export default class Home extends Component<Props, State>{
};
}
-
-
- render_gallery(event: ImageGalleryEvent) {
- const caption = event.content['m.caption'].filter((cap) => {
- const possible_html_caption = (cap as { body: string; mimetype: string; });
- return possible_html_caption.body !== undefined && possible_html_caption.mimetype === "text/html";
- });
- let caption_text = "";
- if (caption.length != 0) {
- caption_text = (caption[0] as { body: string; mimetype: string; }).body;
- }
- return event.content['m.image_gallery'].map(image => {
- return this.render_image_box(image['m.thumbnail'][0].url, event.event_id + image['m.file'].url, event.event_id, event.sender, caption_text);
- });
- }
-
-
- render_image(event: ImageEvent) {
- const caption = event.content['m.caption'].filter((cap) => {
- const possible_html_caption = (cap as { body: string; mimetype: string; });
- return possible_html_caption.body !== undefined && possible_html_caption.mimetype === "text/html";
- });
- let caption_text = "";
- if (caption.length != 0) {
- caption_text = (caption[0] as { body: string; mimetype: string; }).body;
- }
- return this.render_image_box(event.content['m.thumbnail'][0].url, event.event_id, event.event_id, event.sender, caption_text);
- }
-
- render_image_box(thumbnail_url: string, id: string, post_id: string, sender: string, caption: string) {
- // TODO show creators display name instead of mxid and show avatar image
- // TODO proper alt text
- const direct_link = `/post/${encodeURIComponent(post_id)}`;
- return (
- <li className='h-[270px]' key={id}>
- <Link href={direct_link} passHref>
- <div className='relative h-[270px] cursor-pointer'>
- <img alt={caption} title={caption} className='relative max-w-full h-[270px] object-cover align-bottom z-0' src={this.context.client?.thumbnailLink(thumbnail_url, "scale", 270, 270)}></img>
- <div className="flex-col max-w-full h-[270px] opacity-0 hover:opacity-100 duration-300 absolute bg-gradient-to-b from-transparent to-black/[.25] inset-0 z-10 flex justify-end items-start text-white p-4">
- <h2 className='truncate max-w-full text-base font-semibold'>{caption}</h2>
- <p className='truncate max-w-full text-sm'>{sender}</p>
- </div>
- </div>
- </Link>
- </li>
- );
- }
-
-}
-Home.contextType = ClientContext;
-
-// TODO also render the edits properly later on
-export function isImageGalleryEvent(event: ImageEvents): event is ImageGalleryEvent {
- return event.type === "m.image_gallery" && event.redacted_because === undefined;
}
-
-
-export function isImageEvent(event: ImageEvents): event is ImageEvent {
- return event.type === "m.image" && event.redacted_because === undefined;
-};
-\ No newline at end of file
+Home.contextType = ClientContext;
+\ No newline at end of file
diff --git a/pages/post/[id].tsx b/pages/post/[id].tsx
@@ -14,7 +14,8 @@ import Header from "../../components/Header";
import { ImageEvent, ImageGalleryEvent, MatrixEventBase, MatrixImageEvents } from "../../helpers/event_types";
import { constMatrixArtServer } from "../../helpers/matrix_client";
import { get_data } from "../api/directory";
-import { isImageEvent, isImageGalleryEvent } from "../Home";
+import { isImageEvent, isImageGalleryEvent } from '../../components/FrontPageImage';
+import Link from 'next/link';
const centerSpinner = `
position: fixed;
@@ -30,7 +31,8 @@ type Props = InferGetServerSidePropsType<typeof getServerSideProps> & {
type State = {
hasFullyLoaded: boolean;
isLoadingImages: boolean;
- image_event: MatrixImageEvents;
+ image_event?: MatrixImageEvents;
+ displayname?: string;
error?: any;
};
@@ -43,7 +45,7 @@ class Post extends Component<Props, State> {
this.state = {
hasFullyLoaded: false,
isLoadingImages: false
- } as State;
+ };
}
async componentDidMount() {
@@ -75,12 +77,22 @@ class Post extends Component<Props, State> {
for (let user of this.props.directory_data) {
// We dont need many events
const roomId = await this.context.client?.followUser(user.user_room);
- await this.context.client?.getTimeline(roomId, 100, (events) => {
+ await this.context.client?.getTimeline(roomId, 100, async (events) => {
// Filter events by type
const image_event = events.filter((event) => (event.type === "m.image_gallery" || event.type === "m.image") && event.event_id === event_id);
- this.setState({
- image_event: image_event[0],
- });
+ try {
+ const profile = await this.context.client.getProfile(image_event[0].sender);
+ this.setState({
+ image_event: image_event[0],
+ 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],
+ displayname: image_event[0].sender,
+ });
+ }
});
}
} catch (err) {
@@ -151,7 +163,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>
- <h3 className="my-4 text-l text-gray-900 dark:text-gray-200 font-normal">{image_event.sender}</h3>
+ <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>
{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>