From e3c1472040105651fe55158b741c7ba92c1a7332 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Mon, 15 Apr 2019 22:23:05 +0200 Subject: Shift+click on column Back button to return to last pinable column --- .../flavours/glitch/features/status/components/detailed_status.js | 8 ++++++-- app/javascript/flavours/glitch/features/status/index.js | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'app/javascript/flavours/glitch/features/status') diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index e9130b1b0..69b646427 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -42,7 +42,9 @@ export default class DetailedStatus extends ImmutablePureComponent { handleAccountClick = (e) => { if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey) && this.context.router) { e.preventDefault(); - this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`); + let state = {...this.context.router.history.location.state}; + state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1; + this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`, state); } e.stopPropagation(); @@ -51,7 +53,9 @@ export default class DetailedStatus extends ImmutablePureComponent { parseClick = (e, destination) => { if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey) && this.context.router) { e.preventDefault(); - this.context.router.history.push(destination); + let state = {...this.context.router.history.location.state}; + state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1; + this.context.router.history.push(destination, state); } e.stopPropagation(); diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index 7f8f02188..a0a9b986c 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -331,7 +331,9 @@ export default class Status extends ImmutablePureComponent { } handleHotkeyOpenProfile = () => { - this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`); + let state = {...this.context.router.history.location.state}; + state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1; + this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`, state); } handleMoveUp = id => { -- cgit From f3acf8f41422e70b88371f0f6ed4a0b6d9723783 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Sat, 27 Apr 2019 19:08:38 +0200 Subject: Add hotkey for bookmarking a toot --- app/javascript/flavours/glitch/components/status.js | 6 ++++++ app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js | 4 ++++ app/javascript/flavours/glitch/features/status/index.js | 5 +++++ app/javascript/flavours/glitch/features/ui/index.js | 1 + 4 files changed, 16 insertions(+) (limited to 'app/javascript/flavours/glitch/features/status') diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index cd22fb593..b28abbc42 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -53,6 +53,7 @@ export default class Status extends ImmutablePureComponent { onReply: PropTypes.func, onFavourite: PropTypes.func, onReblog: PropTypes.func, + onBookmark: PropTypes.func, onDelete: PropTypes.func, onDirect: PropTypes.func, onMention: PropTypes.func, @@ -337,6 +338,10 @@ export default class Status extends ImmutablePureComponent { this.props.onReblog(this.props.status, e); } + handleHotkeyBookmark = e => { + this.props.onBookmark(this.props.status, e); + } + handleHotkeyMention = e => { e.preventDefault(); this.props.onMention(this.props.status.get('account'), this.context.router.history); @@ -550,6 +555,7 @@ export default class Status extends ImmutablePureComponent { moveUp: this.handleHotkeyMoveUp, moveDown: this.handleHotkeyMoveDown, toggleSpoiler: this.handleExpandedToggle, + bookmark: this.handleHotkeyBookmark, }; const computedClass = classNames('status', `status-${status.get('visibility')}`, { diff --git a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js index 75eb5653c..465754153 100644 --- a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js +++ b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js @@ -52,6 +52,10 @@ export default class KeyboardShortcuts extends ImmutablePureComponent { b + + d + + enter, o diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index a0a9b986c..6cf8b8151 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -325,6 +325,10 @@ export default class Status extends ImmutablePureComponent { this.handleReblogClick(this.props.status); } + handleHotkeyBookmark = () => { + this.handleBookmarkClick(this.props.status); + } + handleHotkeyMention = e => { e.preventDefault(); this.handleMentionClick(this.props.status); @@ -463,6 +467,7 @@ export default class Status extends ImmutablePureComponent { reply: this.handleHotkeyReply, favourite: this.handleHotkeyFavourite, boost: this.handleHotkeyBoost, + bookmark: this.handleHotkeyBookmark, mention: this.handleHotkeyMention, openProfile: this.handleHotkeyOpenProfile, toggleSpoiler: this.handleExpandedToggle, diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index dd527d528..947fdac93 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -98,6 +98,7 @@ const keyMap = { goToMuted: 'g m', goToRequests: 'g r', toggleSpoiler: 'x', + bookmark: 'd', }; @connect(mapStateToProps) -- cgit From d4c95e6a8a8b869c6f22a700c7ab37aa8a8f653a Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Mon, 29 Apr 2019 20:10:44 +0200 Subject: When selecting a toot via keyboard, ensure it is scrolled into view Fixes detailed status column --- .../flavours/glitch/features/status/index.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'app/javascript/flavours/glitch/features/status') diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index 6cf8b8151..57d70db1a 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -344,15 +344,15 @@ export default class Status extends ImmutablePureComponent { const { status, ancestorsIds, descendantsIds } = this.props; if (id === status.get('id')) { - this._selectChild(ancestorsIds.size - 1); + this._selectChild(ancestorsIds.size - 1, true); } else { let index = ancestorsIds.indexOf(id); if (index === -1) { index = descendantsIds.indexOf(id); - this._selectChild(ancestorsIds.size + index); + this._selectChild(ancestorsIds.size + index, true); } else { - this._selectChild(index - 1); + this._selectChild(index - 1, true); } } } @@ -361,23 +361,29 @@ export default class Status extends ImmutablePureComponent { const { status, ancestorsIds, descendantsIds } = this.props; if (id === status.get('id')) { - this._selectChild(ancestorsIds.size + 1); + this._selectChild(ancestorsIds.size + 1, false); } else { let index = ancestorsIds.indexOf(id); if (index === -1) { index = descendantsIds.indexOf(id); - this._selectChild(ancestorsIds.size + index + 2); + this._selectChild(ancestorsIds.size + index + 2, false); } else { - this._selectChild(index + 1); + this._selectChild(index + 1, false); } } } - _selectChild (index) { - const element = this.node.querySelectorAll('.focusable')[index]; + _selectChild (index, align_top) { + const container = this.node; + const element = container.querySelectorAll('.focusable')[index]; if (element) { + if (align_top && container.scrollTop > element.offsetTop) { + element.scrollIntoView(true); + } else if (!align_top && container.scrollTop + container.clientHeight < element.offsetTop + element.offsetHeight) { + element.scrollIntoView(false); + } element.focus(); } } -- cgit From 9f25ab9792532fff9a0b067a24cfd0c20b99644b Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Fri, 3 May 2019 20:25:57 +0200 Subject: Fix polls icon not showing in CW button in detailed statuses --- .../flavours/glitch/features/status/components/detailed_status.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'app/javascript/flavours/glitch/features/status') diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index 69b646427..8b35de0d4 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -125,6 +125,7 @@ export default class DetailedStatus extends ImmutablePureComponent { if (status.get('poll')) { media = ; + mediaIcon = 'tasks'; } else if (status.get('media_attachments').size > 0) { if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) { media = ; @@ -161,7 +162,10 @@ export default class DetailedStatus extends ImmutablePureComponent { ); mediaIcon = 'picture-o'; } - } else media = ; + } else { + media = ; + mediaIcon = 'link'; + } if (status.get('application')) { applicationLink = ยท {status.getIn(['application', 'name'])}; -- cgit From ccf4f3240ae80f4b1410d816f03d0bef33062a71 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 27 Apr 2019 03:24:09 +0200 Subject: [Glitch] Add blurhash Port front-end changes from fba96c808d25d2fc35ec63ee6745a1e55a95d707 to glitch-soc Signed-off-by: Thibaut Girka --- .../flavours/glitch/components/media_gallery.js | 107 ++++++++++++++------- .../flavours/glitch/components/status.js | 1 + .../features/report/components/status_check_box.js | 1 + .../features/status/components/detailed_status.js | 1 + .../glitch/features/ui/components/media_modal.js | 1 + .../glitch/features/ui/components/video_modal.js | 1 + .../flavours/glitch/features/video/index.js | 48 +++++++-- .../flavours/glitch/styles/components/index.scss | 44 ++++++++- .../flavours/glitch/styles/components/media.scss | 17 ++++ .../flavours/glitch/styles/components/status.scss | 9 +- 10 files changed, 179 insertions(+), 51 deletions(-) (limited to 'app/javascript/flavours/glitch/features/status') diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js index b7360bae4..d1dde45b1 100644 --- a/app/javascript/flavours/glitch/components/media_gallery.js +++ b/app/javascript/flavours/glitch/components/media_gallery.js @@ -7,6 +7,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { isIOS } from 'flavours/glitch/util/is_mobile'; import classNames from 'classnames'; import { autoPlayGif, displayMedia } from 'flavours/glitch/util/initial_state'; +import { decode } from 'blurhash'; const messages = defineMessages({ hidden: { @@ -41,6 +42,7 @@ class Item extends React.PureComponent { letterbox: PropTypes.bool, onClick: PropTypes.func.isRequired, displayWidth: PropTypes.number, + visible: PropTypes.bool.isRequired, }; static defaultProps = { @@ -49,6 +51,10 @@ class Item extends React.PureComponent { size: 1, }; + state = { + loaded: false, + }; + handleMouseEnter = (e) => { if (this.hoverToPlay()) { e.target.play(); @@ -82,8 +88,40 @@ class Item extends React.PureComponent { e.stopPropagation(); } + componentDidMount () { + if (this.props.attachment.get('blurhash')) { + this._decode(); + } + } + + componentDidUpdate (prevProps) { + if (prevProps.attachment.get('blurhash') !== this.props.attachment.get('blurhash') && this.props.attachment.get('blurhash')) { + this._decode(); + } + } + + _decode () { + const hash = this.props.attachment.get('blurhash'); + const pixels = decode(hash, 32, 32); + + if (pixels) { + const ctx = this.canvas.getContext('2d'); + const imageData = new ImageData(pixels, 32, 32); + + ctx.putImageData(imageData, 0, 0); + } + } + + setCanvasRef = c => { + this.canvas = c; + } + + handleImageLoad = () => { + this.setState({ loaded: true }); + } + render () { - const { attachment, index, size, standalone, letterbox, displayWidth } = this.props; + const { attachment, index, size, standalone, letterbox, displayWidth, visible } = this.props; let width = 50; let height = 100; @@ -136,12 +174,20 @@ class Item extends React.PureComponent { let thumbnail = ''; - if (attachment.get('type') === 'image') { + if (attachment.get('type') === 'unknown') { + return ( +
+ + + +
+ ); + } else if (attachment.get('type') === 'image') { const previewUrl = attachment.get('preview_url'); const previewWidth = attachment.getIn(['meta', 'small', 'width']); - const originalUrl = attachment.get('url'); - const originalWidth = attachment.getIn(['meta', 'original', 'width']); + const originalUrl = attachment.get('url'); + const originalWidth = attachment.getIn(['meta', 'original', 'width']); const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number'; @@ -168,6 +214,7 @@ class Item extends React.PureComponent { alt={attachment.get('description')} title={attachment.get('description')} style={{ objectPosition: letterbox ? null : `${x}% ${y}%` }} + onLoad={this.handleImageLoad} /> ); @@ -197,7 +244,8 @@ class Item extends React.PureComponent { return (
- {thumbnail} + + {visible && thumbnail}
); } @@ -257,6 +305,7 @@ export default class MediaGallery extends React.PureComponent { this.node = node; if (node && node.offsetWidth && node.offsetWidth != this.state.width) { if (this.props.cacheWidth) this.props.cacheWidth(node.offsetWidth); + this.setState({ width: node.offsetWidth, }); @@ -275,7 +324,7 @@ export default class MediaGallery extends React.PureComponent { const width = this.state.width || defaultWidth; - let children; + let children, spoilerButton; const style = {}; @@ -289,40 +338,32 @@ export default class MediaGallery extends React.PureComponent { return (
); } - if (!visible) { - let warning = ; + if (this.isStandaloneEligible()) { + children = ; + } else { + children = media.take(4).map((attachment, i) => ); + } - children = ( - ); - } else { - if (this.isStandaloneEligible()) { - children = ; - } else { - children = media.take(4).map((attachment, i) => ); - } } return (
- {visible ? ( -
- - {sensitive ? ( - - - - ) : null} -
- ) : null} +
+ {spoilerButton} + {visible && sensitive && ( + + + + )} +
{children}
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 94297260b..5f10e0c52 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -479,6 +479,7 @@ export default class Status extends ImmutablePureComponent { {Component => ( (