commit
700c9c890f
@ -28,7 +28,7 @@
|
||||
|
||||
<div class="header__download-box--description">
|
||||
A FLOSS multi-account Mastodon and Pleroma desktop client<br />
|
||||
Now available in Beta (v0.7.0)<br />
|
||||
Now available in Beta (v0.8.0)<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
@ -43,9 +43,9 @@
|
||||
<br />
|
||||
|
||||
<h4 class="header__download-box--subtitle">Or download the desktop client:</h4>
|
||||
<a href="https://github.com/NicolasConstant/sengi/releases/download/0.7.0/Sengi-0.7.0-win.exe" class="download-button" title="download client for windows"><i class="fab fa-windows"></i></a>
|
||||
<a href="https://github.com/NicolasConstant/sengi/releases/download/0.7.0/Sengi-0.7.0-mac.dmg" class="download-button" title="download client for mac"><i class="fab fa-apple"></i></a>
|
||||
<a href="https://github.com/NicolasConstant/sengi/releases/download/0.7.0/Sengi-0.7.0-linux.deb" class="download-button" title="download client for debian-based distrib"><i class="fab fa-ubuntu"></i></a>
|
||||
<a href="https://github.com/NicolasConstant/sengi/releases/download/0.8.0/Sengi-0.8.0-win.exe" class="download-button" title="download client for windows"><i class="fab fa-windows"></i></a>
|
||||
<a href="https://github.com/NicolasConstant/sengi/releases/download/0.8.0/Sengi-0.8.0-mac.dmg" class="download-button" title="download client for mac"><i class="fab fa-apple"></i></a>
|
||||
<a href="https://github.com/NicolasConstant/sengi/releases/download/0.8.0/Sengi-0.8.0-linux.deb" class="download-button" title="download client for debian-based distrib"><i class="fab fa-ubuntu"></i></a>
|
||||
<a href="https://snapcraft.io/sengi" title="use Snap Store for linux"><img src="images/snap-store-white.png" /></a>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -28,7 +28,8 @@ function createWindow() {
|
||||
{ role: "reload" },
|
||||
{ role: "forcereload" },
|
||||
{ type: "separator" },
|
||||
{ role: "close" }
|
||||
{ role: "close" },
|
||||
{ role: 'quit' }
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -68,7 +69,8 @@ function createWindow() {
|
||||
{ role: "delete" },
|
||||
{ role: "selectall" },
|
||||
{ type: "separator" },
|
||||
{ role: "close" }
|
||||
{ role: "close" },
|
||||
{ role: 'quit' }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sengi",
|
||||
"version": "0.7.2",
|
||||
"version": "0.8.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "main-electron.js",
|
||||
"description": "A multi-account desktop client for Mastodon and Pleroma",
|
||||
|
@ -57,6 +57,7 @@ import { MentionsComponent } from './components/floating-column/manage-account/m
|
||||
import { NotificationsComponent } from './components/floating-column/manage-account/notifications/notifications.component';
|
||||
import { SettingsState } from './states/settings.state';
|
||||
import { AccountEmojiPipe } from './pipes/account-emoji.pipe';
|
||||
import { CardComponent } from './components/stream/status/card/card.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: "", redirectTo: "home", pathMatch: "full" },
|
||||
@ -102,7 +103,8 @@ const routes: Routes = [
|
||||
DirectMessagesComponent,
|
||||
MentionsComponent,
|
||||
NotificationsComponent,
|
||||
AccountEmojiPipe
|
||||
AccountEmojiPipe,
|
||||
CardComponent
|
||||
],
|
||||
imports: [
|
||||
FontAwesomeModule,
|
||||
|
@ -54,7 +54,7 @@ export class AddNewAccountComponent implements OnInit {
|
||||
return Promise.resolve(instanceApps[0].app);
|
||||
} else {
|
||||
const redirect_uri = this.getLocalHostname() + '/register';
|
||||
return this.authService.createNewApplication(instance, 'Sengi', redirect_uri, 'read write follow', 'https://github.com/NicolasConstant/sengi')
|
||||
return this.authService.createNewApplication(instance, 'Sengi', redirect_uri, 'read write follow', 'https://nicolasconstant.github.io/sengi/')
|
||||
.then((appData: AppData) => {
|
||||
return this.saveNewApp(instance, appData)
|
||||
.then(() => { return appData; });
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="panel">
|
||||
<h3 class="panel__title">settings</h3>
|
||||
|
||||
<p class="version">Sengi version: 0.7.2</p>
|
||||
<p class="version">Sengi version: 0.8.0</p>
|
||||
</div>
|
||||
|
||||
|
@ -1,7 +1,16 @@
|
||||
<div class="media-viewer-canvas" (click)="close()">
|
||||
<button class="media-viewer-canvas__close" title="close">
|
||||
<button class="media-viewer-canvas__close media-viewer-canvas__button" title="close">
|
||||
<fa-icon [icon]="faTimes"></fa-icon>
|
||||
</button>
|
||||
|
||||
<button class="media-viewer-canvas__previous media-viewer-canvas__button" title="previous" (click)="previous($event)" *ngIf="previousAvailable">
|
||||
<fa-icon [icon]="faAngleLeft"></fa-icon>
|
||||
</button>
|
||||
|
||||
<button class="media-viewer-canvas__next media-viewer-canvas__button" title="next" (click)="next($event)" *ngIf="nextAvailable">
|
||||
<fa-icon [icon]="faAngleRight"></fa-icon>
|
||||
</button>
|
||||
|
||||
<img class="media-viewer-canvas__image" *ngIf="imageUrl" src="{{imageUrl}}" (click)="blockClick($event)"/>
|
||||
<video class="media-viewer-canvas__image" *ngIf="gifvUrl" role="application" loop autoplay (click)="blockClick($event)">
|
||||
<source src="{{ gifvUrl }}" type="video/mp4">
|
||||
@ -9,4 +18,7 @@
|
||||
<video class="media-viewer-canvas__image" *ngIf="videoUrl" role="application" loop controls="controls" (click)="blockClick($event)">
|
||||
<source src="{{ videoUrl }}" type="video/mp4">
|
||||
</video>
|
||||
|
||||
<div #video *ngIf="html" class="media-viewer-canvas__image media-viewer-canvas__iframe" [innerHTML]="html">
|
||||
</div>
|
||||
</div>
|
@ -8,23 +8,76 @@
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&__close {
|
||||
&__button {
|
||||
@include clearButton;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&__close {
|
||||
top: 15px;
|
||||
right: 20px;
|
||||
font-size: 24px;
|
||||
color: $font-link-primary;
|
||||
color: $font-link-primary;
|
||||
|
||||
&:hover {
|
||||
color: $font-link-primary-hover;
|
||||
}
|
||||
}
|
||||
|
||||
$browsing-icon-size: 30px;
|
||||
$browsing-icon-top-position: calc(50vh - #{$browsing-icon-size/2});
|
||||
$browsing-icon-bottom-position-mini: 0px;
|
||||
$screen-break: 800px;
|
||||
|
||||
&__previous {
|
||||
@media screen and (min-width: $screen-break) {
|
||||
top: $browsing-icon-top-position;
|
||||
left: 5px;
|
||||
}
|
||||
@media screen and (max-width: $screen-break) {
|
||||
bottom: $browsing-icon-bottom-position-mini;
|
||||
left: 40vw;
|
||||
}
|
||||
|
||||
font-size: $browsing-icon-size;
|
||||
color: whitesmoke;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
&__next {
|
||||
@media screen and (min-width: 800px) {
|
||||
top: $browsing-icon-top-position;
|
||||
right: 5px;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
bottom: $browsing-icon-bottom-position-mini;
|
||||
right: 40vw;
|
||||
}
|
||||
|
||||
font-size: $browsing-icon-size;
|
||||
color: whitesmoke;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
&__image {
|
||||
max-width: 95%;
|
||||
@media screen and (min-width: $screen-break) {
|
||||
max-width: 85%;
|
||||
}
|
||||
@media screen and (max-width: $screen-break) {
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
max-height: calc(100% - 120px);
|
||||
margin-top: 50vh;
|
||||
margin-left: 50vw;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
&__iframe {
|
||||
height: 60vw;
|
||||
width: 95vw;
|
||||
max-height: 600px;
|
||||
max-width: 950px;
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
import { Component, OnInit, Input, Output } from '@angular/core';
|
||||
import { faChevronLeft, faChevronRight, faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||
import { Component, OnInit, Input, Output, ElementRef, ViewChild, HostListener } from '@angular/core';
|
||||
import { SafeHtml } from '@angular/platform-browser';
|
||||
import { faTimes, faAngleLeft, faAngleRight } from "@fortawesome/free-solid-svg-icons";
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { OpenMediaEvent } from '../../models/common.model';
|
||||
import { Attachment } from '../../services/models/mastodon.interfaces';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-media-viewer',
|
||||
templateUrl: './media-viewer.component.html',
|
||||
@ -12,27 +14,51 @@ import { Attachment } from '../../services/models/mastodon.interfaces';
|
||||
})
|
||||
export class MediaViewerComponent implements OnInit {
|
||||
private _mediaEvent: OpenMediaEvent;
|
||||
|
||||
faChevronLeft = faChevronLeft;
|
||||
faChevronRight = faChevronRight;
|
||||
faTimes = faTimes;
|
||||
faAngleLeft = faAngleLeft;
|
||||
faAngleRight = faAngleRight;
|
||||
|
||||
imageUrl: string;
|
||||
gifvUrl: string;
|
||||
videoUrl: string;
|
||||
|
||||
html: SafeHtml;
|
||||
|
||||
previousAvailable: boolean;
|
||||
nextAvailable: boolean;
|
||||
private currentIndex: number;
|
||||
|
||||
@Input('openedMediaEvent')
|
||||
set openedMediaEvent(value: OpenMediaEvent) {
|
||||
this._mediaEvent = value;
|
||||
|
||||
const attachment = value.attachments[value.selectedIndex];
|
||||
this.loadAttachment(attachment);
|
||||
if (value.iframe) {
|
||||
this.html = value.iframe;
|
||||
this.autoplayIframe();
|
||||
} else {
|
||||
const attachment = value.attachments[value.selectedIndex];
|
||||
this.currentIndex = value.selectedIndex;
|
||||
this.loadAttachment(attachment);
|
||||
this.setBrowsing();
|
||||
}
|
||||
}
|
||||
get openedMediaEvent(): OpenMediaEvent {
|
||||
return this._mediaEvent;
|
||||
}
|
||||
|
||||
@Output() closeSubject = new Subject<boolean>();
|
||||
@ViewChild('video') myVideo: ElementRef;
|
||||
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
handleKeyboardEvent(event: KeyboardEvent) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (event.key === 'ArrowRight') {
|
||||
this.next(event);
|
||||
} else if (event.key === 'ArrowLeft') {
|
||||
this.previous(event);
|
||||
}
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
|
||||
@ -42,20 +68,72 @@ export class MediaViewerComponent implements OnInit {
|
||||
private loadAttachment(attachment: Attachment) {
|
||||
if (attachment.type === 'image') {
|
||||
this.imageUrl = attachment.url;
|
||||
} else if (attachment.type === 'gifv'){
|
||||
} else if (attachment.type === 'gifv') {
|
||||
this.gifvUrl = attachment.url;
|
||||
} else if (attachment.type === 'video'){
|
||||
} else if (attachment.type === 'video') {
|
||||
this.videoUrl = attachment.url;
|
||||
}
|
||||
}
|
||||
|
||||
private setBrowsing() {
|
||||
var index = this.currentIndex;
|
||||
var attachments = this.openedMediaEvent.attachments;
|
||||
|
||||
console.log(`index ${index}`);
|
||||
console.log(`attachments.length ${attachments.length}`);
|
||||
|
||||
if (index < attachments.length - 1) {
|
||||
this.nextAvailable = true;
|
||||
} else {
|
||||
this.nextAvailable = false;
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
this.previousAvailable = true;
|
||||
} else {
|
||||
this.previousAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
private autoplayIframe(): any {
|
||||
setTimeout(() => {
|
||||
if (this.myVideo.nativeElement.childNodes[0].src.includes('?')) {
|
||||
this.myVideo.nativeElement.childNodes[0].src += '&autoplay=1&auto_play=1';
|
||||
} else {
|
||||
this.myVideo.nativeElement.childNodes[0].src += '?autoplay=1&auto_play=1';
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
close(): boolean {
|
||||
this.closeSubject.next(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
blockClick(event: any): boolean{
|
||||
blockClick(event: any): boolean {
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
previous(event): boolean {
|
||||
event.stopPropagation();
|
||||
if (this.currentIndex <= 0) return false;
|
||||
|
||||
this.currentIndex--;
|
||||
this.imageUrl = this.openedMediaEvent.attachments[this.currentIndex].url;
|
||||
this.setBrowsing();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
next(event): boolean {
|
||||
event.stopPropagation();
|
||||
if (this.currentIndex >= this.openedMediaEvent.attachments.length - 1) return false;
|
||||
|
||||
this.currentIndex++;
|
||||
this.imageUrl = this.openedMediaEvent.attachments[this.currentIndex].url;
|
||||
this.setBrowsing();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ export class AttachementsComponent implements OnInit {
|
||||
}
|
||||
|
||||
attachmentSelected(index: number): boolean {
|
||||
let openMediaEvent = new OpenMediaEvent(index, this.attachments);
|
||||
let openMediaEvent = new OpenMediaEvent(index, this.attachments, null);
|
||||
this.navigationService.openMedia(openMediaEvent);
|
||||
return false;
|
||||
}
|
||||
|
38
src/app/components/stream/status/card/card.component.html
Normal file
38
src/app/components/stream/status/card/card.component.html
Normal file
@ -0,0 +1,38 @@
|
||||
<div class="card" *ngIf="card.type === 'link' || card.type === 'video'">
|
||||
<a *ngIf="card.type === 'link'" class="card__link" href="{{ card.url }}" target="_blank" title="{{ card.title }}">
|
||||
<img *ngIf="card.image" class="card__link--image" src="{{ card.image }}" alt="" />
|
||||
<div *ngIf="!card.image" class="card__link--image">
|
||||
<fa-icon class="card__link--image--logo" [icon]="faFileAlt"></fa-icon>
|
||||
</div>
|
||||
|
||||
<h3 class="card__link--title">{{ card.title }}</h3>
|
||||
<span class="card__link--host">{{ host }}</span>
|
||||
</a>
|
||||
<div *ngIf="card.type === 'photo'" class="card__photo">
|
||||
|
||||
</div>
|
||||
<div *ngIf="card.type === 'video'" class="card__video">
|
||||
<div *ngIf="!showHtml" class="card__video--preview">
|
||||
<div class="card__video--preview--controls">
|
||||
<a href class="card__video--preview--link" (click)="play()" title="play">
|
||||
<fa-icon [icon]="faPlay"></fa-icon>
|
||||
</a>
|
||||
<a href class="card__video--preview--link" (click)="expand()" title="open">
|
||||
<fa-icon [icon]="faExpand"></fa-icon>
|
||||
</a>
|
||||
<a href="{{ card.url }}" class="card__video--preview--link" target="_blank" title="browse">
|
||||
<fa-icon [icon]="faExternalLinkAlt"></fa-icon>
|
||||
</a>
|
||||
</div>
|
||||
<img src="{{ card.image }}" class="card__video--preview--image" />
|
||||
</div>
|
||||
<div #video *ngIf="showHtml" class="card__video--content" [innerHTML]="html">
|
||||
|
||||
</div>
|
||||
<!-- {{ card.html }} -->
|
||||
<!-- <div [innerHTML]="html">
|
||||
|
||||
</div> -->
|
||||
</div>
|
||||
<div *ngIf="card.type === 'rich'" class="card__rich" [innerHTML]="html"></div>
|
||||
</div>
|
111
src/app/components/stream/status/card/card.component.scss
Normal file
111
src/app/components/stream/status/card/card.component.scss
Normal file
@ -0,0 +1,111 @@
|
||||
@import "variables";
|
||||
// @import "mixins";
|
||||
|
||||
.card {
|
||||
border: 1px solid $card-border-color;
|
||||
background-color: $column-background;
|
||||
border-radius: 3px;
|
||||
transition: all .2s;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($column-background, 5);
|
||||
}
|
||||
|
||||
&__link {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: whitesmoke;
|
||||
|
||||
&--image {
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
float: left;
|
||||
object-fit: cover;
|
||||
border-right: 1px solid $card-border-color;
|
||||
margin-right: 10px;
|
||||
|
||||
&--logo {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
color: lighten($card-border-color, 5);
|
||||
margin: 9px 0 0 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&--title {
|
||||
margin: 7px 0 0 0;
|
||||
padding-right: 5px;
|
||||
font-size: 1em;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&--host {
|
||||
margin: 11px 0 0 0px;
|
||||
font-size: 0.8em;
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
&__photo {
|
||||
|
||||
}
|
||||
|
||||
&__video {
|
||||
// border-radius: 3px;
|
||||
|
||||
&--preview {
|
||||
position: relative;
|
||||
|
||||
&--controls {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 65px;
|
||||
// outline: 2px solid greenyellow;
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
z-index: 10;
|
||||
border-radius: 5px;
|
||||
background-color: rgba($color: #000000, $alpha: .85);
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
&--link {
|
||||
transition: all .2s;
|
||||
display: block;
|
||||
color: whitesmoke;
|
||||
font-size: 16px;
|
||||
margin: 12px 5px 0 5px;
|
||||
float: left;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
&--image {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
// border: 1px solid salmon;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
&--content {
|
||||
//width: 300px;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
&__rich {
|
||||
|
||||
|
||||
}
|
||||
}
|
25
src/app/components/stream/status/card/card.component.spec.ts
Normal file
25
src/app/components/stream/status/card/card.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CardComponent } from './card.component';
|
||||
|
||||
describe('CardComponent', () => {
|
||||
let component: CardComponent;
|
||||
let fixture: ComponentFixture<CardComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
68
src/app/components/stream/status/card/card.component.ts
Normal file
68
src/app/components/stream/status/card/card.component.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { faPlay, faExpand, faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faFileAlt } from "@fortawesome/free-regular-svg-icons";
|
||||
|
||||
import { Card } from '../../../../services/models/mastodon.interfaces';
|
||||
import { NavigationService } from '../../../../services/navigation.service';
|
||||
import { OpenMediaEvent } from '../../../../models/common.model';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-card',
|
||||
templateUrl: './card.component.html',
|
||||
styleUrls: ['./card.component.scss']
|
||||
})
|
||||
export class CardComponent implements OnInit {
|
||||
faPlay = faPlay;
|
||||
faExpand = faExpand;
|
||||
faFileAlt = faFileAlt;
|
||||
faExternalLinkAlt = faExternalLinkAlt;
|
||||
|
||||
@Input() card: Card;
|
||||
@ViewChild('video') myVideo: ElementRef;
|
||||
|
||||
host: string;
|
||||
html: SafeHtml;
|
||||
showHtml: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly navigationService: NavigationService,
|
||||
private readonly sanitizer: DomSanitizer) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.host = this.card.url.replace('https://', '').replace('http://', '').split('/')[0];
|
||||
}
|
||||
|
||||
private sanitize(card: Card): SafeHtml{
|
||||
if(!card.html) return null;
|
||||
|
||||
let html = card.html.replace(`width="${card.width}"`, '');
|
||||
html = html.replace(`height="${card.height}"`, '');
|
||||
html = html.replace(`<iframe `, '<iframe allow="autoplay" style="width:100%; height:100%;"');
|
||||
return this.sanitizer.bypassSecurityTrustHtml(html);
|
||||
}
|
||||
|
||||
play(): boolean {
|
||||
this.html = this.sanitize(this.card);
|
||||
this.showHtml = true;
|
||||
|
||||
setTimeout(() => {
|
||||
if(this.myVideo.nativeElement.childNodes[0].src.includes('?')){
|
||||
this.myVideo.nativeElement.childNodes[0].src+='&autoplay=1&auto_play=1';
|
||||
} else {
|
||||
this.myVideo.nativeElement.childNodes[0].src+='?autoplay=1&auto_play=1';
|
||||
}
|
||||
}, 500);
|
||||
return false;
|
||||
}
|
||||
|
||||
expand(): boolean {
|
||||
this.html = this.sanitize(this.card);
|
||||
|
||||
const openMedia = new OpenMediaEvent(null, null, this.html);
|
||||
this.navigationService.openMedia(openMedia);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,12 +1,15 @@
|
||||
<div class="reblog" *ngIf="reblog">
|
||||
<a class="reblog__profile-link" href (click)="openAccount(status.account)"><span innerHTML="{{ status.account | accountEmoji }}"></span> <img *ngIf="reblog" class="reblog__avatar" src="{{ status.account.avatar }}" /></a> boosted
|
||||
<a class="reblog__profile-link" href (click)="openAccount(status.account)"><span
|
||||
innerHTML="{{ status.account | accountEmoji }}"></span> <img *ngIf="reblog" class="reblog__avatar"
|
||||
src="{{ status.account.avatar }}" /></a> boosted
|
||||
</div>
|
||||
<div *ngIf="notificationType === 'favourite'">
|
||||
<div class="notification--icon">
|
||||
<fa-icon class="favorite" [icon]="faStar"></fa-icon>
|
||||
</div>
|
||||
<div class="notification--label">
|
||||
<a href class="notification--link" (click)="openAccount(notificationAccount)" innerHTML="{{ notificationAccount | accountEmoji }}"></a> favorited your status
|
||||
<a href class="notification--link" (click)="openAccount(notificationAccount)"
|
||||
innerHTML="{{ notificationAccount | accountEmoji }}"></a> favorited your status
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="notificationType === 'reblog'">
|
||||
@ -14,7 +17,8 @@
|
||||
<fa-icon class="boost" [icon]="faRetweet"></fa-icon>
|
||||
</div>
|
||||
<div class="notification--label">
|
||||
<a href class="notification--link" (click)="openAccount(notificationAccount)" innerHTML="{{ notificationAccount | accountEmoji }}"></a> boosted your status
|
||||
<a href class="notification--link" (click)="openAccount(notificationAccount)"
|
||||
innerHTML="{{ notificationAccount | accountEmoji }}"></a> boosted your status
|
||||
</div>
|
||||
</div>
|
||||
<div class="status">
|
||||
@ -32,7 +36,7 @@
|
||||
</span>
|
||||
</a>
|
||||
<div class="status__created-at" title="{{ displayedStatus.created_at | date: 'full' }}">
|
||||
<a class="status__created-at--link" href="{{ displayedStatus.url }}" target="_blank">
|
||||
<a href class="status__created-at--link" (click)="textSelected()" (auxclick)="openUrl()">
|
||||
{{ status.created_at | timeAgo | async }}
|
||||
</a>
|
||||
</div>
|
||||
@ -47,7 +51,7 @@
|
||||
<div class="status__labels--label status__labels--thread" title="thread" *ngIf="isThread">
|
||||
thread
|
||||
</div>
|
||||
<div class="status__labels--label status__labels--discuss" title="this status has a discution"
|
||||
<div class="status__labels--label status__labels--discuss" title="this status has a discussion"
|
||||
*ngIf="hasReply">
|
||||
replies
|
||||
</div>
|
||||
@ -62,6 +66,9 @@
|
||||
<app-databinded-text class="status__content" *ngIf="!isContentWarned" [text]="statusContent"
|
||||
(accountSelected)="accountSelected($event)" (hashtagSelected)="hashtagSelected($event)"
|
||||
(textSelected)="textSelected()"></app-databinded-text>
|
||||
|
||||
<app-card class="status__card" *ngIf="displayedStatus.card && !hasAttachments" [card]="displayedStatus.card"></app-card>
|
||||
|
||||
<app-attachements *ngIf="!isContentWarned && hasAttachments" class="attachments"
|
||||
[attachments]="displayedStatus.media_attachments">
|
||||
</app-attachements>
|
||||
@ -69,6 +76,6 @@
|
||||
<app-action-bar #appActionBar [statusWrapper]="statusWrapper" (cwIsActiveEvent)="changeCw($event)"
|
||||
(replyEvent)="openReply()"></app-action-bar>
|
||||
</div>
|
||||
<app-create-status *ngIf="replyingToStatus" [statusReplyingToWrapper]="statusWrapper"
|
||||
(onClose)="closeReply()"></app-create-status>
|
||||
<app-create-status *ngIf="replyingToStatus" [statusReplyingToWrapper]="statusWrapper" (onClose)="closeReply()">
|
||||
</app-create-status>
|
||||
</div>
|
@ -111,6 +111,11 @@
|
||||
margin: 0 10px 0 $avatar-column-space;
|
||||
display: block;
|
||||
}
|
||||
&__card {
|
||||
word-wrap: break-word;
|
||||
margin: 10px 10px 0 $avatar-column-space;
|
||||
display: block;
|
||||
}
|
||||
&__content-warning {
|
||||
min-height: 80px;
|
||||
display: block; // border: 1px solid greenyellow;
|
||||
|
@ -47,6 +47,7 @@ export class StatusComponent implements OnInit {
|
||||
@Input('statusWrapper')
|
||||
set statusWrapper(value: StatusWrapper) {
|
||||
this._statusWrapper = value;
|
||||
// console.warn(value.status);
|
||||
this.status = value.status;
|
||||
|
||||
if (this.status.reblog) {
|
||||
@ -148,7 +149,7 @@ export class StatusComponent implements OnInit {
|
||||
this.browseHashtagEvent.next(hashtag);
|
||||
}
|
||||
|
||||
textSelected(): void {
|
||||
textSelected(): boolean {
|
||||
const status = this._statusWrapper.status;
|
||||
const accountInfo = this._statusWrapper.provider;
|
||||
|
||||
@ -160,5 +161,12 @@ export class StatusComponent implements OnInit {
|
||||
}
|
||||
|
||||
this.browseThreadEvent.next(openThread);
|
||||
return false;
|
||||
}
|
||||
|
||||
openUrl(): boolean {
|
||||
event.preventDefault();
|
||||
window.open(this.displayedStatus.url, "_blank");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -23,10 +23,10 @@ $header-height: 160px;
|
||||
}
|
||||
&__avatar {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
width: 80px;
|
||||
border-radius: 50%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
&__display-name {
|
||||
position: absolute;
|
||||
|
@ -1,10 +1,14 @@
|
||||
import { SafeHtml } from '@angular/platform-browser';
|
||||
|
||||
import { Attachment, Status } from "../services/models/mastodon.interfaces";
|
||||
import { AccountInfo } from '../states/accounts.state';
|
||||
|
||||
|
||||
export class OpenMediaEvent {
|
||||
constructor(
|
||||
public selectedIndex: number,
|
||||
public attachments: Attachment[]) {
|
||||
public attachments: Attachment[],
|
||||
public iframe: SafeHtml) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,14 @@ export interface Card {
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
type: 'link' | 'photo' | 'video' | 'rich';
|
||||
author_name: string;
|
||||
author_url: string;
|
||||
provider_name: string;
|
||||
provider_url: string;
|
||||
html: any;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface Context {
|
||||
@ -165,6 +173,7 @@ export interface Status {
|
||||
emojis: Emoji[];
|
||||
language: string;
|
||||
pinned: boolean;
|
||||
card: Card;
|
||||
|
||||
pleroma: PleromaStatusInfo;
|
||||
}
|
||||
|
@ -50,4 +50,6 @@ $button-background-color-hover: lighten($color-primary, 20);
|
||||
|
||||
|
||||
|
||||
$column-background: #0f111a;
|
||||
$column-background: #0f111a;
|
||||
|
||||
$card-border-color: #1e2435;
|
Loading…
x
Reference in New Issue
Block a user