Skip to content
This repository has been archived by the owner on May 16, 2019. It is now read-only.

Commit

Permalink
Render m.image events
Browse files Browse the repository at this point in the history
Adds #25. Part of #24
  • Loading branch information
turt2live committed Mar 13, 2018
1 parent b9b3be8 commit 5135990
Show file tree
Hide file tree
Showing 10 changed files with 321 additions and 6 deletions.
5 changes: 5 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ import { MediaService } from "./services/matrix/media.service";
import { TextBody_MessageEventTileComponent } from "./elements/event-tiles/message/text/text.component";
import { NoticeBody_MessageEventTileComponent } from "./elements/event-tiles/message/notice/notice.component";
import { EmoteBody_MessageEventTileComponent } from "./elements/event-tiles/message/emote/emote.component";
import { ImageBody_MessageEventTileComponent } from "./elements/event-tiles/message/image/image.component";
import { FileSizePipe } from "./pipes/file-size.pipe";

const DEFAULT_PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {};

Expand Down Expand Up @@ -99,6 +101,8 @@ const DEFAULT_PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {};
TextBody_MessageEventTileComponent,
NoticeBody_MessageEventTileComponent,
EmoteBody_MessageEventTileComponent,
ImageBody_MessageEventTileComponent,
FileSizePipe,

// Vendor
],
Expand Down Expand Up @@ -126,6 +130,7 @@ const DEFAULT_PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {};
TextBody_MessageEventTileComponent,
NoticeBody_MessageEventTileComponent,
EmoteBody_MessageEventTileComponent,
ImageBody_MessageEventTileComponent,
]
})
export class AppModule {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
@import "../../../../../style/vars";

span {
color: $event_tile_room_message_emote_color;
color: $event_tile_room_message_emote_fg_color;
}
48 changes: 48 additions & 0 deletions src/app/elements/event-tiles/message/image/image.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!--
~ Evelium - A matrix client
~ Copyright (C) 2018 Travis Ralston
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<div class="container">
<div class="img-container" [ngStyle]="style">
<div class="filename" *ngIf="!isLoading && !isError">{{ event.content.body || "image" }}</div>
<a [href]="downloadUrl" class="download-button" *ngIf="!isLoading && !isError" download="" target="_blank">
<i class="fa fa-download"></i>
</a>

<img class="img"
*ngIf="hasUrl"
[hidden]="isLoading || isError"
[src]="thumbnailUrl"
(error)="onImageError($event)"
(load)="onImageLoaded($event)"
[ngStyle]="style"
/>

<div class="img loading" *ngIf="isLoading" [ngStyle]="style">
<my-spinner></my-spinner>
</div>

<div class="img error" *ngIf="isError && !isLoading" [ngStyle]="style">
<div class="icon"><i class="fa fa-exclamation-circle"></i></div>
<div class="message">There was an error showing this image</div>
</div>

<div class="size" *ngIf="!isLoading && !isError && imageEvent.content.info && imageEvent.content.info.size > 0">
{{ imageEvent.content.info.size | fileSize:2 }}
</div>
</div>
</div>
91 changes: 91 additions & 0 deletions src/app/elements/event-tiles/message/image/image.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*!
* Evelium - A matrix client
* Copyright (C) 2018 Travis Ralston
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@import "../../../../../style/vars";

.container {
display: block;
position: relative;

.img-container {
display: block;
position: relative;

.filename {
position: absolute;
top: 0;
left: 0;
color: $event_tile_room_message_image_overlay_fg_color;
background-color: $event_tile_room_message_image_overlay_bg_color;
padding: 3.5px;
max-width: 80%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}

.download-button {
position: absolute;
top: 0;
right: 0;
padding: 3.5px 10px;
border: none;
color: $event_tile_room_message_image_overlay_fg_color;
background-color: $event_tile_room_message_image_overlay_bg_color;
}

.img {
background-color: $event_tile_room_message_image_bg_color;
border: 1px solid $event_tile_room_message_image_border_color;
border-radius: 3px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
flex-flow: wrap;
flex-direction: column;
}

.img.error {
text-align: center;

.icon {
color: $event_tile_room_message_image_error_color;
font-size: 5em;
}

.message {
padding: 20px;
color: $event_tile_content_fg_color;
}
}

.size {
position: absolute;
bottom: 0;
right: 0;
color: $event_tile_room_message_image_overlay_fg_color;
background-color: $event_tile_room_message_image_overlay_bg_color;
padding: 2px;
max-width: 50%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 0.85em;
}
}
}
110 changes: 110 additions & 0 deletions src/app/elements/event-tiles/message/image/image.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Evelium - A matrix client
* Copyright (C) 2018 Travis Ralston
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { Component } from "@angular/core";
import { EventTileComponentBase } from "../../event-tile.component.base";
import { MediaService } from "../../../../services/matrix/media.service";
import { RoomImageMessageEvent } from "../../../../models/matrix/events/room/m.room.message";

const MAX_WIDTH = 700;
const MAX_HEIGHT = 300;

@Component({
selector: "my-image-body-message-event-tile",
templateUrl: "./image.component.html",
styleUrls: ["./image.component.scss"]
})
export class ImageBody_MessageEventTileComponent extends EventTileComponentBase {

public isLoading = true;
public isError = false;

constructor(private media: MediaService) {
super();
}

public get imageEvent(): RoomImageMessageEvent {
return <RoomImageMessageEvent>this.event;
}

public get style(): any {
return {
width: this.width + "px",
height: this.height + "px",
};
}

public get mxcUrl(): string {
if (!this.imageEvent || !this.imageEvent.content || !this.imageEvent.content.url) {
return null;
}

return this.imageEvent.content.url;
}

public get hasUrl(): boolean {
const mxc = this.mxcUrl;
if (!mxc) return false;
return MediaService.isValidMxc(mxc);
}

public get thumbnailUrl(): string {
if (!this.hasUrl) return null;
return this.media.mxcToThumbnailUrl(this.mxcUrl, this.width, this.height, "crop");
}

public get downloadUrl(): string {
if (!this.hasUrl) return null;
return this.media.mxcToHttp(this.mxcUrl);
}

public get dimensions(): { width: number, height: number } {
if (!this.imageEvent || !this.imageEvent.content || !this.imageEvent.content.info) {
return {width: MAX_WIDTH, height: MAX_HEIGHT};
}

const width = this.imageEvent.content.info.w || MAX_WIDTH;
const height = this.imageEvent.content.info.h || MAX_HEIGHT;
const ratio = Math.min(MAX_WIDTH / width, MAX_HEIGHT / height);

return {
width: Math.round(width * ratio),
height: Math.round(height * ratio),
};
}

public get width(): number {
// shortcut property
return this.dimensions.width;
}

public get height(): number {
// shortcut property
return this.dimensions.height;
}

public onImageLoaded() {
this.isLoading = false;
this.isError = false;
}

public onImageError() {
this.isLoading = false;
this.isError = true;
}
}
2 changes: 2 additions & 0 deletions src/app/elements/event-tiles/message/message.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { TextBody_MessageEventTileComponent } from "./text/text.component";
import { NoticeBody_MessageEventTileComponent } from "./notice/notice.component";
import { EmoteBody_MessageEventTileComponent } from "./emote/emote.component";
import moment = require("moment");
import { ImageBody_MessageEventTileComponent } from "./image/image.component";

const MAX_MESSAGE_TIME_BREAK = 2 * 60 * 1000; // 2 minutes

Expand Down Expand Up @@ -106,6 +107,7 @@ export class MessageEventTileComponent extends EventTileComponentBase implements
'm.text': TextBody_MessageEventTileComponent,
'm.notice': NoticeBody_MessageEventTileComponent,
'm.emote': EmoteBody_MessageEventTileComponent,
'm.image': ImageBody_MessageEventTileComponent,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
@import "../../../../../style/vars";

span {
color: $event_tile_room_message_notice_color;
color: $event_tile_room_message_notice_fg_color;
opacity: 0.5;
font-weight: 100;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
@import "../../../../../style/vars";

span {
color: $event_tile_room_message_text_color;
color: $event_tile_room_message_text_fg_color;
}
54 changes: 54 additions & 0 deletions src/app/pipes/file-size.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Evelium - A matrix client
* Copyright (C) 2018 Travis Ralston
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { Pipe, PipeTransform } from '@angular/core';

/*
* Convert bytes into largest possible unit.
* Takes an precision argument that defaults to 2.
* Usage:
* bytes | fileSize:precision
* Example:
* {{ 1024 | fileSize}}
* formats to: 1 KB
*/
@Pipe({name: 'fileSize'})
export class FileSizePipe implements PipeTransform {

private units = [
'bytes',
'KB',
'MB',
'GB',
'TB',
'PB'
];

transform(bytes: number = 0, precision: number = 2): string {
if (isNaN(parseFloat(String(bytes))) || !isFinite(bytes)) return '?';

let unit = 0;

while (bytes >= 1024) {
bytes /= 1024;
unit++;
}

return bytes.toFixed(+precision) + ' ' + this.units[unit];
}
}
11 changes: 8 additions & 3 deletions src/style/vars.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ $event_tile_timestamp_fg_color: #696969;
$event_tile_content_fg_color: $primary_fg_color; // default
$event_tile_state_content_fg_color: $secondary_fg_color; // default

$event_tile_room_message_text_color: $event_tile_content_fg_color;
$event_tile_room_message_notice_color: #93d6ff;
$event_tile_room_message_emote_color: $event_tile_content_fg_color;
$event_tile_room_message_text_fg_color: $event_tile_content_fg_color;
$event_tile_room_message_notice_fg_color: #93d6ff;
$event_tile_room_message_emote_fg_color: $event_tile_content_fg_color;
$event_tile_room_message_image_bg_color: #cee1ff1f;
$event_tile_room_message_image_border_color: #222;
$event_tile_room_message_image_overlay_bg_color: #222222;
$event_tile_room_message_image_overlay_fg_color: #fff;
$event_tile_room_message_image_error_color: #ff59597a;

0 comments on commit 5135990

Please sign in to comment.