mirror of
https://github.com/NicolasConstant/sengi
synced 2025-02-02 03:26:51 +01:00
Merge pull request #151 from NicolasConstant/topic_enhance-profiles
Topic enhance profiles
This commit is contained in:
commit
7fef28ee8f
@ -67,6 +67,7 @@ import { PollComponent } from './components/stream/status/poll/poll.component';
|
|||||||
import { TimeLeftPipe } from './pipes/time-left.pipe';
|
import { TimeLeftPipe } from './pipes/time-left.pipe';
|
||||||
import { AutosuggestComponent } from './components/create-status/autosuggest/autosuggest.component';
|
import { AutosuggestComponent } from './components/create-status/autosuggest/autosuggest.component';
|
||||||
import { EmojiPickerComponent } from './components/create-status/emoji-picker/emoji-picker.component';
|
import { EmojiPickerComponent } from './components/create-status/emoji-picker/emoji-picker.component';
|
||||||
|
import { StatusUserContextMenuComponent } from './components/stream/status/action-bar/status-user-context-menu/status-user-context-menu.component';
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
@ -120,7 +121,8 @@ const routes: Routes = [
|
|||||||
PollComponent,
|
PollComponent,
|
||||||
TimeLeftPipe,
|
TimeLeftPipe,
|
||||||
AutosuggestComponent,
|
AutosuggestComponent,
|
||||||
EmojiPickerComponent
|
EmojiPickerComponent,
|
||||||
|
StatusUserContextMenuComponent
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
EmojiPickerComponent
|
EmojiPickerComponent
|
||||||
|
@ -28,7 +28,8 @@
|
|||||||
<fa-icon [icon]="faWindowCloseRegular"></fa-icon>
|
<fa-icon [icon]="faWindowCloseRegular"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href class="action-bar__link action-bar__link--more" (click)="onContextMenu($event)" title="More">
|
<app-status-user-context-menu class="action-bar__link action-bar__link--more" [statusWrapper]="statusWrapper" (browseThreadEvent)="browseThread($event)"></app-status-user-context-menu>
|
||||||
|
<!-- <a href class="action-bar__link action-bar__link--more" (click)="onContextMenu($event)" title="More">
|
||||||
<fa-icon [icon]="faEllipsisH"></fa-icon>
|
<fa-icon [icon]="faEllipsisH"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
<context-menu #contextMenu>
|
<context-menu #contextMenu>
|
||||||
@ -70,5 +71,5 @@
|
|||||||
<ng-template contextMenuItem (execute)="delete(true)" *ngIf="isOwnerSelected">
|
<ng-template contextMenuItem (execute)="delete(true)" *ngIf="isOwnerSelected">
|
||||||
Delete & re-draft
|
Delete & re-draft
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</context-menu>
|
</context-menu> -->
|
||||||
</div>
|
</div>
|
@ -1,5 +1,4 @@
|
|||||||
@import "variables";
|
@import "variables";
|
||||||
@import "context-menu";
|
|
||||||
|
|
||||||
.action-bar {
|
.action-bar {
|
||||||
// outline: 1px solid greenyellow; // height: 20px;
|
// outline: 1px solid greenyellow; // height: 20px;
|
||||||
@ -44,7 +43,7 @@
|
|||||||
|
|
||||||
&--more {
|
&--more {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -5px;
|
right: 11px;
|
||||||
// left: 155px;
|
// left: 155px;
|
||||||
bottom: -2px;
|
bottom: -2px;
|
||||||
}
|
}
|
||||||
@ -150,5 +149,4 @@
|
|||||||
-moz-animation: loadingAnimation 1s infinite;
|
-moz-animation: loadingAnimation 1s infinite;
|
||||||
-o-animation: loadingAnimation 1s infinite;
|
-o-animation: loadingAnimation 1s infinite;
|
||||||
animation: loadingAnimation 1s infinite;
|
animation: loadingAnimation 1s infinite;
|
||||||
|
|
||||||
}
|
}
|
@ -1,10 +1,9 @@
|
|||||||
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ViewChild } from '@angular/core';
|
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core';
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
import { Store } from '@ngxs/store';
|
import { Store } from '@ngxs/store';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { faWindowClose, faReply, faRetweet, faStar, faEllipsisH, faLock, faEnvelope } from "@fortawesome/free-solid-svg-icons";
|
import { faWindowClose, faReply, faRetweet, faStar, faEllipsisH, faLock, faEnvelope } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { faWindowClose as faWindowCloseRegular } from "@fortawesome/free-regular-svg-icons";
|
import { faWindowClose as faWindowCloseRegular } from "@fortawesome/free-regular-svg-icons";
|
||||||
import { ContextMenuComponent, ContextMenuService } from 'ngx-contextmenu';
|
|
||||||
|
|
||||||
import { MastodonService } from '../../../../services/mastodon.service';
|
import { MastodonService } from '../../../../services/mastodon.service';
|
||||||
import { AccountInfo } from '../../../../states/accounts.state';
|
import { AccountInfo } from '../../../../states/accounts.state';
|
||||||
@ -12,7 +11,6 @@ import { Status, Account, Results } from '../../../../services/models/mastodon.i
|
|||||||
import { ToolsService, OpenThreadEvent } from '../../../../services/tools.service';
|
import { ToolsService, OpenThreadEvent } from '../../../../services/tools.service';
|
||||||
import { NotificationService } from '../../../../services/notification.service';
|
import { NotificationService } from '../../../../services/notification.service';
|
||||||
import { StatusWrapper } from '../../../../models/common.model';
|
import { StatusWrapper } from '../../../../models/common.model';
|
||||||
import { NavigationService } from '../../../../services/navigation.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-action-bar',
|
selector: 'app-action-bar',
|
||||||
@ -29,12 +27,6 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||||||
faLock = faLock;
|
faLock = faLock;
|
||||||
faEnvelope = faEnvelope;
|
faEnvelope = faEnvelope;
|
||||||
|
|
||||||
@ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent;
|
|
||||||
public items = [
|
|
||||||
{ name: 'John', otherProperty: 'Foo' },
|
|
||||||
{ name: 'Joe', otherProperty: 'Bar' }
|
|
||||||
];
|
|
||||||
|
|
||||||
@Input() statusWrapper: StatusWrapper;
|
@Input() statusWrapper: StatusWrapper;
|
||||||
@Output() replyEvent = new EventEmitter();
|
@Output() replyEvent = new EventEmitter();
|
||||||
@Output() cwIsActiveEvent = new EventEmitter<boolean>();
|
@Output() cwIsActiveEvent = new EventEmitter<boolean>();
|
||||||
@ -51,27 +43,20 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||||||
favoriteIsLoading: boolean;
|
favoriteIsLoading: boolean;
|
||||||
boostIsLoading: boolean;
|
boostIsLoading: boolean;
|
||||||
|
|
||||||
isContentWarningActive: boolean = false;
|
isContentWarningActive: boolean = false;
|
||||||
|
|
||||||
isOwnerSelected: boolean;
|
displayedStatus: Status;
|
||||||
|
|
||||||
private isProviderSelected: boolean;
|
private isProviderSelected: boolean;
|
||||||
private selectedAccounts: AccountInfo[];
|
private selectedAccounts: AccountInfo[];
|
||||||
|
|
||||||
username: string;
|
|
||||||
displayedStatus: Status;
|
|
||||||
private fullHandle: string;
|
|
||||||
private loadedAccounts: AccountInfo[];
|
|
||||||
|
|
||||||
private favoriteStatePerAccountId: { [id: string]: boolean; } = {};
|
private favoriteStatePerAccountId: { [id: string]: boolean; } = {};
|
||||||
private bootedStatePerAccountId: { [id: string]: boolean; } = {};
|
private bootedStatePerAccountId: { [id: string]: boolean; } = {};
|
||||||
|
|
||||||
private accounts$: Observable<AccountInfo[]>;
|
private accounts$: Observable<AccountInfo[]>;
|
||||||
private accountSub: Subscription;
|
private accountSub: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly navigationService: NavigationService,
|
|
||||||
private readonly contextMenuService: ContextMenuService,
|
|
||||||
private readonly store: Store,
|
private readonly store: Store,
|
||||||
private readonly toolsService: ToolsService,
|
private readonly toolsService: ToolsService,
|
||||||
private readonly mastodonService: MastodonService,
|
private readonly mastodonService: MastodonService,
|
||||||
@ -87,12 +72,10 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||||||
if (status.reblog) {
|
if (status.reblog) {
|
||||||
this.favoriteStatePerAccountId[account.id] = status.reblog.favourited;
|
this.favoriteStatePerAccountId[account.id] = status.reblog.favourited;
|
||||||
this.bootedStatePerAccountId[account.id] = status.reblog.reblogged;
|
this.bootedStatePerAccountId[account.id] = status.reblog.reblogged;
|
||||||
this.extractHandle(status.reblog.account);
|
|
||||||
this.displayedStatus = status.reblog;
|
this.displayedStatus = status.reblog;
|
||||||
} else {
|
} else {
|
||||||
this.favoriteStatePerAccountId[account.id] = status.favourited;
|
this.favoriteStatePerAccountId[account.id] = status.favourited;
|
||||||
this.bootedStatePerAccountId[account.id] = status.reblogged;
|
this.bootedStatePerAccountId[account.id] = status.reblogged;
|
||||||
this.extractHandle(status.account);
|
|
||||||
this.displayedStatus = status;
|
this.displayedStatus = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,18 +84,10 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
||||||
this.loadedAccounts = accounts;
|
|
||||||
this.checkStatus(accounts);
|
this.checkStatus(accounts);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private extractHandle(account: Account) {
|
|
||||||
this.username = account.acct.split('@')[0];
|
|
||||||
|
|
||||||
this.fullHandle = this.toolsService.getAccountFullHandle(account);
|
|
||||||
// this.fullHandle = `@${this.fullHandle}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.accountSub.unsubscribe();
|
this.accountSub.unsubscribe();
|
||||||
}
|
}
|
||||||
@ -123,9 +98,6 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||||||
this.selectedAccounts = accounts.filter(x => x.isSelected);
|
this.selectedAccounts = accounts.filter(x => x.isSelected);
|
||||||
this.isProviderSelected = this.selectedAccounts.filter(x => x.id === provider.id).length > 0;
|
this.isProviderSelected = this.selectedAccounts.filter(x => x.id === provider.id).length > 0;
|
||||||
|
|
||||||
this.isOwnerSelected = this.selectedAccounts[0].username === this.displayedStatus.account.username
|
|
||||||
&& this.selectedAccounts[0].instance === this.displayedStatus.account.url.replace('https://', '').split('/')[0];
|
|
||||||
|
|
||||||
if (status.visibility === 'direct' || status.visibility === 'private') {
|
if (status.visibility === 'direct' || status.visibility === 'private') {
|
||||||
this.isBoostLocked = true;
|
this.isBoostLocked = true;
|
||||||
} else {
|
} else {
|
||||||
@ -246,187 +218,9 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||||||
} else {
|
} else {
|
||||||
this.isFavorited = false;
|
this.isFavorited = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onContextMenu($event: MouseEvent): void {
|
browseThread(event: OpenThreadEvent){
|
||||||
this.contextMenuService.show.next({
|
this.browseThreadEvent.next(event);
|
||||||
// Optional - if unspecified, all context menu components will open
|
|
||||||
contextMenu: this.contextMenu,
|
|
||||||
event: $event,
|
|
||||||
item: null
|
|
||||||
});
|
|
||||||
$event.preventDefault();
|
|
||||||
$event.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
expandStatus(): boolean {
|
|
||||||
const openThread = new OpenThreadEvent(this.displayedStatus, this.statusWrapper.provider);
|
|
||||||
this.browseThreadEvent.next(openThread);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
copyStatusLink(): boolean {
|
|
||||||
let selBox = document.createElement('textarea');
|
|
||||||
selBox.style.position = 'fixed';
|
|
||||||
selBox.style.left = '0';
|
|
||||||
selBox.style.top = '0';
|
|
||||||
selBox.style.opacity = '0';
|
|
||||||
selBox.value = this.displayedStatus.url;
|
|
||||||
document.body.appendChild(selBox);
|
|
||||||
selBox.focus();
|
|
||||||
selBox.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
document.body.removeChild(selBox);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mentionAccount(): boolean {
|
|
||||||
this.navigationService.replyToUser(this.fullHandle, false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dmAccount(): boolean {
|
|
||||||
this.navigationService.replyToUser(this.fullHandle, true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
muteAccount(): boolean {
|
|
||||||
this.loadedAccounts.forEach(acc => {
|
|
||||||
this.toolsService.findAccount(acc, this.fullHandle)
|
|
||||||
.then((target: Account) => {
|
|
||||||
this.mastodonService.mute(acc, target.id);
|
|
||||||
return target;
|
|
||||||
})
|
|
||||||
.then((target: Account) => {
|
|
||||||
this.notificationService.hideAccount(target);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.notificationService.notifyHttpError(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockAccount(): boolean {
|
|
||||||
this.loadedAccounts.forEach(acc => {
|
|
||||||
this.toolsService.findAccount(acc, this.fullHandle)
|
|
||||||
.then((target: Account) => {
|
|
||||||
this.mastodonService.block(acc, target.id);
|
|
||||||
return target;
|
|
||||||
})
|
|
||||||
.then((target: Account) => {
|
|
||||||
this.notificationService.hideAccount(target);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.notificationService.notifyHttpError(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
muteConversation(): boolean {
|
|
||||||
const selectedAccount = this.selectedAccounts[0];
|
|
||||||
|
|
||||||
this.getStatus(selectedAccount)
|
|
||||||
.then((status: Status) => {
|
|
||||||
return this.mastodonService.muteConversation(selectedAccount, status.id)
|
|
||||||
})
|
|
||||||
.then((status: Status) => {
|
|
||||||
this.displayedStatus.muted = status.muted;
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.notificationService.notifyHttpError(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unmuteConversation(): boolean {
|
|
||||||
const selectedAccount = this.selectedAccounts[0];
|
|
||||||
|
|
||||||
this.getStatus(selectedAccount)
|
|
||||||
.then((status: Status) => {
|
|
||||||
return this.mastodonService.unmuteConversation(selectedAccount, status.id)
|
|
||||||
})
|
|
||||||
.then((status: Status) => {
|
|
||||||
this.displayedStatus.muted = status.muted;
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.notificationService.notifyHttpError(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pinOnProfile(): boolean {
|
|
||||||
const selectedAccount = this.selectedAccounts[0];
|
|
||||||
|
|
||||||
this.getStatus(selectedAccount)
|
|
||||||
.then((status: Status) => {
|
|
||||||
return this.mastodonService.pinOnProfile(selectedAccount, status.id)
|
|
||||||
})
|
|
||||||
.then((status: Status) => {
|
|
||||||
this.displayedStatus.pinned = status.pinned;
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.notificationService.notifyHttpError(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unpinFromProfile(): boolean {
|
|
||||||
const selectedAccount = this.selectedAccounts[0];
|
|
||||||
|
|
||||||
this.getStatus(selectedAccount)
|
|
||||||
.then((status: Status) => {
|
|
||||||
return this.mastodonService.unpinFromProfile(selectedAccount, status.id)
|
|
||||||
})
|
|
||||||
.then((status: Status) => {
|
|
||||||
this.displayedStatus.pinned = status.pinned;
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.notificationService.notifyHttpError(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(redraft: boolean): boolean {
|
|
||||||
const selectedAccount = this.selectedAccounts[0];
|
|
||||||
|
|
||||||
this.getStatus(selectedAccount)
|
|
||||||
.then((status: Status) => {
|
|
||||||
return this.mastodonService.deleteStatus(selectedAccount, status.id);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
if (redraft) {
|
|
||||||
this.navigationService.redraft(this.statusWrapper)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deletedStatus = new StatusWrapper(this.displayedStatus, selectedAccount);
|
|
||||||
this.notificationService.deleteStatus(deletedStatus);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.notificationService.notifyHttpError(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getStatus(account: AccountInfo): Promise<Status> {
|
|
||||||
let statusPromise: Promise<Status> = Promise.resolve(this.statusWrapper.status);
|
|
||||||
|
|
||||||
if (account.id !== this.statusWrapper.provider.id) {
|
|
||||||
statusPromise = this.mastodonService.search(account, this.statusWrapper.status.url, true)
|
|
||||||
.then((result: Results) => {
|
|
||||||
return result.statuses[0];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return statusPromise;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
<a href class="context-menu-link" (click)="onContextMenu($event)"
|
||||||
|
[class.context-menu-link__status]="statusWrapper"
|
||||||
|
[class.context-menu-link__profile]="displayedAccount"
|
||||||
|
title="More">
|
||||||
|
<fa-icon [icon]="faEllipsisH"></fa-icon>
|
||||||
|
</a>
|
||||||
|
<context-menu #contextMenu>
|
||||||
|
<ng-template contextMenuItem (execute)="expandStatus()" *ngIf="statusWrapper">
|
||||||
|
Expand status
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="copyStatusLink()" *ngIf="statusWrapper">
|
||||||
|
Copy link to status
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem divider="true" *ngIf="statusWrapper"></ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="mentionAccount()" *ngIf="!isOwnerSelected">
|
||||||
|
Mention @{{ this.username }}
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="dmAccount()" *ngIf="!isOwnerSelected">
|
||||||
|
Direct message @{{ this.username }}
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="muteConversation()" *ngIf="statusWrapper && isOwnerSelected && !displayedStatus.muted">
|
||||||
|
Mute conversation
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="unmuteConversation()" *ngIf="statusWrapper && isOwnerSelected && displayedStatus.muted">
|
||||||
|
Unmute conversation
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem divider="true"></ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="muteAccount()" *ngIf="!isOwnerSelected">
|
||||||
|
Mute @{{ this.username }}
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="blockAccount()" *ngIf="!isOwnerSelected">
|
||||||
|
Block @{{ this.username }}
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="pinOnProfile()" *ngIf="statusWrapper && isOwnerSelected && !displayedStatus.pinned && displayedStatus.visibility === 'public'">
|
||||||
|
Pin on profile
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="unpinFromProfile()" *ngIf="statusWrapper && isOwnerSelected && displayedStatus.pinned && displayedStatus.visibility === 'public'">
|
||||||
|
Unpin from profile
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="delete(false)" *ngIf="statusWrapper && isOwnerSelected">
|
||||||
|
Delete
|
||||||
|
</ng-template>
|
||||||
|
<ng-template contextMenuItem (execute)="delete(true)" *ngIf="statusWrapper && isOwnerSelected">
|
||||||
|
Delete & re-draft
|
||||||
|
</ng-template>
|
||||||
|
</context-menu>
|
@ -0,0 +1,22 @@
|
|||||||
|
@import "variables";
|
||||||
|
@import "context-menu";
|
||||||
|
|
||||||
|
.context-menu-link {
|
||||||
|
font-size: 18px;
|
||||||
|
|
||||||
|
&__status {
|
||||||
|
color: $status-secondary-color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $status-links-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__profile {
|
||||||
|
color: whitesmoke;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(192, 192, 192);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { StatusUserContextMenuComponent } from './status-user-context-menu.component';
|
||||||
|
|
||||||
|
xdescribe('StatusUserContextMenuComponent', () => {
|
||||||
|
let component: StatusUserContextMenuComponent;
|
||||||
|
let fixture: ComponentFixture<StatusUserContextMenuComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ StatusUserContextMenuComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(StatusUserContextMenuComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,271 @@
|
|||||||
|
import { Component, OnInit, ViewChild, Output, EventEmitter, Input, OnDestroy } from '@angular/core';
|
||||||
|
import { faEllipsisH } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { ContextMenuComponent, ContextMenuService } from 'ngx-contextmenu';
|
||||||
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
import { Store } from '@ngxs/store';
|
||||||
|
|
||||||
|
import { Status, Account, Results } from '../../../../../services/models/mastodon.interfaces';
|
||||||
|
import { ToolsService, OpenThreadEvent } from '../../../../../services/tools.service';
|
||||||
|
import { StatusWrapper } from '../../../../../models/common.model';
|
||||||
|
import { NavigationService } from '../../../../../services/navigation.service';
|
||||||
|
import { AccountInfo } from '../../../../../states/accounts.state';
|
||||||
|
import { MastodonService } from '../../../../../services/mastodon.service';
|
||||||
|
import { NotificationService } from '../../../../../services/notification.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-status-user-context-menu',
|
||||||
|
templateUrl: './status-user-context-menu.component.html',
|
||||||
|
styleUrls: ['./status-user-context-menu.component.scss']
|
||||||
|
})
|
||||||
|
export class StatusUserContextMenuComponent implements OnInit, OnDestroy {
|
||||||
|
faEllipsisH = faEllipsisH;
|
||||||
|
|
||||||
|
private fullHandle: string;
|
||||||
|
private loadedAccounts: AccountInfo[];
|
||||||
|
displayedStatus: Status;
|
||||||
|
username: string;
|
||||||
|
isOwnerSelected: boolean;
|
||||||
|
|
||||||
|
@Input() statusWrapper: StatusWrapper;
|
||||||
|
@Input() displayedAccount: Account;
|
||||||
|
|
||||||
|
@Output() browseThreadEvent = new EventEmitter<OpenThreadEvent>();
|
||||||
|
|
||||||
|
@ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent;
|
||||||
|
|
||||||
|
private accounts$: Observable<AccountInfo[]>;
|
||||||
|
private accountSub: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly store: Store,
|
||||||
|
private readonly mastodonService: MastodonService,
|
||||||
|
private readonly notificationService: NotificationService,
|
||||||
|
private readonly navigationService: NavigationService,
|
||||||
|
private readonly toolsService: ToolsService,
|
||||||
|
private readonly contextMenuService: ContextMenuService) {
|
||||||
|
this.accounts$ = this.store.select(state => state.registeredaccounts.accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (this.statusWrapper) {
|
||||||
|
|
||||||
|
const status = this.statusWrapper.status;
|
||||||
|
if (status.reblog) {
|
||||||
|
this.displayedStatus = status.reblog;
|
||||||
|
} else {
|
||||||
|
this.displayedStatus = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
||||||
|
this.loadedAccounts = accounts;
|
||||||
|
this.checkStatus(accounts);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let account: Account;
|
||||||
|
if(this.statusWrapper) {
|
||||||
|
account = this.displayedStatus.account;
|
||||||
|
} else {
|
||||||
|
account = this.displayedAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.username = account.acct.split('@')[0];
|
||||||
|
this.fullHandle = this.toolsService.getAccountFullHandle(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkStatus(accounts: AccountInfo[]): void {
|
||||||
|
const selectedAccount = accounts.find(x => x.isSelected);
|
||||||
|
|
||||||
|
this.isOwnerSelected = selectedAccount.username === this.displayedStatus.account.username
|
||||||
|
&& selectedAccount.instance === this.displayedStatus.account.url.replace('https://', '').split('/')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if(this.accountSub) this.accountSub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onContextMenu($event: MouseEvent): void {
|
||||||
|
this.contextMenuService.show.next({
|
||||||
|
// Optional - if unspecified, all context menu components will open
|
||||||
|
contextMenu: this.contextMenu,
|
||||||
|
event: $event,
|
||||||
|
item: null
|
||||||
|
});
|
||||||
|
$event.preventDefault();
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
expandStatus(): boolean {
|
||||||
|
const openThread = new OpenThreadEvent(this.displayedStatus, this.statusWrapper.provider);
|
||||||
|
this.browseThreadEvent.next(openThread);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
copyStatusLink(): boolean {
|
||||||
|
let selBox = document.createElement('textarea');
|
||||||
|
selBox.style.position = 'fixed';
|
||||||
|
selBox.style.left = '0';
|
||||||
|
selBox.style.top = '0';
|
||||||
|
selBox.style.opacity = '0';
|
||||||
|
selBox.value = this.displayedStatus.url;
|
||||||
|
document.body.appendChild(selBox);
|
||||||
|
selBox.focus();
|
||||||
|
selBox.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(selBox);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mentionAccount(): boolean {
|
||||||
|
this.navigationService.replyToUser(this.fullHandle, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dmAccount(): boolean {
|
||||||
|
this.navigationService.replyToUser(this.fullHandle, true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
muteAccount(): boolean {
|
||||||
|
this.loadedAccounts.forEach(acc => {
|
||||||
|
this.toolsService.findAccount(acc, this.fullHandle)
|
||||||
|
.then((target: Account) => {
|
||||||
|
this.mastodonService.mute(acc, target.id);
|
||||||
|
return target;
|
||||||
|
})
|
||||||
|
.then((target: Account) => {
|
||||||
|
this.notificationService.hideAccount(target);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockAccount(): boolean {
|
||||||
|
this.loadedAccounts.forEach(acc => {
|
||||||
|
this.toolsService.findAccount(acc, this.fullHandle)
|
||||||
|
.then((target: Account) => {
|
||||||
|
this.mastodonService.block(acc, target.id);
|
||||||
|
return target;
|
||||||
|
})
|
||||||
|
.then((target: Account) => {
|
||||||
|
this.notificationService.hideAccount(target);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
muteConversation(): boolean {
|
||||||
|
const selectedAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
|
|
||||||
|
this.getStatus(selectedAccount)
|
||||||
|
.then((status: Status) => {
|
||||||
|
return this.mastodonService.muteConversation(selectedAccount, status.id)
|
||||||
|
})
|
||||||
|
.then((status: Status) => {
|
||||||
|
this.displayedStatus.muted = status.muted;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unmuteConversation(): boolean {
|
||||||
|
const selectedAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
|
|
||||||
|
this.getStatus(selectedAccount)
|
||||||
|
.then((status: Status) => {
|
||||||
|
return this.mastodonService.unmuteConversation(selectedAccount, status.id)
|
||||||
|
})
|
||||||
|
.then((status: Status) => {
|
||||||
|
this.displayedStatus.muted = status.muted;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pinOnProfile(): boolean {
|
||||||
|
const selectedAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
|
|
||||||
|
this.getStatus(selectedAccount)
|
||||||
|
.then((status: Status) => {
|
||||||
|
console.warn(status);
|
||||||
|
return this.mastodonService.pinOnProfile(selectedAccount, status.id)
|
||||||
|
})
|
||||||
|
.then((status: Status) => {
|
||||||
|
this.displayedStatus.pinned = status.pinned;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unpinFromProfile(): boolean {
|
||||||
|
const selectedAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
|
|
||||||
|
this.getStatus(selectedAccount)
|
||||||
|
.then((status: Status) => {
|
||||||
|
return this.mastodonService.unpinFromProfile(selectedAccount, status.id)
|
||||||
|
})
|
||||||
|
.then((status: Status) => {
|
||||||
|
this.displayedStatus.pinned = status.pinned;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(redraft: boolean): boolean {
|
||||||
|
const selectedAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
|
|
||||||
|
this.getStatus(selectedAccount)
|
||||||
|
.then((status: Status) => {
|
||||||
|
return this.mastodonService.deleteStatus(selectedAccount, status.id);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
if (redraft) {
|
||||||
|
this.navigationService.redraft(this.statusWrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletedStatus = new StatusWrapper(this.displayedStatus, selectedAccount);
|
||||||
|
this.notificationService.deleteStatus(deletedStatus);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getStatus(account: AccountInfo): Promise<Status> {
|
||||||
|
let statusPromise: Promise<Status> = Promise.resolve(this.statusWrapper.status);
|
||||||
|
|
||||||
|
if (account.id !== this.statusWrapper.provider.id) {
|
||||||
|
statusPromise = this.mastodonService.search(account, this.statusWrapper.status.url, true)
|
||||||
|
.then((result: Results) => {
|
||||||
|
return result.statuses[0];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusPromise;
|
||||||
|
}
|
||||||
|
}
|
@ -81,7 +81,7 @@
|
|||||||
*ngIf="hasReply">
|
*ngIf="hasReply">
|
||||||
replies
|
replies
|
||||||
</div>
|
</div>
|
||||||
<div class="status__labels--label status__labels--old" title="this status is old" *ngIf="isOld">
|
<div class="status__labels--label status__labels--old" title="this status is old" *ngIf="isOld && !statusWrapper.status.pinned">
|
||||||
old
|
old
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, Input } from '@angular/core';
|
import { Component, OnInit, Input, HostListener, ElementRef, Output, EventEmitter } from '@angular/core';
|
||||||
import { Store } from '@ngxs/store';
|
import { Store } from '@ngxs/store';
|
||||||
import { faChevronLeft, faChevronRight, faTimes } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronLeft, faChevronRight, faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
@ -18,21 +18,40 @@ export class StreamEditionComponent implements OnInit {
|
|||||||
hideReplies: boolean;
|
hideReplies: boolean;
|
||||||
hideBots: boolean;
|
hideBots: boolean;
|
||||||
|
|
||||||
|
private init: boolean;
|
||||||
|
|
||||||
@Input() streamElement: StreamElement;
|
@Input() streamElement: StreamElement;
|
||||||
|
|
||||||
constructor(private readonly store: Store) { }
|
@Output('closed') public closedEvent = new EventEmitter();
|
||||||
|
|
||||||
|
@HostListener('document:click', ['$event'])
|
||||||
|
clickout(event) {
|
||||||
|
if (!this.init) return;
|
||||||
|
|
||||||
|
if (!this.eRef.nativeElement.contains(event.target)) {
|
||||||
|
this.closedEvent.emit(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly store: Store,
|
||||||
|
private eRef: ElementRef) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.hideBoosts = this.streamElement.hideBoosts;
|
this.hideBoosts = this.streamElement.hideBoosts;
|
||||||
this.hideReplies = this.streamElement.hideReplies;
|
this.hideReplies = this.streamElement.hideReplies;
|
||||||
this.hideBots = this.streamElement.hideBots;
|
this.hideBots = this.streamElement.hideBots;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.init = true;
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsChanged(): boolean {
|
settingsChanged(): boolean {
|
||||||
this.streamElement.hideBoosts = this.hideBoosts;
|
this.streamElement.hideBoosts = this.hideBoosts;
|
||||||
this.streamElement.hideReplies = this.hideReplies;
|
this.streamElement.hideReplies = this.hideReplies;
|
||||||
this.streamElement.hideBots = this.hideBots;
|
this.streamElement.hideBots = this.hideBots;
|
||||||
|
|
||||||
this.store.dispatch([new UpdateStream(this.streamElement)]);
|
this.store.dispatch([new UpdateStream(this.streamElement)]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-stream-edition class="stream-edition" *ngIf="editionPanelIsOpen" [streamElement]="streamElement">
|
<app-stream-edition class="stream-edition" *ngIf="editionPanelIsOpen"
|
||||||
|
[streamElement]="streamElement"
|
||||||
|
(closed)="streamEditionClosed()">
|
||||||
</app-stream-edition>
|
</app-stream-edition>
|
||||||
|
|
||||||
<app-stream-statuses class="stream-statuses"
|
<app-stream-statuses class="stream-statuses"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@import "variables";
|
@import "variables";
|
||||||
@import "commons";
|
@import "commons";
|
||||||
$stream-header-height: 40px;
|
|
||||||
.stream-edition {
|
.stream-edition {
|
||||||
width: $stream-column-width;
|
width: $stream-column-width;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -95,5 +95,10 @@ export class StreamComponent implements OnInit {
|
|||||||
this.editionPanelIsOpen = !this.editionPanelIsOpen;
|
this.editionPanelIsOpen = !this.editionPanelIsOpen;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
streamEditionClosed(): boolean {
|
||||||
|
this.editionPanelIsOpen = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,82 +1,216 @@
|
|||||||
<div class="profile flexcroll" #statusstream (scroll)="onScroll()">
|
<div class="outer-profile">
|
||||||
<app-waiting-animation *ngIf="isLoading" class="waiting-icon"></app-waiting-animation>
|
<div class="profile flexcroll" #statusstream (scroll)="onScroll()">
|
||||||
|
<div *ngIf="!isLoading" class="profile__floating-header"
|
||||||
|
[ngStyle]="{'background-image':'url('+displayedAccount.header+')'}"
|
||||||
|
[class.profile__floating-header__activated]="showFloatingHeader">
|
||||||
|
<div class="profile__floating-header__inner">
|
||||||
|
<a href (click)="showAvatar(displayedAccount.avatar)" (auxclick)="openAccount()" title="open avatar">
|
||||||
|
<img class="profile__floating-header__avatar" [class.profile__disabled]="displayedAccount.moved"
|
||||||
|
src="{{displayedAccount.avatar}}" alt="header" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="profile__floating-header__names">
|
||||||
<div *ngIf="displayedAccount" class="profile-header"
|
<h2 class="profile__floating-header__names__display-name"
|
||||||
[ngStyle]="{'background-image':'url('+displayedAccount.header+')'}">
|
innerHTML="{{displayedAccount | accountEmoji }}" title="{{displayedAccount.display_name}}"></h2>
|
||||||
<div class="profile-header__inner">
|
<a class="profile__floating-header__names__fullhandle" href="{{displayedAccount.url}}"
|
||||||
<a href (click)="showAvatar(displayedAccount.avatar)" (auxclick)="openAccount()" title="open avatar">
|
target="_blank" title="{{displayedAccount.acct}}">@{{displayedAccount.acct}}</a>
|
||||||
<img class="profile-header__avatar" src="{{displayedAccount.avatar}}" alt="header" />
|
|
||||||
</a>
|
|
||||||
<h2 class="profile-header__display-name" innerHTML="{{displayedAccount | accountEmoji }}"></h2>
|
|
||||||
<h2 class="profile-header__fullhandle"><a href="{{displayedAccount.url}}"
|
|
||||||
target="_blank">@{{displayedAccount.acct}}</a></h2>
|
|
||||||
|
|
||||||
<div class="profile-header__follow" *ngIf="relationship">
|
|
||||||
<button 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"
|
|
||||||
(click)="unfollow()" *ngIf="relationship.following">
|
|
||||||
<fa-icon [icon]="faUserCheck"></fa-icon>
|
|
||||||
</button>
|
|
||||||
<button class="profile-header__follow--button profile-header__follow--followed" title="pending"
|
|
||||||
(click)="unfollow()" *ngIf="relationship.requested">
|
|
||||||
<fa-icon [icon]="faHourglassHalf"></fa-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="profile-header__state" *ngIf="relationship">
|
|
||||||
<div class="profile-header__state--data" *ngIf="relationship.followed_by">follows you</div>
|
|
||||||
<div class="profile-header__state--data" *ngIf="relationship.blocking">blocked</div>
|
|
||||||
<div class="profile-header__state--data" *ngIf="relationship.blocked_by">blocks you</div>
|
|
||||||
<div class="profile-header__state--data" *ngIf="relationship.domain_blocking">domain blocked</div>
|
|
||||||
<div class="profile-header__state--data" *ngIf="relationship.muting">muted</div>
|
|
||||||
<div class="profile-header__state--data" *ngIf="relationship.muting_notifications">notifications muted</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-sub-header ">
|
|
||||||
<div *ngIf="displayedAccount && hasNote" class="profile-description">
|
|
||||||
<app-databinded-text class="profile-description__content" [textIsSelectable]="false" [text]="note"
|
|
||||||
(accountSelected)="browseAccount($event)" (hashtagSelected)="browseHashtag($event)">
|
|
||||||
</app-databinded-text>
|
|
||||||
</div>
|
|
||||||
<div class="profile-fields" *ngIf="displayedAccount && displayedAccount.fields.length > 0">
|
|
||||||
<div class="profile-fields__field" *ngFor="let field of displayedAccount.fields">
|
|
||||||
<div class="profile-fields__field--value" innerHTML="{{ displayedAccount | accountEmoji:field.value}}"
|
|
||||||
[ngClass]="{'profile-fields__field--validated': field.verified_at }">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-fields__field--name">
|
|
||||||
{{ field.name }}
|
<div class="profile__floating-header__follow" *ngIf="relationship && !displayedAccount.moved">
|
||||||
|
<app-waiting-animation *ngIf="loadingRelationShip"
|
||||||
|
class="waiting-icon profile-header__follow--waiting">
|
||||||
|
</app-waiting-animation>
|
||||||
|
|
||||||
|
<div *ngIf="!loadingRelationShip">
|
||||||
|
<button 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"
|
||||||
|
(click)="unfollow()" *ngIf="relationship.following">
|
||||||
|
<fa-icon [icon]="faUserCheck"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button class="profile-header__follow--button profile-header__follow--followed" title="pending"
|
||||||
|
(click)="unfollow()" *ngIf="relationship.requested">
|
||||||
|
<fa-icon [icon]="faHourglassHalf"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="profile-statuses">
|
<app-waiting-animation *ngIf="isLoading" class="waiting-icon"></app-waiting-animation>
|
||||||
<div *ngIf="!isLoading && !statusLoading && statuses.length == 0" class="profile-no-toots">
|
|
||||||
no toots found
|
<div class="profile__moved" *ngIf="displayedAccount && displayedAccount.moved">
|
||||||
|
{{displayedAccount | accountEmoji }} has moved to <a href
|
||||||
|
(click)="openMigratedAccount(displayedAccount.moved)" class="profile__moved--link"
|
||||||
|
title="open @{{displayedAccount.moved.acct }}">@{{displayedAccount.moved.acct }}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="displayedAccount" class="profile-header"
|
||||||
|
[ngStyle]="{'background-image':'url('+displayedAccount.header+')'}">
|
||||||
|
<div class="profile-header__inner">
|
||||||
|
<a href (click)="showAvatar(displayedAccount.avatar)" (auxclick)="openAccount()" title="open avatar">
|
||||||
|
<img class="profile-header__avatar" [class.profile__disabled]="displayedAccount.moved"
|
||||||
|
src="{{displayedAccount.avatar}}" alt="header" />
|
||||||
|
</a>
|
||||||
|
<!-- <h2 class="profile-header__display-name" innerHTML="{{displayedAccount | accountEmoji }}"
|
||||||
|
title="{{displayedAccount.display_name}}"></h2>
|
||||||
|
<h2 class="profile-header__fullhandle"><a href="{{displayedAccount.url}}" target="_blank"
|
||||||
|
title="{{displayedAccount.acct}}">@{{displayedAccount.acct}}</a></h2> -->
|
||||||
|
|
||||||
|
<div class="profile-header__follow" *ngIf="relationship && !displayedAccount.moved">
|
||||||
|
<app-waiting-animation *ngIf="loadingRelationShip"
|
||||||
|
class="waiting-icon profile-header__follow--waiting">
|
||||||
|
</app-waiting-animation>
|
||||||
|
|
||||||
|
<div *ngIf="!loadingRelationShip">
|
||||||
|
<button 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"
|
||||||
|
(click)="unfollow()" *ngIf="relationship.following">
|
||||||
|
<fa-icon [icon]="faUserCheck"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button class="profile-header__follow--button profile-header__follow--followed" title="pending"
|
||||||
|
(click)="unfollow()" *ngIf="relationship.requested">
|
||||||
|
<fa-icon [icon]="faHourglassHalf"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-header__state"
|
||||||
|
*ngIf="relationship && !displayedAccount.moved && !loadingRelationShip">
|
||||||
|
<div class="profile-header__state--data" *ngIf="relationship.followed_by">follows you</div>
|
||||||
|
<div class="profile-header__state--data" *ngIf="relationship.blocking">blocked</div>
|
||||||
|
<div class="profile-header__state--data" *ngIf="relationship.blocked_by">blocks you</div>
|
||||||
|
<div class="profile-header__state--data" *ngIf="relationship.domain_blocking">domain blocked</div>
|
||||||
|
<div class="profile-header__state--data" *ngIf="relationship.muting">muted</div>
|
||||||
|
<div class="profile-header__state--data" *ngIf="relationship.endorsed">endorsed</div>
|
||||||
|
<!-- <div class="profile-header__state--data" *ngIf="relationship.muting_notifications">notifications
|
||||||
|
muted
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngFor="let statusWrapper of pinnedStatuses">
|
<app-status-user-context-menu class="profile-header__more" [displayedAccount]="displayedAccount">
|
||||||
<app-status [statusWrapper]="statusWrapper"
|
</app-status-user-context-menu>
|
||||||
(browseHashtagEvent)="browseHashtag($event)"
|
</div>
|
||||||
(browseAccountEvent)="browseAccount($event)"
|
|
||||||
(browseThreadEvent)="browseThread($event)">
|
<div class="profile-sub-header">
|
||||||
</app-status>
|
|
||||||
|
<div *ngIf="displayedAccount">
|
||||||
|
<div class="profile-name">
|
||||||
|
<h2 class="profile-name__link profile-name__display-name"
|
||||||
|
innerHTML="{{displayedAccount | accountEmoji }}" title="{{displayedAccount.display_name}}"></h2>
|
||||||
|
<h2 class="profile-name__link profile-name__fullhandle"><a href="{{displayedAccount.url}}"
|
||||||
|
target="_blank" title="{{displayedAccount.acct}}">@{{displayedAccount.acct}}</a></h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="profile__extra-info">
|
||||||
|
<div class="profile__extra-info__section">
|
||||||
|
<a href class="profile__extra-info__links"
|
||||||
|
(click)="switchProfileSection('fields')"
|
||||||
|
[class.profile__extra-info__links--selected]="profileSection === 'fields'">Fields</a>
|
||||||
|
</div>
|
||||||
|
<div class="profile__extra-info__section">
|
||||||
|
<a href class="profile__extra-info__links"
|
||||||
|
(click)="switchProfileSection('choices')"
|
||||||
|
[class.profile__extra-info__links--selected]="profileSection === 'choices'">User choices</a>
|
||||||
|
</div>
|
||||||
|
<div class="profile__extra-info__section">
|
||||||
|
<a href class="profile__extra-info__links"
|
||||||
|
(click)="switchProfileSection('hashtags')"
|
||||||
|
[class.profile__extra-info__links--selected]="profileSection === 'hashtags'">Hashtags</a>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
<div class="profile-fields" *ngIf="displayedAccount.fields.length > 0">
|
||||||
|
<div class="profile-fields__field" *ngFor="let field of displayedAccount.fields">
|
||||||
|
<div class="profile-fields__field--value"
|
||||||
|
innerHTML="{{ displayedAccount | accountEmoji:field.value}}"
|
||||||
|
[ngClass]="{'profile-fields__field--validated': field.verified_at }">
|
||||||
|
</div>
|
||||||
|
<div class="profile-fields__field--name" title="{{ field.name }}">
|
||||||
|
{{ field.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="hasNote" class="profile-description">
|
||||||
|
<app-databinded-text class="profile-description__content" [textIsSelectable]="false" [text]="note"
|
||||||
|
(accountSelected)="browseAccount($event)" (hashtagSelected)="browseHashtag($event)">
|
||||||
|
</app-databinded-text>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngFor="let statusWrapper of statuses">
|
<div class="profile__extra-info profile__extra-info__preparefloating" *ngIf="!isLoading"
|
||||||
<app-status [statusWrapper]="statusWrapper"
|
[class.profile__extra-info__floating]="showFloatingStatusMenu">
|
||||||
(browseHashtagEvent)="browseHashtag($event)"
|
<div class="profile__extra-info__section">
|
||||||
(browseAccountEvent)="browseAccount($event)"
|
<a href class="profile__extra-info__links" (click)="switchStatusSection('status')" title="Status"
|
||||||
(browseThreadEvent)="browseThread($event)">
|
[class.profile__extra-info__links--selected]="statusSection === 'status'">Status</a>
|
||||||
</app-status>
|
</div>
|
||||||
|
<div class="profile__extra-info__section">
|
||||||
|
<a href class="profile__extra-info__links" (click)="switchStatusSection('replies')"
|
||||||
|
title="Status & Replies"
|
||||||
|
[class.profile__extra-info__links--selected]="statusSection === 'replies'">Status &
|
||||||
|
Replies</a>
|
||||||
|
</div>
|
||||||
|
<div class="profile__extra-info__section">
|
||||||
|
<a href class="profile__extra-info__links" (click)="switchStatusSection('media')" title="Media"
|
||||||
|
[class.profile__extra-info__links--selected]="statusSection === 'media'">Media</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="profile-statuses" #profilestatuses>
|
||||||
|
<div class="profile__extra-info" *ngIf="!isLoading">
|
||||||
|
<div class="profile__extra-info__section">
|
||||||
|
<a href class="profile__extra-info__links" (click)="switchStatusSection('status')"
|
||||||
|
title="Status"
|
||||||
|
[class.profile__extra-info__links--selected]="statusSection === 'status'">Status</a>
|
||||||
|
</div>
|
||||||
|
<div class="profile__extra-info__section">
|
||||||
|
<a href class="profile__extra-info__links" (click)="switchStatusSection('replies')"
|
||||||
|
title="Status & Replies"
|
||||||
|
[class.profile__extra-info__links--selected]="statusSection === 'replies'">Status &
|
||||||
|
Replies</a>
|
||||||
|
</div>
|
||||||
|
<div class="profile__extra-info__section">
|
||||||
|
<a href class="profile__extra-info__links" (click)="switchStatusSection('media')" title="Media"
|
||||||
|
[class.profile__extra-info__links--selected]="statusSection === 'media'">Media</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<app-waiting-animation *ngIf="statusLoading" class="waiting-icon"></app-waiting-animation>
|
<div [class.profile__status-switching-section]="isSwitchingSection">
|
||||||
|
<div *ngIf="!isLoading && !statusLoading && statuses.length == 0" class="profile-no-toots">
|
||||||
|
no status found
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="statusSection === 'status' && !statusLoading">
|
||||||
|
<div *ngFor="let statusWrapper of pinnedStatuses">
|
||||||
|
<app-status [statusWrapper]="statusWrapper" (browseHashtagEvent)="browseHashtag($event)"
|
||||||
|
(browseAccountEvent)="browseAccount($event)" (browseThreadEvent)="browseThread($event)">
|
||||||
|
</app-status>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngFor="let statusWrapper of statuses">
|
||||||
|
<div *ngIf="statusSection !== 'media'">
|
||||||
|
<app-status [statusWrapper]="statusWrapper" (browseHashtagEvent)="browseHashtag($event)"
|
||||||
|
(browseAccountEvent)="browseAccount($event)" (browseThreadEvent)="browseThread($event)">
|
||||||
|
</app-status>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="statusSection === 'media'" class="status-media">
|
||||||
|
<div *ngFor="let media of statusWrapper.status.media_attachments">
|
||||||
|
<a href title="open" (click)="openAttachment(media)">
|
||||||
|
<img class="status-media__image" src="{{media.preview_url}}" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="clear: both;"></div>
|
||||||
|
<app-waiting-animation *ngIf="statusLoading" class="waiting-icon"></app-waiting-animation>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -3,47 +3,159 @@
|
|||||||
@import "commons";
|
@import "commons";
|
||||||
$validated-font-color: #4fde23;
|
$validated-font-color: #4fde23;
|
||||||
$validated-background: #164109;
|
$validated-background: #164109;
|
||||||
$header-height: 160px;
|
$header-height: 140px;
|
||||||
.profile {
|
$full-alias-color: rgb(201, 201, 201);
|
||||||
|
$full-alias-color-hover: white;
|
||||||
|
$floating-header-height: 60px;
|
||||||
|
|
||||||
|
.outer-profile {
|
||||||
|
height: calc(100%);
|
||||||
|
// width: $stream-column-width;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile {
|
||||||
// overflow: auto;
|
// overflow: auto;
|
||||||
height: calc(100%);
|
height: calc(100%);
|
||||||
|
// width: $stream-column-width;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
&-header {
|
|
||||||
|
&__floating-header {
|
||||||
|
transition: all .2s;
|
||||||
|
transition-timing-function: ease-in;
|
||||||
|
|
||||||
|
height: $floating-header-height;
|
||||||
|
width: calc(100% - #{$scroll-bar-width});
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
position: relative; // height: 140px;
|
background-color: #0f111a;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: -66px;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
// border-bottom: 1px solid #222736;
|
||||||
|
box-shadow: 0 6px 4px -4px rgba(0, 0, 0, .25);
|
||||||
|
|
||||||
|
// opacity: 0;
|
||||||
|
|
||||||
|
&__inner {
|
||||||
|
overflow: hidden;
|
||||||
|
height: $floating-header-height;
|
||||||
|
background-color: rgba(0, 0, 0, .45);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__activated {
|
||||||
|
// opacity: 1;
|
||||||
|
transition: all .25s;
|
||||||
|
transition-timing-function: ease-out;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__avatar {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
left: 6px;
|
||||||
|
width: calc(#{$floating-header-height} - 14px);
|
||||||
|
height: calc(#{$floating-header-height} - 14px);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__names {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
left: 56px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
height: calc(#{$floating-header-height} - 14px);
|
||||||
|
width: calc(100% - 44px - 65px);
|
||||||
|
padding: 3px 5px 0px 3px;
|
||||||
|
|
||||||
|
&__display-name {
|
||||||
|
font-size: 15px;
|
||||||
|
color: white;
|
||||||
|
margin: 0 0 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__fullhandle {
|
||||||
|
transition: all .2s;
|
||||||
|
font-size: 15px;
|
||||||
|
color: $full-alias-color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $full-alias-color-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__follow {
|
||||||
|
// transition: all .4s;
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
right: 10px;
|
||||||
|
font-size: 28px;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__moved {
|
||||||
|
padding: 5px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&--link {
|
||||||
|
transition: .2s;
|
||||||
|
color: white;
|
||||||
|
display: inline-block;
|
||||||
|
max-width: calc(100%);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(255, 190, 71);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__disabled {
|
||||||
|
filter: gray;
|
||||||
|
-webkit-filter: grayscale(100%);
|
||||||
|
-moz-filter: grayscale(100%);
|
||||||
|
-ms-filter: grayscale(100%);
|
||||||
|
-o-filter: grayscale(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
|
background-size: cover;
|
||||||
|
position: relative;
|
||||||
overflow: hidden; // background-color: black;
|
overflow: hidden; // background-color: black;
|
||||||
border-bottom: 1px solid black;
|
//border-bottom: 1px solid black;
|
||||||
|
|
||||||
& h2 {
|
& h2 {
|
||||||
font-size: $default-font-size;
|
font-size: $default-font-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__inner {
|
&__inner {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: $header-height;
|
height: $header-height;
|
||||||
background-color: rgba(0, 0, 0, .45);
|
background-color: rgba(0, 0, 0, .45);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__avatar {
|
&__avatar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 12px;
|
bottom: 12px;
|
||||||
left: 12px;
|
left: 12px;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
&__display-name {
|
|
||||||
position: absolute;
|
|
||||||
top: 105px;
|
|
||||||
left: 15px;
|
|
||||||
width: calc(100% - 30px);
|
|
||||||
overflow: hidden;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
&__fullhandle a {
|
|
||||||
position: absolute;
|
|
||||||
top: 130px;
|
|
||||||
left: 15px;
|
|
||||||
width: calc(100% - 30px);
|
|
||||||
overflow: hidden;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
&__follow {
|
&__follow {
|
||||||
// transition: all .4s;
|
// transition: all .4s;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -53,26 +165,57 @@ $header-height: 160px;
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
|
|
||||||
&--button {
|
&--button {
|
||||||
@include clearButton;
|
@include clearButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--unfollowed {}
|
&--unfollowed {}
|
||||||
|
|
||||||
&--followed {
|
&--followed {
|
||||||
color: #38abff;
|
color: #38abff;
|
||||||
color: #5fbcff;
|
color: #5fbcff;
|
||||||
color: #85ccff;
|
color: #85ccff;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
&__state {
|
&--waiting {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 50px;
|
top: -5px;
|
||||||
right: 15px;
|
left: 6px;
|
||||||
font-size: 12px;
|
|
||||||
&--data {
|
|
||||||
text-align: right;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__state {
|
||||||
|
position: absolute;
|
||||||
|
top: 14px;
|
||||||
|
left: 14px;
|
||||||
|
|
||||||
|
top: 11px;
|
||||||
|
left: 11px;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
// max-width: 150px;
|
||||||
|
width: 265px;
|
||||||
|
|
||||||
|
//outline: 1px solid greenyellow;
|
||||||
|
|
||||||
|
&--data {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: rgba(0, 0, 0, .60);
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 2px 2px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__more {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
right: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-sub-header {
|
&-sub-header {
|
||||||
// overflow: auto;
|
// overflow: auto;
|
||||||
// height: calc(100% - #{$header-height});
|
// height: calc(100% - #{$header-height});
|
||||||
@ -81,22 +224,96 @@ $header-height: 160px;
|
|||||||
// height: 150px;
|
// height: 150px;
|
||||||
// border: 1px solid greenyellow;
|
// border: 1px solid greenyellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
padding: 9px 10px 10px 10px;
|
||||||
|
width: calc(100%);
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
width: calc(100%);
|
||||||
|
font-size: 15px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&:not(:last-child){
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__display-name {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__fullhandle a {
|
||||||
|
transition: all .2s;
|
||||||
|
color: $full-alias-color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $full-alias-color-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-description {
|
&-description {
|
||||||
padding: 10px 10px 15px 10px;
|
padding: 9px 10px 15px 10px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border-bottom: 1px solid black;
|
// border-bottom: 1px solid black;
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
width: calc(100%);
|
width: calc(100%);
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__extra-info {
|
||||||
|
background-color: #20273a;
|
||||||
|
background-color: #141824;
|
||||||
|
background-color: #1a1f2e;
|
||||||
|
font-size: 13px;
|
||||||
|
transition: all .4s;
|
||||||
|
|
||||||
|
&__section {
|
||||||
|
text-align: center;
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(33.333% - 5px);
|
||||||
|
padding: 5px 0 7px 0;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__preparefloating {
|
||||||
|
width: calc(100% - #{$scroll-bar-width});
|
||||||
|
position: absolute;
|
||||||
|
top: -66px;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__floating {
|
||||||
|
top: 60px;
|
||||||
|
box-shadow: 0 6px 4px -4px rgba(0, 0, 0, .45);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__links {
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&--selected {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-fields {
|
&-fields {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border-bottom: 1px solid black;
|
border-bottom: 1px solid black;
|
||||||
|
|
||||||
&__field {
|
&__field {
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
border-bottom: 1px solid black;
|
border-bottom: 1px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--name {
|
&--name {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-right: 1px solid black;
|
border-right: 1px solid black;
|
||||||
@ -104,25 +321,46 @@ $header-height: 160px;
|
|||||||
width: calc(33%);
|
width: calc(33%);
|
||||||
background-color: #0b0d13;
|
background-color: #0b0d13;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--value {
|
&--value {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
width: calc(66%);
|
width: calc(66%);
|
||||||
float: right;
|
float: right;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--validated {
|
&--validated {
|
||||||
background-color: $validated-background;
|
background-color: $validated-background;
|
||||||
// border: 1px solid $validated-font-color;
|
// border: 1px solid $validated-font-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include clearfix;
|
@include clearfix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__status-switching-section {
|
||||||
|
height: calc(100vh - 35px - #{$floating-header-height} - #{$stream-header-height} - #{$stream-selector-height});
|
||||||
|
}
|
||||||
|
|
||||||
&-no-toots {
|
&-no-toots {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
border: 2px solid whitesmoke;
|
// border: 2px solid whitesmoke;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-media {
|
||||||
|
&__image {
|
||||||
|
width: calc(50% - 2px);
|
||||||
|
height: 150px;
|
||||||
|
object-fit: cover;
|
||||||
|
margin: 1px;
|
||||||
|
float: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,14 +368,17 @@ $header-height: 160px;
|
|||||||
:host ::ng-deep .profile-fields__field--value {
|
:host ::ng-deep .profile-fields__field--value {
|
||||||
// font-size: 14px;
|
// font-size: 14px;
|
||||||
color: $status-primary-color;
|
color: $status-primary-color;
|
||||||
|
|
||||||
& a,
|
& a,
|
||||||
.mention,
|
.mention,
|
||||||
.ellipsis {
|
.ellipsis {
|
||||||
color: $status-links-color;
|
color: $status-links-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .invisible {
|
& .invisible {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
& p {
|
& p {
|
||||||
margin: 0px; //font-size: .9em;
|
margin: 0px; //font-size: .9em;
|
||||||
// font-size: 14px;
|
// font-size: 14px;
|
||||||
@ -147,14 +388,17 @@ $header-height: 160px;
|
|||||||
:host ::ng-deep .profile-fields__field--validated {
|
:host ::ng-deep .profile-fields__field--validated {
|
||||||
// font-size: 14px;
|
// font-size: 14px;
|
||||||
color: $validated-font-color;
|
color: $validated-font-color;
|
||||||
|
|
||||||
& a,
|
& a,
|
||||||
.mention,
|
.mention,
|
||||||
.ellipsis {
|
.ellipsis {
|
||||||
color: $validated-font-color;
|
color: $validated-font-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .invisible {
|
& .invisible {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
& p {
|
& p {
|
||||||
margin: 0px; //font-size: .9em;
|
margin: 0px; //font-size: .9em;
|
||||||
// font-size: 14px;
|
// font-size: 14px;
|
||||||
|
@ -10,7 +10,7 @@ import { MastodonService } from '../../../services/mastodon.service';
|
|||||||
import { ToolsService, OpenThreadEvent } from '../../../services/tools.service';
|
import { ToolsService, OpenThreadEvent } from '../../../services/tools.service';
|
||||||
import { NotificationService } from '../../../services/notification.service';
|
import { NotificationService } from '../../../services/notification.service';
|
||||||
import { AccountInfo } from '../../../states/accounts.state';
|
import { AccountInfo } from '../../../states/accounts.state';
|
||||||
import { StatusWrapper } from '../../../models/common.model';
|
import { StatusWrapper, OpenMediaEvent } from '../../../models/common.model';
|
||||||
import { EmojiConverter, EmojiTypeEnum } from '../../../tools/emoji.tools';
|
import { EmojiConverter, EmojiTypeEnum } from '../../../tools/emoji.tools';
|
||||||
import { NavigationService } from '../../../services/navigation.service';
|
import { NavigationService } from '../../../services/navigation.service';
|
||||||
|
|
||||||
@ -33,6 +33,9 @@ export class UserProfileComponent implements OnInit {
|
|||||||
note: string;
|
note: string;
|
||||||
|
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
loadingRelationShip = false;
|
||||||
|
showFloatingHeader = false;
|
||||||
|
showFloatingStatusMenu = false;
|
||||||
|
|
||||||
private maxReached = false;
|
private maxReached = false;
|
||||||
private maxId: string;
|
private maxId: string;
|
||||||
@ -43,6 +46,9 @@ export class UserProfileComponent implements OnInit {
|
|||||||
statuses: StatusWrapper[] = [];
|
statuses: StatusWrapper[] = [];
|
||||||
pinnedStatuses: StatusWrapper[] = [];
|
pinnedStatuses: StatusWrapper[] = [];
|
||||||
|
|
||||||
|
profileSection: 'fields' | 'choices' | 'hashtags' = 'fields';
|
||||||
|
statusSection: 'status' | 'replies' | 'media' = 'status';
|
||||||
|
|
||||||
private lastAccountName: string;
|
private lastAccountName: string;
|
||||||
|
|
||||||
private currentlyUsedAccount: AccountInfo;
|
private currentlyUsedAccount: AccountInfo;
|
||||||
@ -51,6 +57,7 @@ export class UserProfileComponent implements OnInit {
|
|||||||
private deleteStatusSubscription: Subscription;
|
private deleteStatusSubscription: Subscription;
|
||||||
|
|
||||||
@ViewChild('statusstream') public statustream: ElementRef;
|
@ViewChild('statusstream') public statustream: ElementRef;
|
||||||
|
@ViewChild('profilestatuses') public profilestatuses: ElementRef;
|
||||||
|
|
||||||
@Output() browseAccountEvent = new EventEmitter<string>();
|
@Output() browseAccountEvent = new EventEmitter<string>();
|
||||||
@Output() browseHashtagEvent = new EventEmitter<string>();
|
@Output() browseHashtagEvent = new EventEmitter<string>();
|
||||||
@ -76,12 +83,16 @@ export class UserProfileComponent implements OnInit {
|
|||||||
if (this.displayedAccount) {
|
if (this.displayedAccount) {
|
||||||
const userAccount = accounts.filter(x => x.isSelected)[0];
|
const userAccount = accounts.filter(x => x.isSelected)[0];
|
||||||
|
|
||||||
|
this.loadingRelationShip = true;
|
||||||
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
||||||
.then((account: Account) => {
|
.then((account: Account) => {
|
||||||
this.getFollowStatus(userAccount, account);
|
return this.getFollowStatus(userAccount, account);
|
||||||
})
|
})
|
||||||
.catch((err: HttpErrorResponse) => {
|
.catch((err: HttpErrorResponse) => {
|
||||||
this.notificationService.notifyHttpError(err);
|
this.notificationService.notifyHttpError(err);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.loadingRelationShip = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -106,6 +117,8 @@ export class UserProfileComponent implements OnInit {
|
|||||||
|
|
||||||
this.displayedAccount = null;
|
this.displayedAccount = null;
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
this.showFloatingHeader = false;
|
||||||
|
this.isSwitchingSection = false;
|
||||||
|
|
||||||
this.lastAccountName = accountName;
|
this.lastAccountName = accountName;
|
||||||
this.currentlyUsedAccount = this.toolsService.getSelectedAccounts()[0];
|
this.currentlyUsedAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
@ -122,7 +135,7 @@ export class UserProfileComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getFollowStatusPromise = this.getFollowStatus(this.currentlyUsedAccount, this.displayedAccount);
|
const getFollowStatusPromise = this.getFollowStatus(this.currentlyUsedAccount, this.displayedAccount);
|
||||||
const getStatusesPromise = this.getStatuses(this.currentlyUsedAccount, this.displayedAccount);
|
const getStatusesPromise = this.getStatuses(this.currentlyUsedAccount, this.displayedAccount, false, true, null);
|
||||||
const getPinnedStatusesPromise = this.getPinnedStatuses(this.currentlyUsedAccount, this.displayedAccount);
|
const getPinnedStatusesPromise = this.getPinnedStatuses(this.currentlyUsedAccount, this.displayedAccount);
|
||||||
|
|
||||||
return Promise.all([getFollowStatusPromise, getStatusesPromise, getPinnedStatusesPromise]);
|
return Promise.all([getFollowStatusPromise, getStatusesPromise, getPinnedStatusesPromise]);
|
||||||
@ -137,9 +150,10 @@ export class UserProfileComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getPinnedStatuses(userAccount: AccountInfo, account: Account): Promise<void> {
|
private getPinnedStatuses(userAccount: AccountInfo, account: Account): Promise<void> {
|
||||||
return this.mastodonService.getAccountStatuses(userAccount, account.id, false, true, false, null, null, 40)
|
return this.mastodonService.getAccountStatuses(userAccount, account.id, false, true, false, null, null, 20)
|
||||||
.then((statuses: Status[]) => {
|
.then((statuses: Status[]) => {
|
||||||
for (const status of statuses) {
|
for (const status of statuses) {
|
||||||
|
status.pinned = true;
|
||||||
const wrapper = new StatusWrapper(status, userAccount);
|
const wrapper = new StatusWrapper(status, userAccount);
|
||||||
this.pinnedStatuses.push(wrapper);
|
this.pinnedStatuses.push(wrapper);
|
||||||
}
|
}
|
||||||
@ -149,9 +163,10 @@ export class UserProfileComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getStatuses(userAccount: AccountInfo, account: Account): Promise<void> {
|
private getStatuses(userAccount: AccountInfo, account: Account, onlyMedia: boolean, excludeReplies: boolean, maxId: string): Promise<void> {
|
||||||
this.statusLoading = true;
|
this.statusLoading = true;
|
||||||
return this.mastodonService.getAccountStatuses(userAccount, account.id, false, false, true, null, null, 40)
|
|
||||||
|
return this.mastodonService.getAccountStatuses(userAccount, account.id, onlyMedia, false, excludeReplies, maxId, null, 40)
|
||||||
.then((statuses: Status[]) => {
|
.then((statuses: Status[]) => {
|
||||||
this.loadStatus(userAccount, statuses);
|
this.loadStatus(userAccount, statuses);
|
||||||
})
|
})
|
||||||
@ -164,10 +179,16 @@ export class UserProfileComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getFollowStatus(userAccount: AccountInfo, account: Account): Promise<void> {
|
private getFollowStatus(userAccount: AccountInfo, account: Account): Promise<void> {
|
||||||
// this.relationship = null;
|
this.loadingRelationShip = true;
|
||||||
return this.mastodonService.getRelationships(userAccount, [account])
|
return this.mastodonService.getRelationships(userAccount, [account])
|
||||||
.then((result: Relationship[]) => {
|
.then((result: Relationship[]) => {
|
||||||
this.relationship = result.filter(x => x.id === account.id)[0];
|
this.relationship = result.filter(x => x.id === account.id)[0];
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.loadingRelationShip = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +214,7 @@ export class UserProfileComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refresh(): any {
|
refresh(): any {
|
||||||
|
this.showFloatingHeader = false;
|
||||||
this.load(this.lastAccountName);
|
this.load(this.lastAccountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +222,12 @@ export class UserProfileComponent implements OnInit {
|
|||||||
this.browseAccountEvent.next(accountName);
|
this.browseAccountEvent.next(accountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openMigratedAccount(account: Account): boolean {
|
||||||
|
const handle = this.toolsService.getAccountFullHandle(account);
|
||||||
|
this.browseAccount(handle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
browseHashtag(hashtag: string): void {
|
browseHashtag(hashtag: string): void {
|
||||||
this.browseHashtagEvent.next(hashtag);
|
this.browseHashtagEvent.next(hashtag);
|
||||||
}
|
}
|
||||||
@ -242,6 +270,19 @@ export class UserProfileComponent implements OnInit {
|
|||||||
var element = this.statustream.nativeElement as HTMLElement;
|
var element = this.statustream.nativeElement as HTMLElement;
|
||||||
const atBottom = element.scrollHeight <= element.clientHeight + element.scrollTop + 1000;
|
const atBottom = element.scrollHeight <= element.clientHeight + element.scrollTop + 1000;
|
||||||
|
|
||||||
|
if (element.scrollTop > 135) {
|
||||||
|
this.showFloatingHeader = true;
|
||||||
|
} else {
|
||||||
|
this.showFloatingHeader = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuPosition = element.scrollHeight - this.profilestatuses.nativeElement.offsetHeight - 30 - 31;
|
||||||
|
if (element.scrollTop > menuPosition) {
|
||||||
|
this.showFloatingStatusMenu = true;
|
||||||
|
} else {
|
||||||
|
this.showFloatingStatusMenu = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (atBottom) {
|
if (atBottom) {
|
||||||
this.scrolledToBottom();
|
this.scrolledToBottom();
|
||||||
}
|
}
|
||||||
@ -250,18 +291,10 @@ export class UserProfileComponent implements OnInit {
|
|||||||
private scrolledToBottom() {
|
private scrolledToBottom() {
|
||||||
if (this.statusLoading || this.maxReached) return;
|
if (this.statusLoading || this.maxReached) return;
|
||||||
|
|
||||||
this.statusLoading = true;
|
const onlyMedia = this.statusSection === 'media';
|
||||||
const userAccount = this.currentlyUsedAccount;
|
const excludeReplies = this.statusSection === 'status';
|
||||||
this.mastodonService.getAccountStatuses(userAccount, this.displayedAccount.id, false, false, true, this.maxId, null, 40)
|
|
||||||
.then((statuses: Status[]) => {
|
this.getStatuses(this.currentlyUsedAccount, this.displayedAccount, onlyMedia, excludeReplies, this.maxId);
|
||||||
this.loadStatus(userAccount, statuses);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.notificationService.notifyHttpError(err);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.statusLoading = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadStatus(userAccount: AccountInfo, statuses: Status[]) {
|
private loadStatus(userAccount: AccountInfo, statuses: Status[]) {
|
||||||
@ -282,4 +315,60 @@ export class UserProfileComponent implements OnInit {
|
|||||||
window.open(this.displayedAccount.url, '_blank');
|
window.open(this.displayedAccount.url, '_blank');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switchProfileSection(section: 'fields' | 'choices' | 'hashtags'): boolean {
|
||||||
|
this.profileSection = section;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSwitchingSection: boolean;
|
||||||
|
switchStatusSection(section: 'status' | 'replies' | 'media'): boolean {
|
||||||
|
this.isSwitchingSection = true;
|
||||||
|
|
||||||
|
this.statusSection = section;
|
||||||
|
this.statuses.length = 0;
|
||||||
|
this.maxId = null;
|
||||||
|
|
||||||
|
// this.showFloatingHeader = false;
|
||||||
|
// this.showFloatingStatusMenu = false;
|
||||||
|
|
||||||
|
let promise: Promise<any>;
|
||||||
|
switch (section) {
|
||||||
|
case "status":
|
||||||
|
promise = this.getStatuses(this.currentlyUsedAccount, this.displayedAccount, false, true, this.maxId);
|
||||||
|
break;
|
||||||
|
case "replies":
|
||||||
|
promise = this.getStatuses(this.currentlyUsedAccount, this.displayedAccount, false, false, this.maxId);
|
||||||
|
break;
|
||||||
|
case "media":
|
||||||
|
promise = this.getStatuses(this.currentlyUsedAccount, this.displayedAccount, true, true, this.maxId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (promise) {
|
||||||
|
promise
|
||||||
|
.catch(err => {
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.isSwitchingSection = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.isSwitchingSection = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showFloatingStatusMenu) {
|
||||||
|
setTimeout(() => {
|
||||||
|
var element = this.statustream.nativeElement as HTMLElement;
|
||||||
|
const menuPosition = element.scrollHeight - this.profilestatuses.nativeElement.offsetHeight - 30 - 29;
|
||||||
|
element.scrollTop = menuPosition;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
openAttachment(attachment: Attachment): boolean {
|
||||||
|
let openMediaEvent = new OpenMediaEvent(0, [attachment], null);
|
||||||
|
this.navigationService.openMedia(openMediaEvent);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.flexcroll {
|
.flexcroll {
|
||||||
scrollbar-face-color: #08090d;
|
scrollbar-color: $scrollbar-color-thumb #0f111a;
|
||||||
scrollbar-shadow-color: #08090d;
|
scrollbar-color: #090b10 #0f111a;
|
||||||
scrollbar-highlight-color: #08090d;
|
scrollbar-width: thin;
|
||||||
scrollbar-3dlight-color: #08090d;
|
|
||||||
scrollbar-darkshadow-color: #08090d;
|
scrollbar-face-color: $scrollbar-color;
|
||||||
scrollbar-track-color: #08090d;
|
scrollbar-shadow-color: $scrollbar-color;
|
||||||
scrollbar-arrow-color: #08090d;
|
scrollbar-highlight-color: $scrollbar-color;
|
||||||
|
scrollbar-3dlight-color: $scrollbar-color;
|
||||||
|
scrollbar-darkshadow-color: $scrollbar-color;
|
||||||
|
scrollbar-track-color: $scrollbar-color;
|
||||||
|
scrollbar-arrow-color: $scrollbar-color;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
height: 7px;
|
height: $scroll-bar-width;
|
||||||
}
|
}
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
-webkit-border-radius: 0px;
|
-webkit-border-radius: 0px;
|
||||||
|
@ -13,7 +13,8 @@ export class AccountEmojiPipe implements PipeTransform {
|
|||||||
|
|
||||||
let textToTransform = text;
|
let textToTransform = text;
|
||||||
if(!text){
|
if(!text){
|
||||||
textToTransform = value.display_name;
|
if(value.display_name) textToTransform = value.display_name;
|
||||||
|
else textToTransform = value.acct.split('@')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.emojiConverter.applyEmojis(value.emojis, textToTransform, EmojiTypeEnum.small)
|
return this.emojiConverter.applyEmojis(value.emojis, textToTransform, EmojiTypeEnum.small)
|
||||||
|
@ -38,7 +38,7 @@ export interface Account {
|
|||||||
header: string;
|
header: string;
|
||||||
header_static: string;
|
header_static: string;
|
||||||
emojis: Emoji[];
|
emojis: Emoji[];
|
||||||
moved: boolean;
|
moved: Account;
|
||||||
fields: Field[];
|
fields: Field[];
|
||||||
bot: boolean;
|
bot: boolean;
|
||||||
source: AccountInfo;
|
source: AccountInfo;
|
||||||
|
@ -25,24 +25,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.flexcroll {
|
.flexcroll {
|
||||||
scrollbar-face-color: #08090d;
|
scrollbar-color: $scrollbar-color-thumb #0f111a;
|
||||||
scrollbar-shadow-color: #08090d;
|
scrollbar-width: thin;
|
||||||
scrollbar-highlight-color: #08090d;
|
|
||||||
scrollbar-3dlight-color: #08090d;
|
scrollbar-face-color: $scrollbar-color;
|
||||||
scrollbar-darkshadow-color: #08090d;
|
scrollbar-shadow-color: $scrollbar-color;
|
||||||
scrollbar-track-color: #08090d;
|
scrollbar-highlight-color: $scrollbar-color;
|
||||||
scrollbar-arrow-color: #08090d;
|
scrollbar-3dlight-color: $scrollbar-color;
|
||||||
|
scrollbar-darkshadow-color: $scrollbar-color;
|
||||||
|
scrollbar-track-color: $scrollbar-color;
|
||||||
|
scrollbar-arrow-color: $scrollbar-color;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 7px;
|
width: $scroll-bar-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
-webkit-border-radius: 0px;
|
-webkit-border-radius: 0px;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
// background: #08090d;
|
background: $scrollbar-color-thumb;
|
||||||
background: lighten($color-primary, 5);
|
|
||||||
// -webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,32 +12,28 @@ $content-warning-background-color: #0a0c13;
|
|||||||
$content-warning-background-color: black;
|
$content-warning-background-color: black;
|
||||||
$content-warning-font-color: $font-link-primary-hover;
|
$content-warning-font-color: $font-link-primary-hover;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$default-font-size: 15px;
|
$default-font-size: 15px;
|
||||||
$small-font-size: 12px;
|
$small-font-size: 12px;
|
||||||
// $btn-primary-color: #515a62;
|
|
||||||
// $btn-primary-color: #254d6f;
|
|
||||||
$btn-primary-color: #444f74;
|
$btn-primary-color: #444f74;
|
||||||
$btn-primary-color-hover: darken($btn-primary-color, 10);
|
$btn-primary-color-hover: darken($btn-primary-color, 10);
|
||||||
$btn-primary-font-color: white;
|
$btn-primary-font-color: white;
|
||||||
// TEST 1
|
|
||||||
$status-primary-color: #fff;
|
$status-primary-color: #fff;
|
||||||
// $status-secondary-color: #353e64;
|
|
||||||
$status-secondary-color: #4e5572;
|
$status-secondary-color: #4e5572;
|
||||||
$status-links-color: #d9e1e8;
|
$status-links-color: #d9e1e8;
|
||||||
// $status-primary-color : #8f93a2;
|
|
||||||
// $status-primary-color : lighten(#8f93a2, 30);
|
|
||||||
// $status-links-color : #b2ccd6;
|
|
||||||
$boost-color : #5098eb;
|
$boost-color : #5098eb;
|
||||||
$favorite-color: #ffc16f;
|
$favorite-color: #ffc16f;
|
||||||
|
|
||||||
// Block dispositions
|
// Block dispositions
|
||||||
|
$scroll-bar-width: 8px;
|
||||||
|
|
||||||
$stream-selector-height: 30px;
|
$stream-selector-height: 30px;
|
||||||
$stream-column-separator: 7px;
|
$stream-column-separator: 7px;
|
||||||
$stream-column-width: 320px;
|
$stream-column-width: 320px;
|
||||||
$floating-column-size: 330px;
|
$floating-column-size: 330px;
|
||||||
$avatar-column-space: 70px;
|
$avatar-column-space: 70px;
|
||||||
|
|
||||||
|
$stream-header-height: 40px;
|
||||||
|
|
||||||
//Bootstrap cuistomization
|
//Bootstrap cuistomization
|
||||||
$enable-rounded: false;
|
$enable-rounded: false;
|
||||||
|
|
||||||
@ -81,4 +77,7 @@ $autosuggest-entry-background-hover: #3e455f;
|
|||||||
$autosuggest-entry-background-hover: rgb(37, 41, 56);
|
$autosuggest-entry-background-hover: rgb(37, 41, 56);
|
||||||
$autosuggest-entry-background-hover: rgb(46, 51, 70);
|
$autosuggest-entry-background-hover: rgb(46, 51, 70);
|
||||||
$autosuggest-entry-color-hover: #e2e2e2;
|
$autosuggest-entry-color-hover: #e2e2e2;
|
||||||
$autosuggest-entry-handle-color-hover: #ffffff;
|
$autosuggest-entry-handle-color-hover: #ffffff;
|
||||||
|
|
||||||
|
$scrollbar-color: #08090d;
|
||||||
|
$scrollbar-color-thumb: lighten($color-primary, 5);
|
Loading…
x
Reference in New Issue
Block a user