implement first draft of image lightbox
This commit is contained in:
parent
137264edcb
commit
c9147e6b9a
6 changed files with 161 additions and 6 deletions
|
@ -207,7 +207,9 @@ export class SessionViewModel extends ViewModel {
|
|||
this._lightboxViewModel = this.disposeTracked(this._lightboxViewModel);
|
||||
}
|
||||
if (eventId) {
|
||||
this._lightboxViewModel = this.track(new LightboxViewModel(this.childOptions({eventId})));
|
||||
const roomId = this.navigation.path.get("room").value;
|
||||
const room = this._sessionContainer.session.rooms.get(roomId);
|
||||
this._lightboxViewModel = this.track(new LightboxViewModel(this.childOptions({eventId, room})));
|
||||
}
|
||||
this.emitChange("lightboxViewModel");
|
||||
}
|
||||
|
|
|
@ -20,14 +20,78 @@ export class LightboxViewModel extends ViewModel {
|
|||
constructor(options) {
|
||||
super(options);
|
||||
this._eventId = options.eventId;
|
||||
this._unencryptedImageUrl = null;
|
||||
this._decryptedImage = null;
|
||||
this._closeUrl = this.urlCreator.urlUntilSegment("room");
|
||||
this._eventEntry = null;
|
||||
this._date = null;
|
||||
this._subscribeToEvent(options.room, options.eventId);
|
||||
}
|
||||
|
||||
get eventId() {
|
||||
return this._eventId;
|
||||
_subscribeToEvent(room, eventId) {
|
||||
const eventObservable = room.observeEvent(eventId);
|
||||
this.track(eventObservable.subscribe(eventEntry => {
|
||||
this._loadEvent(room, eventEntry);
|
||||
}));
|
||||
this._loadEvent(room, eventObservable.get());
|
||||
}
|
||||
|
||||
async _loadEvent(room, eventEntry) {
|
||||
if (!eventEntry) {
|
||||
return;
|
||||
}
|
||||
const {mediaRepository} = room;
|
||||
this._eventEntry = eventEntry;
|
||||
const {content} = this._eventEntry;
|
||||
this._date = this._eventEntry.timestamp ? new Date(this._eventEntry.timestamp) : null;
|
||||
if (content.url) {
|
||||
this._unencryptedImageUrl = mediaRepository.mxcUrl(content.url);
|
||||
this.emitChange("imageUrl");
|
||||
} else if (content.file) {
|
||||
this._decryptedImage = this.track(await mediaRepository.downloadEncryptedFile(content.file));
|
||||
this.emitChange("imageUrl");
|
||||
}
|
||||
}
|
||||
|
||||
get imageWidth() {
|
||||
return this._eventEntry?.content?.info?.w;
|
||||
}
|
||||
|
||||
get imageHeight() {
|
||||
return this._eventEntry?.content?.info?.h;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._eventEntry?.content?.body;
|
||||
}
|
||||
|
||||
get sender() {
|
||||
return this._eventEntry?.displayName;
|
||||
}
|
||||
|
||||
get imageUrl() {
|
||||
if (this._decryptedImage) {
|
||||
return this._decryptedImage.url;
|
||||
} else if (this._unencryptedImageUrl) {
|
||||
return this._unencryptedImageUrl;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
get date() {
|
||||
return this._date && this._date.toLocaleDateString({}, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
}
|
||||
|
||||
get time() {
|
||||
return this._date && this._date.toLocaleTimeString({}, {hour: "numeric", minute: "2-digit"});
|
||||
}
|
||||
|
||||
get closeUrl() {
|
||||
return this._closeUrl;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.platform.history.pushUrl(this.closeUrl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,6 @@ main {
|
|||
use numeric positions because named grid areas
|
||||
are not present in mobile layout */
|
||||
grid-area: 2 / 1 / 3 / 3;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
/* this should not be necessary, but chrome seems to have a bug when there are scrollbars in other grid items,
|
||||
it seems to put the scroll areas on top of the other grid items unless they have a z-index */
|
||||
z-index: 1;
|
||||
|
|
|
@ -632,3 +632,63 @@ button.link {
|
|||
color: #03B381;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lightbox {
|
||||
background-color: rgba(0,0,0,0.75);
|
||||
display: grid;
|
||||
grid-template:
|
||||
"content close" auto
|
||||
"content details" 1fr /
|
||||
1fr auto;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media (max-aspect-ratio: 1/1) {
|
||||
.lightbox {
|
||||
grid-template:
|
||||
"close" auto
|
||||
"content" 1fr
|
||||
"details" auto /
|
||||
1fr;
|
||||
}
|
||||
|
||||
.lightbox .details {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.lightbox .picture {
|
||||
grid-area: content;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.lightbox .loading {
|
||||
grid-area: content;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.lightbox .close {
|
||||
grid-area: close;
|
||||
margin-left: auto;
|
||||
background-image: url('icons/dismiss.svg');
|
||||
background-position: center;
|
||||
background-size: 16px;
|
||||
background-repeat: no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.lightbox .details {
|
||||
grid-area: details;
|
||||
padding: 12px;
|
||||
font-size: 1.5rem;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ export class LoginView extends TemplateView {
|
|||
});
|
||||
const homeserver = t.input({
|
||||
id: "homeserver",
|
||||
type: "text",
|
||||
type: "url",
|
||||
placeholder: vm.i18n`Your matrix homeserver`,
|
||||
value: vm.defaultHomeServer,
|
||||
disabled
|
||||
|
|
|
@ -15,9 +15,39 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import {TemplateView} from "../../general/TemplateView.js";
|
||||
import {spinner} from "../../common.js";
|
||||
|
||||
export class LightboxView extends TemplateView {
|
||||
render(t, vm) {
|
||||
return t.div({className: "lightbox"}, [vm.eventId, t.br(), t.a({href: vm.closeUrl}, "close")]);
|
||||
const close = t.a({href: vm.closeUrl, title: vm.i18n`Close`, className: "close"});
|
||||
const image = t.div({
|
||||
role: "img",
|
||||
"aria-label": vm => vm.name,
|
||||
title: vm => vm.name,
|
||||
className: {
|
||||
picture: true,
|
||||
hidden: vm => !vm.imageUrl,
|
||||
},
|
||||
style: vm => `background-image: url('${vm.imageUrl}'); max-width: ${vm.imageWidth}px; max-height: ${vm.imageHeight}px;`
|
||||
});
|
||||
const loading = t.div({
|
||||
className: {
|
||||
loading: true,
|
||||
hidden: vm => !!vm.imageUrl
|
||||
}
|
||||
}, [
|
||||
spinner(t),
|
||||
t.div(vm.i18n`Loading image…`)
|
||||
]);
|
||||
const details = t.div({
|
||||
className: "details"
|
||||
}, [t.strong(vm => vm.name), t.br(), "uploaded by ", t.strong(vm => vm.sender), vm => ` at ${vm.time} on ${vm.date}.`]);
|
||||
return t.div({className: "lightbox", onClick: evt => this.close(evt)}, [image, loading, details, close]);
|
||||
}
|
||||
|
||||
close(evt) {
|
||||
if (evt.target === this.root()) {
|
||||
this.value.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue