diff --git a/src/domain/session/room/timeline/tiles/SimpleTile.js b/src/domain/session/room/timeline/tiles/SimpleTile.js index ec60bbba..f29f46d6 100644 --- a/src/domain/session/room/timeline/tiles/SimpleTile.js +++ b/src/domain/session/room/timeline/tiles/SimpleTile.js @@ -46,6 +46,11 @@ export class SimpleTile extends ViewModel { get isPending() { return this._entry.isPending; } + + abortSending() { + this._entry.pendingEvent?.abort(); + } + // TilesCollection contract below setUpdateEmit(emitUpdate) { this.updateOptions({emitChange: paramName => { diff --git a/src/platform/web/ui/css/themes/element/theme.css b/src/platform/web/ui/css/themes/element/theme.css index 9961dedd..8fd72ef3 100644 --- a/src/platform/web/ui/css/themes/element/theme.css +++ b/src/platform/web/ui/css/themes/element/theme.css @@ -557,11 +557,17 @@ ul.Timeline > li.messageStatus .message-container > p { .message-container .picture { display: grid; - text-decoration: none; margin-top: 4px; width: 100%; } + +.message-container .picture > a { + text-decoration: none; + width: 100%; + display: block; +} + /* .spacer grows with an inline padding-top to the size of the image, so the timeline doesn't jump when the image loads */ .message-container .picture > * { @@ -569,24 +575,41 @@ so the timeline doesn't jump when the image loads */ grid-column: 1; } -.message-container .picture > img { +.message-container .picture img { width: 100%; height: auto; /* for IE11 to still scale even though the spacer is too tall */ align-self: start; border-radius: 4px; + display: block; } /* stretch the image (to the spacer) on platforms where we can trust the spacer to always have the correct height, otherwise the image starts with height 0 and with loading=lazy only loads when the top comes into view*/ -.hydrogen:not(.legacy) .message-container .picture > img { +.hydrogen:not(.legacy) .message-container .picture img { align-self: stretch; } +.message-container .picture > .sendStatus { + align-self: end; + justify-self: start; + font-size: 0.8em; +} + +.message-container .picture > progress { + align-self: center; + justify-self: center; + width: 75%; +} + .message-container .picture > time { align-self: end; justify-self: end; +} + +.message-container .picture > time, +.message-container .picture > .sendStatus { color: #2e2f32; display: block; padding: 2px; diff --git a/src/platform/web/ui/general/html.js b/src/platform/web/ui/general/html.js index 5ab33e86..96f52c25 100644 --- a/src/platform/web/ui/general/html.js +++ b/src/platform/web/ui/general/html.js @@ -94,7 +94,7 @@ export const TAG_NAMES = { [HTML_NS]: [ "br", "a", "ol", "ul", "li", "div", "h1", "h2", "h3", "h4", "h5", "h6", "p", "strong", "em", "span", "img", "section", "main", "article", "aside", - "pre", "button", "time", "input", "textarea", "label", "form"], + "pre", "button", "time", "input", "textarea", "label", "form", "progress"], [SVG_NS]: ["svg", "circle"] }; diff --git a/src/platform/web/ui/session/room/timeline/ImageView.js b/src/platform/web/ui/session/room/timeline/ImageView.js index 058cda45..be7bef8d 100644 --- a/src/platform/web/ui/session/room/timeline/ImageView.js +++ b/src/platform/web/ui/session/room/timeline/ImageView.js @@ -16,7 +16,6 @@ limitations under the License. import {TemplateView} from "../../../general/TemplateView.js"; import {renderMessage} from "./common.js"; -import {spinner} from "../../../common.js"; export class ImageView extends TemplateView { render(t, vm) { @@ -32,26 +31,36 @@ export class ImageView extends TemplateView { // can slow down rendering, and was bleeding through the lightbox. spacerStyle = `height: ${vm.thumbnailHeight}px`; } + const img = t.img({ + loading: "lazy", + src: vm => vm.thumbnailUrl, + alt: vm => vm.label, + title: vm => vm.label, + style: `max-width: ${vm.thumbnailWidth}px; max-height: ${vm.thumbnailHeight}px;` + }); const children = [ + vm.isPending ? img : t.a({href: vm.lightboxUrl}, img), t.div({className: "spacer", style: spacerStyle}), - t.img({ - loading: "lazy", - src: vm => vm.thumbnailUrl, - alt: vm => vm.label, - title: vm => vm.label, - style: `max-width: ${vm.thumbnailWidth}px; max-height: ${vm.thumbnailHeight}px;` - }), t.time(vm.date + " " + vm.time), ]; - if (vm.isUploading) { - const uploadStatus = t.div({className: "uploadStatus"}, [ - spinner(t), - vm => vm.uploadStatus - ]); - children.push(uploadStatus); + if (vm.isPending) { + const cancel = t.button({onClick: () => vm.abortSending(), className: "link"}, vm.i18n`Cancel`); + const sendStatus = t.div({ + className: { + sendStatus: true, + hidden: vm => !vm.sendStatus + }, + }, [vm => vm.sendStatus, " ", cancel]); + const progress = t.progress({ + min: 0, + max: 100, + value: vm => vm.uploadPercentage, + className: {hidden: vm => !vm.isUploading} + }); + children.push(sendStatus, progress); } return renderMessage(t, vm, [ - t.a({href: vm.lightboxUrl, className: "picture", style: `max-width: ${vm.thumbnailWidth}px`}, children), + t.div({className: "picture", style: `max-width: ${vm.thumbnailWidth}px`}, children), t.if(vm => vm.error, t.createTemplate((t, vm) => t.p({className: "error"}, vm.error))) ]); }