Merge pull request #175 from NicolasConstant/develop

0.17.0 PR
This commit is contained in:
Nicolas Constant 2019-09-29 20:43:00 -04:00 committed by GitHub
commit 03be3db8c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 456 additions and 200 deletions

View File

@ -69,6 +69,18 @@ function createWindow() {
// Create our menu entries so that we can use MAC shortcuts
Menu.setApplicationMenu(
Menu.buildFromTemplate([
{
label: "Sengi",
submenu: [
{ role: "close" },
{ role: 'quit' }
]
},
// {
// label: "File",
// submenu: [
// ]
// },
{
label: "Edit",
submenu: [
@ -80,12 +92,14 @@ function createWindow() {
{ role: "paste" },
{ role: "pasteandmatchstyle" },
{ role: "delete" },
{ role: "selectall" },
{ type: "separator" },
{ role: "close" },
{ role: 'quit' }
{ role: "selectall" }
]
},
// {
// label: "Format",
// submenu: [
// ]
// },
{
label: "View",
submenu: [
@ -102,8 +116,13 @@ function createWindow() {
{ role: 'togglefullscreen' }
]
},
// {
// label: "Window",
// submenu: [
// ]
// },
{
role: "help",
role: "Help",
submenu: [
{ role: "toggledevtools" },
{

42
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "sengi",
"version": "0.15.0",
"version": "0.16.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -857,6 +857,11 @@
"@types/jasmine": "*"
}
},
"@types/mousetrap": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.3.tgz",
"integrity": "sha512-13gmo3M2qVvjQrWNseqM3+cR6S2Ss3grbR2NZltgMq94wOwqJYQdgn8qzwDshzgXqMlSUtyPZjysImmktu22ew=="
},
"@types/node": {
"version": "8.9.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz",
@ -1189,6 +1194,15 @@
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"dev": true
},
"angular2-hotkeys": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/angular2-hotkeys/-/angular2-hotkeys-2.1.5.tgz",
"integrity": "sha512-HiAnK1pW7lns5LpxtRsdkRRb5iVa7fv8Cf69Jye6l9gI6/IyvaVDptRtsWmdIG7VAr2Ngz6Yeehkym39O/LdgA==",
"requires": {
"@types/mousetrap": "^1.6.0",
"mousetrap": "^1.6.0"
}
},
"ansi-align": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
@ -2728,6 +2742,11 @@
}
}
},
"compute-scroll-into-view": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.11.tgz",
"integrity": "sha512-uUnglJowSe0IPmWOdDtrlHXof5CTIJitfJEyITHBW6zDVOGu9Pjk5puaLM73SLcwak0L4hEjO7Td88/a6P5i7A=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -8105,6 +8124,11 @@
}
}
},
"mousetrap": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.3.tgz",
"integrity": "sha512-bd+nzwhhs9ifsUrC2tWaSgm24/oo2c83zaRyZQF06hYA6sANfsXHtnZ19AbbbDXCDzeH5nZBSQ4NvCjgD62tJA=="
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@ -10302,6 +10326,14 @@
"ajv-keywords": "^3.1.0"
}
},
"scroll-into-view-if-needed": {
"version": "2.2.20",
"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.20.tgz",
"integrity": "sha512-P9kYMrhi9f6dvWwTGpO5I3HgjSU/8Mts7xL3lkoH5xlewK7O9Obdc5WmMCzppln7bCVGNmf3qfoZXrpCeyNJXw==",
"requires": {
"compute-scroll-into-view": "1.0.11"
}
},
"scss-tokenizer": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
@ -10619,6 +10651,14 @@
"integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==",
"dev": true
},
"smooth-scroll-into-view-if-needed": {
"version": "1.1.23",
"resolved": "https://registry.npmjs.org/smooth-scroll-into-view-if-needed/-/smooth-scroll-into-view-if-needed-1.1.23.tgz",
"integrity": "sha512-52177sj5yR2novVCB+vJRCYEUkHFz2mq5UKmm5wwIWs0ZtC1sotVaTjKBsuNzBPF4nOV1NxMctyD4V/VMmivCQ==",
"requires": {
"scroll-into-view-if-needed": "2.2.20"
}
},
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "sengi",
"version": "0.16.2",
"version": "0.17.0",
"license": "AGPL-3.0-or-later",
"main": "main-electron.js",
"description": "A multi-account desktop client for Mastodon and Pleroma",
@ -45,12 +45,14 @@
"@fortawesome/free-solid-svg-icons": "^5.7.0",
"@ngxs/storage-plugin": "^3.2.0",
"@ngxs/store": "^3.2.0",
"angular2-hotkeys": "^2.1.5",
"bootstrap": "^4.1.3",
"core-js": "^2.5.4",
"emojione": "~4.5.0",
"ng-pick-datetime": "^7.0.0",
"ngx-contextmenu": "^5.2.0",
"rxjs": "^6.4.0",
"smooth-scroll-into-view-if-needed": "^1.1.23",
"tslib": "^1.9.0",
"zone.js": "^0.8.29"
},

View File

@ -16,6 +16,7 @@ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { ContextMenuModule } from 'ngx-contextmenu';
import { PickerModule } from '@ctrl/ngx-emoji-mart';
import { OwlDateTimeModule, OwlNativeDateTimeModule } from 'ng-pick-datetime';
import { HotkeyModule } from 'angular2-hotkeys';
import { AppComponent } from "./app.component";
import { LeftSideBarComponent } from "./components/left-side-bar/left-side-bar.component";
@ -159,7 +160,8 @@ const routes: Routes = [
SettingsState
]),
NgxsStoragePluginModule.forRoot(),
ContextMenuModule.forRoot()
ContextMenuModule.forRoot(),
HotkeyModule.forRoot()
],
providers: [AuthService, NavigationService, NotificationService, MastodonService, StreamingService],
bootstrap: [AppComponent],

View File

@ -148,6 +148,11 @@ $counter-width: 90px;
background-color: darken($status-editor-footer-background, 20%);
}
outline: inherit;
&:focus {
background-color: darken($status-editor-footer-background, 20%);
}
& span {
margin: 0;
padding: 0;

View File

@ -8,18 +8,18 @@
<app-waiting-animation class="waiting-icon"></app-waiting-animation>
</div>
<div class="media__loaded--hover">
<button class="media__loaded--button" title="remove" (click)="removeMedia(m)">
<a href class="media__loaded--button" title="remove" (click)="removeMedia(m)">
<fa-icon [icon]="faTimes"></fa-icon>
</button>
</a>
<input class="media__loaded--description" [(ngModel)]="m.description" autocomplete="off"
placeholder="Describe for the visually impaired" />
</div>
<img class="media__loaded--preview" src="{{m.attachment.preview_url}}" />
</div>
<div *ngIf="m.attachment !== null && m.attachment.type === 'audio'" class="audio">
<button class="audio__button" title="remove" (click)="removeMedia(m)">
<a href class="audio__button" title="remove" (click)="removeMedia(m)">
<fa-icon [icon]="faTimes"></fa-icon>
</button>
</a>
<div *ngIf="m.isMigrating">
<app-waiting-animation class="waiting-icon"></app-waiting-animation>

View File

@ -46,12 +46,11 @@
opacity: 100;
}
&--button {
@include clearButton;
&--button {
width: 10px;
height: 10px;
position: absolute;
top:5px;
top:0px;
right:8px;
color: white;
}
@ -61,13 +60,9 @@
bottom:5px;
left: 5px;
width: calc(100% - 10px);
// background: black;
// color: white;
}
&--preview {
// display: block;
width: calc(100%);
height: calc(100%);
@ -87,16 +82,12 @@
}
&__button {
@include clearButton;
display: block;
width: 10px;
height: 10px;
color: white;
float: right;
// position: absolute;
// top:5px;
// right:8px;
margin-top: 0px;
margin-right: 5px;
}
}

View File

@ -3,6 +3,7 @@ import { Subscription, Observable } from "rxjs";
import { Store } from "@ngxs/store";
import { faPlus, faCog, faSearch } from "@fortawesome/free-solid-svg-icons";
import { faCommentAlt, faCalendarAlt } from "@fortawesome/free-regular-svg-icons";
import { HotkeysService, Hotkey } from 'angular2-hotkeys';
import { AccountWrapper } from "../../models/account.models";
import { AccountInfo, SelectAccount } from "../../states/accounts.state";
@ -33,6 +34,7 @@ export class LeftSideBarComponent implements OnInit, OnDestroy {
private notificationSub: Subscription;
constructor(
private readonly hotkeysService: HotkeysService,
private readonly scheduledStatusService: ScheduledStatusService,
private readonly toolsService: ToolsService,
private readonly userNotificationServiceService: UserNotificationService,
@ -40,6 +42,63 @@ export class LeftSideBarComponent implements OnInit, OnDestroy {
private readonly store: Store) {
this.accounts$ = this.store.select(state => state.registeredaccounts.accounts);
this.hotkeysService.add(new Hotkey('n', (event: KeyboardEvent): boolean => {
this.createNewStatus();
return false;
}));
this.hotkeysService.add(new Hotkey('s', (event: KeyboardEvent): boolean => {
this.openSearch();
return false;
}));
this.hotkeysService.add(new Hotkey('a', (event: KeyboardEvent): boolean => {
this.addNewAccount();
return false;
}));
this.hotkeysService.add(new Hotkey('c', (event: KeyboardEvent): boolean => {
this.navigationService.openPanel(LeftPanelType.Closed);
return false;
}));
this.hotkeysService.add(new Hotkey('escape', (event: KeyboardEvent): boolean => {
this.navigationService.openPanel(LeftPanelType.Closed);
return false;
}));
this.hotkeysService.add(new Hotkey('ctrl+up', (event: KeyboardEvent): boolean => {
this.selectPreviousAccount();
return false;
}));
this.hotkeysService.add(new Hotkey('ctrl+down', (event: KeyboardEvent): boolean => {
this.selectNextAccount();
return false;
}));
}
private selectPreviousAccount(){
let accounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
let selectedAccount = accounts.find(x => x.isSelected);
let selectedIndex = accounts.indexOf(selectedAccount);
if(selectedIndex > 0){
let previousAccount = accounts[selectedIndex - 1];
this.store.dispatch([new SelectAccount(previousAccount)]);
}
}
private selectNextAccount(){
let accounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
let selectedAccount = accounts.find(x => x.isSelected);
let selectedIndex = accounts.indexOf(selectedAccount);
if(selectedIndex < accounts.length - 1){
let nextAccount = accounts[selectedIndex + 1];
this.store.dispatch([new SelectAccount(nextAccount)]);
}
}
ngOnInit() {
@ -100,6 +159,7 @@ export class LeftSideBarComponent implements OnInit, OnDestroy {
ngOnDestroy(): void {
this.accountSub.unsubscribe();
this.notificationSub.unsubscribe();
this.scheduledSub.unsubscribe();
}
onToogleAccountNotify(acc: AccountWrapper) {

View File

@ -1,19 +1,19 @@
<div class="media-viewer-canvas noselect">
<div class="background__close" (click)="close()"></div>
<button class="media-viewer-canvas__close media-viewer-canvas__button" title="close" (click)="close()">
<a href class="media-viewer-canvas__close media-viewer-canvas__button" title="close" (click)="close()">
<fa-icon [icon]="faTimes"></fa-icon>
</button>
</a>
<button class="media-viewer-canvas__previous media-viewer-canvas__button" title="previous"
<a href class="media-viewer-canvas__previous media-viewer-canvas__button" title="previous"
(click)="previous($event)" *ngIf="previousAvailable">
<fa-icon [icon]="faAngleLeft"></fa-icon>
</button>
</a>
<button class="media-viewer-canvas__next media-viewer-canvas__button" title="next" (click)="next($event)"
<a href class="media-viewer-canvas__next media-viewer-canvas__button" title="next" (click)="next($event)"
*ngIf="nextAvailable">
<fa-icon [icon]="faAngleRight"></fa-icon>
</button>
</a>
<div *ngFor="let att of attachments" class="media-viewer-canvas__attachement"
[class.collapsed]="currentIndex !== att.index">

View File

@ -10,7 +10,7 @@
position: relative;
&__button {
@include clearButton;
display: block;
padding: 5px;
position: absolute;
}

View File

@ -46,37 +46,30 @@
</div>
</div>
<div class="galery" *ngIf="isGifv">
<!-- <video width="480" height="320" controls="controls"> -->
<video class="galery__gifv" role="application" loop autoplay (click)="attachmentSelected(0)">
<source src="{{ attachments[0].url }}" type="video/mp4">
</video>
</div>
<div class="galery galery__hover" *ngIf="isVideo">
<!-- <video width="480" height="320" controls="controls"> -->
<video #videoPlayer class="galery__video" role="application" loop>
<source src="{{ attachments[0].url }}" type="video/mp4">
</video>
<div class="galery__play-control">
<button type="button" class="galery__control--button" (click)="onPlay()" *ngIf="!isPlaying">
<a href class="galery__control--button" (click)="onPlay()" *ngIf="!isPlaying">
<fa-icon [icon]="faPlay"></fa-icon>
</button>
<!-- <button type="button" id="play-pause" class="galery__control--button">
<fa-icon [icon]="faPause"></fa-icon>
</button> -->
</a>
</div>
<div class="galery__control">
<button type="button" class="galery__control--button" (click)="onPlay()">
<a href class="galery__control--button" (click)="onPlay()">
<fa-icon [icon]="faPause" *ngIf="isPlaying"></fa-icon>
</button>
<!-- <input type="range" id="seek-bar" class="video-control__button" value="0"> -->
<button type="button" class="galery__control--button galery__control--expand" (click)="onExpand()">
</a>
<a href class="galery__control--button galery__control--expand" (click)="onExpand()">
<fa-icon [icon]="faExpand"></fa-icon>
</button>
<!-- <input type="range" id="volume-bar" class="video-control__button" min="0" max="1" step="0.1" value="1"> -->
<button type="button" class="galery__control--button galery__control--mute" (click)="onMute()">
</a>
<a href class="galery__control--button galery__control--mute" (click)="onMute()">
<fa-icon [icon]="faVolumeUp" *ngIf="!isMuted"></fa-icon>
<fa-icon [icon]="faVolumeMute" *ngIf="isMuted"></fa-icon>
</button>
</a>
</div>
</div>
<div *ngIf="isAudio">

View File

@ -124,8 +124,8 @@
padding-top: 55px;
&--button {
@include clearButton;
color: rgb(211, 211, 211);
display: inline-block;
color: rgb(211, 211, 211);
font-size: 16px;
padding: 10px 15px;

View File

@ -64,7 +64,7 @@ export class AttachementsComponent implements OnInit {
return this.videoplayer.nativeElement;
}
onPlay() {
onPlay(): boolean {
if (!this.isPlaying) {
this.getVideo().play();
} else {
@ -72,10 +72,10 @@ export class AttachementsComponent implements OnInit {
}
this.isPlaying = !this.isPlaying;
return false;
}
onExpand() {
onExpand(): boolean {
if (!this.isMuted) {
this.onMute();
}
@ -85,11 +85,13 @@ export class AttachementsComponent implements OnInit {
}
this.attachmentSelected(0);
return false;
}
onMute() {
onMute(): boolean {
this.isMuted = !this.isMuted;
this.getVideo().muted = this.isMuted;
return false;
}
setAudioData(att: Attachment): any {

View File

@ -1,33 +1,33 @@
<div class="stream-edition">
<div class="stream-edition__settings">
<div class="stream-edition__setting">
<input [(ngModel)]="hideBoosts" (change)="settingsChanged()"
class="stream-edition__setting--checkbox" type="checkbox" id="hideBoosts"> <label for="hideBoosts" class="noselect">hide
<input [(ngModel)]="hideBoosts" (change)="settingsChanged()" class="stream-edition__setting--checkbox"
type="checkbox" id="hideBoosts"> <label for="hideBoosts" class="noselect">hide
boosts</label><br />
</div>
<div class="stream-edition__setting">
<input [(ngModel)]="hideReplies" (change)="settingsChanged()"
class="stream-edition__setting--checkbox" type="checkbox" id="hideReplies"> <label for="hideReplies" class="noselect">hide
<input [(ngModel)]="hideReplies" (change)="settingsChanged()" class="stream-edition__setting--checkbox"
type="checkbox" id="hideReplies"> <label for="hideReplies" class="noselect">hide
replies</label><br />
</div>
<div class="stream-edition__setting">
<input [(ngModel)]="hideBots" (change)="settingsChanged()"
class="stream-edition__setting--checkbox" type="checkbox" id="hideBots" > <label for="hideBots" class="noselect">hide
<input [(ngModel)]="hideBots" (change)="settingsChanged()" class="stream-edition__setting--checkbox"
type="checkbox" id="hideBots"> <label for="hideBots" class="noselect">hide
bots</label><br />
</div>
</div>
<div>
<button (click)="delete()" class="stream-edition__button stream-edition__button--delete" title="remove column">
<a href (click)="delete()" class="stream-edition__button stream-edition__button--delete" title="remove column">
<fa-icon [icon]="faTimes"></fa-icon> <span class="stream-edition__button--delete--label">remove</span>
</button>
</a>
<button (click)="moveRight()"
<a href (click)="moveRight()"
class="stream-edition__button stream-edition__button--move stream-edition__button--move--right"
title="move right">
<fa-icon [icon]="faChevronRight"></fa-icon>
</button>
<button (click)="moveLeft()" class="stream-edition__button stream-edition__button--move" title="move left">
</a>
<a href (click)="moveLeft()" class="stream-edition__button stream-edition__button--move" title="move left">
<fa-icon [icon]="faChevronLeft"></fa-icon>
</button>
</a>
</div>
</div>

View File

@ -26,9 +26,10 @@
}
&__button {
@include clearButton;
display: inline-block;
padding: 5px 10px 5px 10px;
margin: 3px 0;
color: $font-color-primary;
&--delete {
// float: right;

View File

@ -1,35 +1,34 @@
<div class="stream-overlay">
<div class="stream-overlay__header">
<button class="overlay__button overlay-close" title="close" (click)="close()">
<fa-icon [icon]="faTimes"></fa-icon>
</button>
<button class="overlay__button overlay-previous"
[ngClass]="{'overlay__button--focus': hasPreviousElements }" title="previous" (click)="previous()">
<fa-icon [icon]="faAngleLeft"></fa-icon>
</button>
<button class="overlay__button overlay-refresh" [ngClass]="{'overlay__button--focus': refreshFocused }"
title="refresh" (click)="refresh()">
<fa-icon [icon]="faRedoAlt"></fa-icon>
</button>
<a href title="return to top" class="overlay-gototop" (click)="goToTop()">
<div class="overlay">
<div class="overlay__header">
<a href class="overlay__button overlay-close" title="close" (click)="close()">
<fa-icon class="overlay-close__icon" [icon]="faTimes"></fa-icon>
</a>
<button class="overlay__button overlay-next" [ngClass]="{'overlay__button--focus': hasNextElements }"
<a href class="overlay__button overlay-previous"
[ngClass]="{'overlay__button--focus': hasPreviousElements }" title="previous" (click)="previous()">
<fa-icon class="overlay-previous__icon" [icon]="faAngleLeft"></fa-icon>
</a>
<a href class="overlay__button overlay-refresh" [ngClass]="{'overlay__button--focus': refreshFocused }"
title="refresh" (click)="refresh()">
<fa-icon class="overlay-refresh__icon" [icon]="faRedoAlt"></fa-icon>
</a>
<a href title="return to top" class="overlay-gototop" (click)="goToTop()">
</a>
<a href class="overlay__button overlay-next" [ngClass]="{'overlay__button--focus': hasNextElements }"
title="next" (click)="next()">
<fa-icon [icon]="faAngleRight"></fa-icon>
</button>
<fa-icon class="overlay-next__icon" [icon]="faAngleRight"></fa-icon>
</a>
</div>
<div *ngFor="let e of loadedElements" class="stream-overlay__content-wrapper"
[class.stream-overlay__content-wrapper--selected]="e.isVisible">
<div *ngFor="let e of loadedElements" class="overlay__content-wrapper"
[class.overlay__content-wrapper--selected]="e.isVisible">
<app-user-profile #appUserProfile *ngIf="e.type === 'account'"
[currentAccount]="e.account"
[refreshEventEmitter]="e.refreshEventEmitter"
[goToTopEventEmitter]="e.goToTopEventEmitter"
class="stream-overlay__content"
class="overlay__content"
(browseAccountEvent)="browseAccount($event)"
(browseHashtagEvent)="browseHashtag($event)"
(browseThreadEvent)="browseThread($event)"></app-user-profile>
@ -37,12 +36,12 @@
[hashtagElement]="e.hashtag"
[refreshEventEmitter]="e.refreshEventEmitter"
[goToTopEventEmitter]="e.goToTopEventEmitter"
class="stream-overlay__content"
class="overlay__content"
(browseAccountEvent)="browseAccount($event)"
(browseHashtagEvent)="browseHashtag($event)"
(browseThreadEvent)="browseThread($event)"></app-hashtag>
<app-thread #appThread *ngIf="e.type === 'thread'"
[currentThread]="e.thread" class="stream-overlay__content"
[currentThread]="e.thread" class="overlay__content"
[refreshEventEmitter]="e.refreshEventEmitter"
[goToTopEventEmitter]="e.goToTopEventEmitter"
(browseAccountEvent)="browseAccount($event)"

View File

@ -2,8 +2,7 @@
@import "mixins";
@import "commons";
$header-content-height: 40px;
.stream-overlay {
// width: $stream-column-width;
.overlay {
height: calc(100%);
background-color: $column-color;
position: relative;
@ -11,13 +10,8 @@ $header-content-height: 40px;
&__header {
width: calc(100%);
height: $header-content-height;
background-color: $column-header-background-color; // padding: 6px 10px 0 10px;
border-bottom: 1px solid #222736;
& a {
color: whitesmoke;
font-size: 0.8em;
font-weight: normal;
}
background-color: $column-header-background-color;
border-bottom: 1px solid #222736;
}
&__content-wrapper {
transition: all .2s;
@ -26,9 +20,6 @@ $header-content-height: 40px;
right: 0;
left: 0;
bottom: 0;
// outline: 1px solid greenyellow;
// background-color: salmon;
z-index: 1;
opacity: 0;
@ -51,12 +42,8 @@ $header-content-height: 40px;
border-bottom: 1px solid whitesmoke;
padding: 3px 10px 0 10px;
}
}
.overlay {
margin: 0;
&__button {
@include clearButton;
width: 25px;
height: 25px;
color: #354060;
@ -78,21 +65,35 @@ $header-content-height: 40px;
display: block;
float: left;
font-size: 18px;
& fa-icon {
&__icon {
position: relative;
left: -1px;
left: 7px;
top: -1px
}
}
&-next {
display: block;
float: left;
font-size: 18px;
&__icon {
position: relative;
left: 8px;
top: -1px
}
}
&-refresh {
display: block;
float: left;
font-size: 14px;
}
&-next {
display: block;
float: left;
font-size: 18px;
}
&__icon {
position: relative;
left: 5px;
top: 1px
}
}
&-gototop {
position: absolute;
top: 0;
@ -104,9 +105,15 @@ $header-content-height: 40px;
&-close {
display: block;
float: right;
font-size: 14px;
font-size: 13px;
color: white;
margin-right: 8px;
&__icon {
position: relative;
left: 7px;
top: 1px
}
}
}

View File

@ -1,19 +1,21 @@
<div class="stream-toots flexcroll" #statusstream (scroll)="onScroll()">
<div class="stream-toots">
<div class="stream-toots__remove-cw" *ngIf="isThread && hasContentWarnings">
<button class="stream-toots__remove-cw--button" (click)="removeCw()"
title="remove content warnings">Remove CWs</button>
</div>
<div *ngIf="displayError" class="stream-toots__error">{{displayError}}</div>
<!-- data-simplebar -->
<div class="stream-toots__status" *ngFor="let statusWrapper of statuses">
<app-status
[statusWrapper]="statusWrapper"
[isThreadDisplay]="isThread"
(browseAccountEvent)="browseAccount($event)"
(browseHashtagEvent)="browseHashtag($event)"
(browseThreadEvent)="browseThread($event)"></app-status>
<a href class="stream-toots__remove-cw--button" (click)="removeCw()" title="remove content warnings">
Remove CWs
</a>
</div>
<app-waiting-animation *ngIf="isLoading" class="waiting-icon"></app-waiting-animation>
<div class="stream-toots__content flexcroll" #statusstream (scroll)="onScroll()" tabindex="0">
<div *ngIf="displayError" class="stream-toots__error">{{displayError}}</div>
<!-- data-simplebar -->
<div class="stream-toots__status" *ngFor="let statusWrapper of statuses" #status>
<app-status
[statusWrapper]="statusWrapper" [isThreadDisplay]="isThread"
(browseAccountEvent)="browseAccount($event)" (browseHashtagEvent)="browseHashtag($event)"
(browseThreadEvent)="browseThread($event)"></app-status>
</div>
<app-waiting-animation *ngIf="isLoading" class="waiting-icon"></app-waiting-animation>
</div>
</div>

View File

@ -4,45 +4,64 @@
.stream-toots {
height: calc(100%);
width: calc(100%);
overflow: auto;
// overflow: auto;
position: relative;
&__error {
padding: 20px 20px 0 20px;
color: rgb(255, 113, 113);
}
&__content {
height: calc(100%);
overflow: auto;
outline: none;
:focus {
outline: none;
}
}
&__status:not(:last-child) {
border: solid #06070b;
border-width: 0 0 1px 0;
}
&__remove-cw {
&__remove-cw {
z-index: 100;
position: absolute;
top: 10px;
right: 20px;
padding: 5px;
// border: solid #06070b;
// border-width: 0 0 1px 0;
height: 45px;
// width: calc(100%);
// position: relative;
&--button {
@include clearButton;
&--button {
// position: absolute;
// width: calc(80%);
// margin-left: 40%;
// transform: translateX(-40%);
width: calc(100%);
padding: 5px 0;
z-index: 10;
text-align: center;
border: 3px $status-secondary-color double;
transition: all .2s;
text-decoration: none;
padding: 5px 5px;
padding: 5px 10px;
text-align: center;
border: 3px $status-secondary-color double;
border: 0;
background-color: $color-secondary;
background-color: $status-secondary-color;
// background-color: rgb(238, 238, 238);
color: white;
&:hover{
$hover-color: $status-secondary-color;
background-color: $hover-color;
color: white;
border: 3px $hover-color double;
background-color: $status-secondary-color;
background-color: rgb(238, 238, 238);
color: black;
//border: 3px $hover-color double;
}
}
}

View File

@ -1,4 +1,4 @@
import { Component, OnInit, Input, ViewChild, ElementRef, OnDestroy, EventEmitter, Output } from '@angular/core';
import { Component, OnInit, Input, ViewChild, ElementRef, OnDestroy, EventEmitter, Output, ViewChildren, QueryList } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { Observable, Subscription } from 'rxjs';
import { Store } from '@ngxs/store';
@ -79,7 +79,7 @@ export class StreamStatusesComponent implements OnInit, OnDestroy {
this.streamsSubscription = this.streams$.subscribe((streams: StreamElement[]) => {
let updatedStream = streams.find(x => x.id === this.streamElement.id);
if(!updatedStream) return;
if (!updatedStream) return;
if (this.hideBoosts !== updatedStream.hideBoosts
|| this.hideBots !== updatedStream.hideBots
@ -181,6 +181,12 @@ export class StreamStatusesComponent implements OnInit, OnDestroy {
behavior: 'smooth'
});
}, 0);
setTimeout(() => {
stream.scrollTo({
top: 0,
behavior: 'auto'
});
}, 250);
return false;
}
@ -272,6 +278,14 @@ export class StreamStatusesComponent implements OnInit, OnDestroy {
return regAccounts;
}
// @ViewChildren('status') private statusEls: QueryList<ElementRef>;
focus(): boolean {
setTimeout(() => {
var element = this.statustream.nativeElement as HTMLElement;
element.focus();
}, 0);
return false;
}
private retrieveToots(): void {
this.mastodonService.getTimeline(this.account, this._streamElement.type, null, null, this.streamingService.nbStatusPerIteration, this._streamElement.tag, this._streamElement.listId)

View File

@ -43,7 +43,7 @@
&__stream-selector {
display: block;
width: $stream-column-width;
height: $stream-header-height;
height: calc(#{$stream-header-height} - 1px);
background-color: $column-header-background-color;
text-decoration: none;
color: whitesmoke;

View File

@ -6,6 +6,7 @@ import { StreamElement, StreamTypeEnum } from "../../states/streams.state";
import { Status } from "../../services/models/mastodon.interfaces";
import { AccountInfo } from "../../states/accounts.state";
import { OpenThreadEvent } from "../../services/tools.service";
import { StreamStatusesComponent } from './stream-statuses/stream-statuses.component';
@Component({
selector: "app-stream",
@ -25,6 +26,8 @@ export class StreamComponent implements OnInit {
private _streamElement: StreamElement;
@ViewChild(StreamStatusesComponent) private streamStatusesComponent: StreamStatusesComponent;
@Input('streamElement')
set streamElement(stream: StreamElement) {
switch (stream.type) {
@ -57,6 +60,13 @@ export class StreamComponent implements OnInit {
ngOnInit() {
}
focus(): boolean {
if (!this.overlayActive) {
this.streamStatusesComponent.focus();
}
return false;
}
goToTop(): boolean {
this.goToTopSubject.next();
return false;

View File

@ -214,11 +214,12 @@ export class ThreadComponent implements OnInit, OnDestroy {
this.browseThreadEvent.next(openThreadEvent);
}
removeCw() {
removeCw(): boolean {
const statuses = this.statusChildren.toArray();
statuses.forEach(x => {
x.removeContentWarning();
});
this.hasContentWarnings = false;
return false;
}
}

View File

@ -22,18 +22,18 @@
</app-waiting-animation>
<div *ngIf="!loadingRelationShip">
<button class="profile-header__follow--button profile-header__follow--unfollowed" title="follow"
<a href class="profile-header__follow--button profile-header__follow--unfollowed" title="follow"
(click)="follow()" *ngIf="!relationship.following && !relationship.requested">
<fa-icon [icon]="faUserRegular"></fa-icon>
</button>
<button class="profile-header__follow--button profile-header__follow--followed" title="unfollow"
</a>
<a href class="profile-header__follow--button profile-header__follow--followed" title="unfollow"
(click)="unfollow()" *ngIf="relationship.following">
<fa-icon [icon]="faUserCheck"></fa-icon>
</button>
<button class="profile-header__follow--button profile-header__follow--followed" title="pending"
</a>
<a href class="profile-header__follow--button profile-header__follow--followed" title="pending"
(click)="unfollow()" *ngIf="relationship.requested">
<fa-icon [icon]="faHourglassHalf"></fa-icon>
</button>
</a>
</div>
</div>
</div>
@ -42,7 +42,7 @@
<app-waiting-animation *ngIf="isLoading" class="waiting-icon"></app-waiting-animation>
<div class="profile__moved" *ngIf="displayedAccount && displayedAccount.moved">
<span innerHTML="{{displayedAccount | accountEmoji }}"></span> has moved to <br/><a href
<span innerHTML="{{displayedAccount | accountEmoji }}"></span> has moved to <br /><a href
(click)="openMigratedAccount(displayedAccount.moved)" class="profile__moved--link"
title="open @{{displayedAccount.moved.acct }}">@{{displayedAccount.moved.acct }}</a>
</div>
@ -65,18 +65,18 @@
</app-waiting-animation>
<div *ngIf="!loadingRelationShip">
<button class="profile-header__follow--button profile-header__follow--unfollowed" title="follow"
<a href class="profile-header__follow--button profile-header__follow--unfollowed" title="follow"
(click)="follow()" *ngIf="!relationship.following && !relationship.requested">
<fa-icon [icon]="faUserRegular"></fa-icon>
</button>
<button class="profile-header__follow--button profile-header__follow--followed" title="unfollow"
</a>
<a href class="profile-header__follow--button profile-header__follow--followed" title="unfollow"
(click)="unfollow()" *ngIf="relationship.following">
<fa-icon [icon]="faUserCheck"></fa-icon>
</button>
<button class="profile-header__follow--button profile-header__follow--followed" title="pending"
</a>
<a href class="profile-header__follow--button profile-header__follow--followed" title="pending"
(click)="unfollow()" *ngIf="relationship.requested">
<fa-icon [icon]="faHourglassHalf"></fa-icon>
</button>
</a>
</div>
</div>
<div class="profile-header__state"

View File

@ -167,15 +167,26 @@ $floating-header-height: 60px;
text-rendering: optimizeLegibility;
&--button {
@include clearButton;
display: inline-block;
transition: all .2s;
color: white;
&:hover{
color: rgb(216, 216, 216);
}
}
&--unfollowed {}
&--followed {
transition: all .2s;
color: #38abff;
color: #5fbcff;
color: #85ccff;
&:hover{
color: #85ccff;
color: #38abff;
}
}
&--waiting {

View File

@ -1,5 +1,7 @@
<div class="streams-selection-footer">
<a class="stream-selection" *ngFor="let str of streams; let i=index" href (click)="onColumnSelection(i)" title="open {{getDisplayableName(str)}}">
<span class="stream-selection__column-reprensentation"></span>
</a>
<a class="stream-selection" *ngFor="let str of streams; let i=index" href (click)="onColumnSelection(i)"
title="open {{getDisplayableName(str.stream)}}">
<span class="stream-selection__column-reprensentation"
[class.stream-selection__column-reprensentation--selected]="str.isSelected"></span>
</a>
</div>

View File

@ -1,29 +1,37 @@
@import "variables";
.streams-selection-footer {
width: calc(100%);
height: $stream-selector-height;
text-align: center;
width: calc(100%);
height: $stream-selector-height;
text-align: center;
background-color: $color-secondary;
background-color: $color-secondary;
}
.stream-selection {
display: inline-block;
width: 9px;
padding: 4px 5px 0 5px;
height: $stream-selector-height;
&__column-reprensentation {
display: inline-block;
width: 5px;
height: $stream-selector-height - 8px;
background-color:$font-link-primary;
width: 9px;
padding: 4px 5px 0 5px;
height: $stream-selector-height;
}
&__column-reprensentation {
transition: all .2s;
display: inline-block;
width: 5px;
height: $stream-selector-height - 8px;
background-color: $font-link-primary;
&:hover &__column-reprensentation{
background-color:$font-link-primary-hover;
}
}
&--selected {
height: ($stream-selector-height - 8px)/1.75;
background-color: rgb(168, 168, 168);
}
}
&:hover &__column-reprensentation {
background-color: $font-link-primary-hover;
&--selected {
background-color: rgb(255, 255, 255);
}
}
}

View File

@ -1,7 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { StreamElement, StreamTypeEnum } from '../../states/streams.state';
import { Store } from '@ngxs/store';
import { HotkeysService, Hotkey } from 'angular2-hotkeys';
import { StreamElement, StreamTypeEnum } from '../../states/streams.state';
import { NavigationService } from '../../services/navigation.service';
@Component({
@ -10,22 +12,65 @@ import { NavigationService } from '../../services/navigation.service';
styleUrls: ['./streams-selection-footer.component.scss']
})
export class StreamsSelectionFooterComponent implements OnInit {
streams: StreamElement[] = [];
streams: SelectableStream[] = [];
private streams$: Observable<StreamElement[]>;
constructor(
private readonly hotkeysService: HotkeysService,
private readonly navigationService: NavigationService,
private readonly store: Store) {
this.streams$ = this.store.select(state => state.streamsstatemodel.streams);
this.hotkeysService.add(new Hotkey('ctrl+right', (event: KeyboardEvent): boolean => {
this.nextColumnSelected();
return false;
}));
this.hotkeysService.add(new Hotkey('ctrl+left', (event: KeyboardEvent): boolean => {
this.previousColumnSelected();
return false;
}));
}
ngOnInit() {
this.streams$.subscribe((streams: StreamElement[]) => {
this.streams = streams;
this.streams = streams.map(x => new SelectableStream(x));
});
}
private nextColumnSelected() {
const nbStreams = this.streams.length;
const selectedElement = this.streams.find(x => x.isSelected);
let currentSelectionIndex = 0;
if (selectedElement) {
currentSelectionIndex = this.streams.indexOf(selectedElement);
}
if(currentSelectionIndex < nbStreams - 1){
this.onColumnSelection(currentSelectionIndex + 1);
}
}
private previousColumnSelected() {
const selectedElement = this.streams.find(x => x.isSelected);
let currentSelectionIndex = 0;
if (selectedElement) {
currentSelectionIndex = this.streams.indexOf(selectedElement);
}
if(currentSelectionIndex > 0){
this.onColumnSelection(currentSelectionIndex - 1);
} else {
this.onColumnSelection(0);
}
}
onColumnSelection(index: number): boolean {
this.streams.forEach(x => x.isSelected = false);
const selectedStream = this.streams[index];
selectedStream.isSelected = true;
this.navigationService.columnSelected(index);
return false;
}
@ -52,3 +97,10 @@ export class StreamsSelectionFooterComponent implements OnInit {
return `${prefix}@${stream.instance}`;
}
}
class SelectableStream {
constructor(public readonly stream: StreamElement) {
}
isSelected: boolean;
}

View File

@ -1,9 +1,11 @@
import { Component, OnInit, OnDestroy, QueryList, ViewChildren, ElementRef } from "@angular/core";
import { Observable, Subscription } from "rxjs";
import { Select } from "@ngxs/store";
import scrollIntoView from "smooth-scroll-into-view-if-needed";
import { StreamElement } from "../../states/streams.state";
import { NavigationService } from "../../services/navigation.service";
import { StreamComponent } from '../../components/stream/stream.component';
@Component({
selector: "app-streams-main-display",
@ -12,6 +14,7 @@ import { NavigationService } from "../../services/navigation.service";
})
export class StreamsMainDisplayComponent implements OnInit, OnDestroy {
@ViewChildren(StreamComponent) private streamComponents: QueryList<StreamComponent>;
@Select(state => state.streamsstatemodel.streams) streamElements$: Observable<StreamElement[]>;
private columnSelectedSub: Subscription;
@ -33,8 +36,21 @@ export class StreamsMainDisplayComponent implements OnInit, OnDestroy {
private focusOnColumn(columnIndex: number): void {
if (columnIndex > -1) {
setTimeout(() => {
this.streamsElementRef.toArray()[columnIndex].nativeElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
});
let element = this.streamsElementRef.toArray()[columnIndex].nativeElement;
element.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
const scrolling = <Promise<any>><any>scrollIntoView(element, { behavior: 'smooth', block: 'nearest'});
scrolling
.then(() => {
this.streamComponents.toArray()[columnIndex].focus();
});
// setTimeout(() => {
// this.streamComponents.toArray()[columnIndex].focus();
// }, 500);
}, 0);
}
}
}
}

View File

@ -60,12 +60,12 @@ export class ToolsService {
}
getSelectedAccounts(): AccountInfo[] {
var regAccounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
let regAccounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
return regAccounts.filter(x => x.isSelected);
}
getAccountSettings(account: AccountInfo): AccountSettings {
var accountsSettings = <AccountSettings[]>this.store.snapshot().globalsettings.settings.accountSettings;
let accountsSettings = <AccountSettings[]>this.store.snapshot().globalsettings.settings.accountSettings;
let accountSettings = accountsSettings.find(x => x.accountId === account.id);
if (!accountSettings) {
accountSettings = new AccountSettings();

View File

@ -13,5 +13,5 @@
padding: 0;
font: inherit;
cursor: pointer;
outline: inherit;
// outline: inherit;
}