the browser-facing portion of osu!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

render spinner instead of 1px dot

bakaneko eda54ea2 b75b279e

+41 -7
+1
resources/css/bem-index.less
··· 71 71 @import "bem/beatmapset-beatmap-picker"; 72 72 @import "bem/beatmapset-cover"; 73 73 @import "bem/beatmapset-cover-admin"; 74 + @import "bem/beatmapset-discussion-image-link"; 74 75 @import "bem/beatmapset-discussion-message"; 75 76 @import "bem/beatmapset-discussions-chart"; 76 77 @import "bem/beatmapset-event";
+14
resources/css/bem/beatmapset-discussion-image-link.less
··· 1 + // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 + // See the LICENCE file in the repository root for full licence text. 3 + 4 + .beatmapset-discussion-image-link { 5 + position: relative; 6 + display: inline-flex; 7 + min-width: 1em; 8 + min-height: 1em; 9 + 10 + &__spinner { 11 + .full-size(); 12 + .center-content(); 13 + } 14 + }
+26 -7
resources/js/beatmap-discussions/image-link.tsx
··· 1 1 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 - import { action } from 'mobx'; 4 + import { Spinner } from 'components/spinner'; 5 + import { action, makeObservable, observable } from 'mobx'; 5 6 import { observer } from 'mobx-react'; 6 7 import React from 'react'; 7 8 import { ReactMarkdownProps } from 'react-markdown/lib/complex-types'; 8 9 import DiscussionsStateContext from './discussions-state-context'; 9 10 10 11 type Props = ReactMarkdownProps & React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>; 11 - 12 - const placeholder = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; 13 12 14 13 @observer 15 14 export default class ImageLink extends React.Component<Props> { 16 15 static contextType = DiscussionsStateContext; 17 16 declare context: React.ContextType<typeof DiscussionsStateContext>; 17 + @observable private loaded = false; 18 + 19 + constructor(props: Props) { 20 + super(props); 21 + 22 + makeObservable(this); 23 + } 18 24 19 25 @action 20 26 componentDidMount(): void { ··· 25 31 26 32 render() { 27 33 const url = this.props.src != null ? this.context.mediaUrls.get(this.props.src) : null; 28 - // render node mutation when url changes to trigger Layzr 34 + 29 35 if (url == null) { 30 36 return ( 31 - <img {...this.props.node.properties} src={placeholder} /> 37 + <span className='beatmapset-discussion-image-link'> 38 + {this.renderSpinner()} 39 + </span> 32 40 ); 33 41 } 34 42 43 + // TODO: render something else on fail? 35 44 return ( 36 - <a href={url} rel='nofollow noreferrer' target='_blank'> 37 - <img {...this.props.node.properties} data-normal={url} src={placeholder} /> 45 + <a className='beatmapset-discussion-image-link' href={url} rel='nofollow noreferrer' target='_blank'> 46 + {!this.loaded && this.renderSpinner()} 47 + <img {...this.props.node.properties} onLoad={this.handleOnLoad} src={url} /> 38 48 </a> 39 49 ); 50 + } 51 + 52 + @action 53 + private handleOnLoad = () => { 54 + this.loaded = true; 55 + }; 56 + 57 + private renderSpinner() { 58 + return <span className='beatmapset-discussion-image-link__spinner'><Spinner /></span>; 40 59 } 41 60 }