|
@ -2,6 +2,7 @@
|
||||||
/release
|
/release
|
||||||
|
|
||||||
# compiled output
|
# compiled output
|
||||||
|
/release
|
||||||
/dist
|
/dist
|
||||||
/dist-server
|
/dist-server
|
||||||
/tmp
|
/tmp
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
|
language: c
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: osx
|
||||||
|
- os: linux
|
||||||
|
env: CC=clang CXX=clang++ npm_config_clang=1
|
||||||
|
compiler: clang
|
||||||
|
|
||||||
|
node_js:
|
||||||
|
- 10.9.0
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- node_modules
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-4.8
|
||||||
|
- icnsutils
|
||||||
|
- graphicsmagick
|
||||||
|
- libgnome-keyring-dev
|
||||||
|
- xz-utils
|
||||||
|
- xorriso
|
||||||
|
- xvfb
|
||||||
|
|
||||||
|
install:
|
||||||
|
- nvm install 10.9.0
|
||||||
|
- npm install electron-builder@next
|
||||||
|
- npm install
|
||||||
|
- npm rebuild node-sass
|
||||||
|
- export DISPLAY=':99.0'
|
||||||
|
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- export DISPLAY=:99.0
|
||||||
|
- sh -e /etc/init.d/xvfb start &
|
||||||
|
- sleep 3
|
||||||
|
|
||||||
|
script:
|
||||||
|
- npm run dist
|
|
@ -0,0 +1,45 @@
|
||||||
|
os: unstable
|
||||||
|
cache:
|
||||||
|
- node_modules
|
||||||
|
environment:
|
||||||
|
GH_TOKEN:
|
||||||
|
secure: wRRBU0GXTmTBgZBs2PGSaEJWOflynAyvp3Nc/7e9xmciPfkUCQAXcpOn0jIYmzpb
|
||||||
|
matrix:
|
||||||
|
- nodejs_version: 10.9.0
|
||||||
|
install:
|
||||||
|
- ps: Install-Product node $env:nodejs_version
|
||||||
|
- set CI=true
|
||||||
|
- npm install -g npm@latest
|
||||||
|
- set PATH=%APPDATA%\npm;%PATH%
|
||||||
|
- npm install
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
build: off
|
||||||
|
version: '{build}'
|
||||||
|
shallow_clone: true
|
||||||
|
clone_depth: 1
|
||||||
|
test_script:
|
||||||
|
- ps: >-
|
||||||
|
npm run test-nowatch
|
||||||
|
|
||||||
|
$wc = New-Object 'System.Net.WebClient'
|
||||||
|
|
||||||
|
Get-ChildItem . -Name -Recurse 'TESTS-*.xml' |
|
||||||
|
|
||||||
|
Foreach-Object {
|
||||||
|
$wc.UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $_))
|
||||||
|
}
|
||||||
|
- npm run dist
|
||||||
|
artifacts:
|
||||||
|
- path: dist
|
||||||
|
deploy:
|
||||||
|
- provider: FTP
|
||||||
|
host: home205977321.1and1-data.host
|
||||||
|
protocol: sftp
|
||||||
|
username: u45308485-sengi
|
||||||
|
password:
|
||||||
|
secure: Sk3NZwuaYK9hTIQ3kgIIQEc8SmaPDVGvGpgsZzFEzoVLuy4WxVfvKQtegW9oXaj7
|
||||||
|
folder: /
|
||||||
|
application: dist.zip
|
||||||
|
on:
|
||||||
|
branch: master
|
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 34 KiB |
|
@ -10,7 +10,7 @@ let win
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
win = new BrowserWindow({ width: 393, height: 800, title: "Sengi", backgroundColor: '#FFF' });
|
win = new BrowserWindow({ width: 377, height: 800, title: "Sengi", backgroundColor: '#FFF', 'useContentSize': true });
|
||||||
|
|
||||||
var server = http.createServer(requestHandler).listen(9527);
|
var server = http.createServer(requestHandler).listen(9527);
|
||||||
win.loadURL('http://localhost:9527');
|
win.loadURL('http://localhost:9527');
|
||||||
|
|
69
package.json
|
@ -1,8 +1,17 @@
|
||||||
{
|
{
|
||||||
"name": "sengi",
|
"name": "sengi",
|
||||||
"version": "0.0.0",
|
"version": "0.6.0",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"main": "main-electron.js",
|
"main": "main-electron.js",
|
||||||
|
"description": "A multi-account desktop client for Mastodon and Pleroma",
|
||||||
|
"author": {
|
||||||
|
"name": "Nicolas Constant",
|
||||||
|
"email": "github@nicolas-constant.com"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/NicolasConstant/sengi.git"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
|
@ -12,7 +21,8 @@
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"e2e": "ng e2e",
|
"e2e": "ng e2e",
|
||||||
"electron": "ng build --prod && electron .",
|
"electron": "ng build --prod && electron .",
|
||||||
"electron-debug": "ng build && electron ."
|
"electron-debug": "ng build && electron .",
|
||||||
|
"dist": "npm run build && build --publish onTagOrDraft"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -34,7 +44,6 @@
|
||||||
"@ngxs/store": "^3.2.0",
|
"@ngxs/store": "^3.2.0",
|
||||||
"bootstrap": "^4.1.3",
|
"bootstrap": "^4.1.3",
|
||||||
"core-js": "^2.5.4",
|
"core-js": "^2.5.4",
|
||||||
"electron": "^4.0.6",
|
|
||||||
"ionicons": "^4.4.3",
|
"ionicons": "^4.4.3",
|
||||||
"rxjs": "^6.4.0",
|
"rxjs": "^6.4.0",
|
||||||
"tslib": "^1.9.0",
|
"tslib": "^1.9.0",
|
||||||
|
@ -49,6 +58,8 @@
|
||||||
"@types/jasminewd2": "~2.0.3",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
"@types/node": "~8.9.4",
|
"@types/node": "~8.9.4",
|
||||||
"codelyzer": "~4.2.1",
|
"codelyzer": "~4.2.1",
|
||||||
|
"electron": "^4.0.6",
|
||||||
|
"electron-builder": "^20.39.0",
|
||||||
"jasmine-core": "~2.99.1",
|
"jasmine-core": "~2.99.1",
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
"karma": "~1.7.1",
|
"karma": "~1.7.1",
|
||||||
|
@ -61,5 +72,57 @@
|
||||||
"ts-node": "~5.0.1",
|
"ts-node": "~5.0.1",
|
||||||
"tslint": "~5.9.1",
|
"tslint": "~5.9.1",
|
||||||
"typescript": "~3.2.4"
|
"typescript": "~3.2.4"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"jquery": "1.9.1 - 3",
|
||||||
|
"popper.js": "^1.14.7"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"productName": "Sengi",
|
||||||
|
"appId": "org.sengi.desktop",
|
||||||
|
"artifactName": "${productName}-${version}-${os}.${ext}",
|
||||||
|
"directories": {
|
||||||
|
"output": "release"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/",
|
||||||
|
"node_modules/",
|
||||||
|
"main-electron.js",
|
||||||
|
"package.json"
|
||||||
|
],
|
||||||
|
"dmg": {
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"x": 130,
|
||||||
|
"y": 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 410,
|
||||||
|
"y": 220,
|
||||||
|
"type": "link",
|
||||||
|
"path": "/Applications"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"icon": "assets/icons/mac/icon.icns",
|
||||||
|
"target": [
|
||||||
|
"dmg"
|
||||||
|
],
|
||||||
|
"category": "public.app-category.social-networking"
|
||||||
|
},
|
||||||
|
"win": {
|
||||||
|
"icon": "assets/icons/win/icon.ico",
|
||||||
|
"target": "nsis"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"icon": "assets/icons/png",
|
||||||
|
"target": [
|
||||||
|
"AppImage",
|
||||||
|
"deb",
|
||||||
|
"snap"
|
||||||
|
],
|
||||||
|
"category": "Network"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,10 +63,9 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
this.dragoverSub = this.dragoverSubject
|
this.dragoverSub = this.dragoverSubject
|
||||||
.pipe(
|
.pipe(
|
||||||
debounceTime(150)
|
debounceTime(1500)
|
||||||
)
|
)
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
console.warn('disable drag');
|
|
||||||
this.drag = false;
|
this.drag = false;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,8 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
private maxCharLength: number;
|
private maxCharLength: number;
|
||||||
charCountLeft: number;
|
charCountLeft: number;
|
||||||
postCounts: number = 1;
|
postCounts: number = 1;
|
||||||
|
|
||||||
isSending: boolean;
|
isSending: boolean;
|
||||||
|
mentionTooFarAwayError: boolean;
|
||||||
|
|
||||||
@Input() statusReplyingToWrapper: StatusWrapper;
|
@Input() statusReplyingToWrapper: StatusWrapper;
|
||||||
@Output() onClose = new EventEmitter();
|
@Output() onClose = new EventEmitter();
|
||||||
|
@ -76,6 +76,21 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
this.status += `@${mention} `;
|
this.status += `@${mention} `;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (this.statusReplyingTo.visibility) {
|
||||||
|
case 'unlisted':
|
||||||
|
this.setVisibility(VisibilityEnum.Unlisted);
|
||||||
|
break;
|
||||||
|
case 'public':
|
||||||
|
this.setVisibility(VisibilityEnum.Public);
|
||||||
|
break;
|
||||||
|
case 'private':
|
||||||
|
this.setVisibility(VisibilityEnum.Private);
|
||||||
|
break;
|
||||||
|
case 'direct':
|
||||||
|
this.setVisibility(VisibilityEnum.Direct);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
this.title = this.statusReplyingTo.spoiler_text;
|
this.title = this.statusReplyingTo.spoiler_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,8 +115,19 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
this.notificationService.notifyHttpError(err);
|
this.notificationService.notifyHttpError(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!this.statusReplyingToWrapper) {
|
||||||
this.instancesInfoService.getDefaultPrivacy(selectedAccount)
|
this.instancesInfoService.getDefaultPrivacy(selectedAccount)
|
||||||
.then((defaultPrivacy: VisibilityEnum) => {
|
.then((defaultPrivacy: VisibilityEnum) => {
|
||||||
|
this.setVisibility(defaultPrivacy);
|
||||||
|
})
|
||||||
|
.catch((err: HttpErrorResponse) => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setVisibility(defaultPrivacy: VisibilityEnum) {
|
||||||
switch (defaultPrivacy) {
|
switch (defaultPrivacy) {
|
||||||
case VisibilityEnum.Public:
|
case VisibilityEnum.Public:
|
||||||
this.selectedPrivacy = 'Public';
|
this.selectedPrivacy = 'Public';
|
||||||
|
@ -116,14 +142,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
this.selectedPrivacy = 'DM';
|
this.selectedPrivacy = 'DM';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch((err: HttpErrorResponse) => {
|
|
||||||
this.notificationService.notifyHttpError(err);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mentionTooFarAwayError: boolean;
|
|
||||||
|
|
||||||
private countStatusChar(status: string) {
|
private countStatusChar(status: string) {
|
||||||
this.mentionTooFarAwayError = false;
|
this.mentionTooFarAwayError = false;
|
||||||
|
@ -144,16 +163,6 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
this.postCounts = 1;
|
this.postCounts = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// const lastMention = mentions[mentions.length - 1];
|
|
||||||
// const lastMentionPosition = status.lastIndexOf(lastMention);
|
|
||||||
// console.warn(`lastMentionPosition ${lastMentionPosition}`);
|
|
||||||
// if(lastMentionPosition > (this.maxCharLength - lastMention.length * 2 + 10)){
|
|
||||||
// this.mentionTooFarAwayError = true;
|
|
||||||
// this.charCountLeft = this.maxCharLength - status.length;
|
|
||||||
// this.postCounts = 1;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentStatus = parseStatus[parseStatus.length - 1];
|
const currentStatus = parseStatus[parseStatus.length - 1];
|
||||||
|
@ -182,6 +191,9 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
globalUniqueMentions.push(mention);
|
globalUniqueMentions.push(mention);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedUser = this.toolsService.getSelectedAccounts()[0];
|
||||||
|
globalUniqueMentions = globalUniqueMentions.filter(x => x.toLowerCase() !== `${selectedUser.username}@${selectedUser.instance}`.toLowerCase());
|
||||||
|
|
||||||
return globalUniqueMentions;
|
return globalUniqueMentions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +208,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
this.isSending = true;
|
this.isSending = true;
|
||||||
|
|
||||||
let visibility: VisibilityEnum = VisibilityEnum.Unknown;
|
let visibility: VisibilityEnum = VisibilityEnum.Unknown;
|
||||||
switch (this.selectedPrivacy) { //FIXME: in case of responding, set the visibility to original
|
switch (this.selectedPrivacy) {
|
||||||
case 'Public':
|
case 'Public':
|
||||||
visibility = VisibilityEnum.Public;
|
visibility = VisibilityEnum.Public;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { faTimes } from "@fortawesome/free-solid-svg-icons";
|
import { faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
import { NavigationService, LeftPanelType } from '../../services/navigation.service';
|
import { NavigationService, LeftPanelType } from '../../services/navigation.service';
|
||||||
import { AccountWrapper } from '../../models/account.models';
|
import { AccountWrapper } from '../../models/account.models';
|
||||||
import { OpenThreadEvent } from '../../services/tools.service';
|
import { OpenThreadEvent } from '../../services/tools.service';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-floating-column',
|
selector: 'app-floating-column',
|
||||||
templateUrl: './floating-column.component.html',
|
templateUrl: './floating-column.component.html',
|
||||||
styleUrls: ['./floating-column.component.scss']
|
styleUrls: ['./floating-column.component.scss']
|
||||||
})
|
})
|
||||||
export class FloatingColumnComponent implements OnInit {
|
export class FloatingColumnComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
faTimes = faTimes;
|
faTimes = faTimes;
|
||||||
overlayActive: boolean;
|
overlayActive: boolean;
|
||||||
overlayAccountToBrowse: string;
|
overlayAccountToBrowse: string;
|
||||||
|
@ -19,31 +21,59 @@ export class FloatingColumnComponent implements OnInit {
|
||||||
|
|
||||||
userAccountUsed: AccountWrapper;
|
userAccountUsed: AccountWrapper;
|
||||||
|
|
||||||
openPanel: string;
|
openPanel: string = '';
|
||||||
|
|
||||||
|
private activatedPanelSub: Subscription;
|
||||||
|
|
||||||
constructor(private readonly navigationService: NavigationService) { }
|
constructor(private readonly navigationService: NavigationService) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.navigationService.activatedPanelSubject.subscribe((type: LeftPanelType) => {
|
this.activatedPanelSub = this.navigationService.activatedPanelSubject.subscribe((type: LeftPanelType) => {
|
||||||
|
this.overlayActive = false;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case LeftPanelType.Closed:
|
case LeftPanelType.Closed:
|
||||||
this.openPanel = '';
|
this.openPanel = '';
|
||||||
break;
|
break;
|
||||||
case LeftPanelType.AddNewAccount:
|
case LeftPanelType.AddNewAccount:
|
||||||
|
if (this.openPanel === 'addNewAccount') {
|
||||||
|
this.closePanel();
|
||||||
|
} else {
|
||||||
this.openPanel = 'addNewAccount';
|
this.openPanel = 'addNewAccount';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LeftPanelType.CreateNewStatus:
|
case LeftPanelType.CreateNewStatus:
|
||||||
|
if (this.openPanel === 'createNewStatus') {
|
||||||
|
this.closePanel();
|
||||||
|
} else {
|
||||||
this.openPanel = 'createNewStatus';
|
this.openPanel = 'createNewStatus';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LeftPanelType.ManageAccount:
|
case LeftPanelType.ManageAccount:
|
||||||
|
let lastUserAccountId = '';
|
||||||
|
if (this.userAccountUsed && this.userAccountUsed.info) {
|
||||||
|
lastUserAccountId = this.userAccountUsed.info.id;
|
||||||
|
}
|
||||||
this.userAccountUsed = this.navigationService.getAccountToManage();
|
this.userAccountUsed = this.navigationService.getAccountToManage();
|
||||||
|
|
||||||
|
if (this.openPanel === 'manageAccount' && this.userAccountUsed.info.id === lastUserAccountId) {
|
||||||
|
this.closePanel();
|
||||||
|
} else {
|
||||||
this.openPanel = 'manageAccount';
|
this.openPanel = 'manageAccount';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LeftPanelType.Search:
|
case LeftPanelType.Search:
|
||||||
|
if (this.openPanel === 'search') {
|
||||||
|
this.closePanel();
|
||||||
|
} else {
|
||||||
this.openPanel = 'search';
|
this.openPanel = 'search';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LeftPanelType.Settings:
|
case LeftPanelType.Settings:
|
||||||
|
if (this.openPanel === 'settings') {
|
||||||
|
this.closePanel();
|
||||||
|
} else {
|
||||||
this.openPanel = 'settings';
|
this.openPanel = 'settings';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.openPanel = '';
|
this.openPanel = '';
|
||||||
|
@ -51,6 +81,12 @@ export class FloatingColumnComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if(this.activatedPanelSub) {
|
||||||
|
this.activatedPanelSub.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
closePanel(): boolean {
|
closePanel(): boolean {
|
||||||
this.navigationService.closePanel();
|
this.navigationService.closePanel();
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<div class="my-account__body flexcroll">
|
<div class="my-account__body flexcroll">
|
||||||
<h4 class="my-account__label">add column:</h4>
|
<h4 class="my-account__label">add column:</h4>
|
||||||
<a class="my-account__link my-account__blue" href *ngFor="let stream of availableStreams" (click)="addStream(stream)">
|
<a class="my-account__link my-account__blue" href *ngFor="let stream of availableStreams"
|
||||||
{{ stream.name }}
|
(click)="addStream(stream)" title="{{ stream.isAdded ? '' : 'add timeline'}}" [class.my-account__link--disabled]="stream.isAdded">
|
||||||
|
{{ stream.name }} <fa-icon class="my-account__link--icon" *ngIf="stream.isAdded" [icon]="faCheckSquare"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
<h4 class="my-account__label my-account__margin-top">remove account from sengi:</h4>
|
<h4 class="my-account__label my-account__margin-top">remove account from sengi:</h4>
|
||||||
<a class="my-account__link my-account__red" href (click)="removeAccount()">
|
<a class="my-account__link my-account__red" href (click)="removeAccount()">
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
&__body {
|
&__body {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: calc(100%);
|
height: calc(100%);
|
||||||
// width: calc(100%);
|
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
font-size: $small-font-size;
|
font-size: $small-font-size;
|
||||||
|
@ -14,24 +13,11 @@
|
||||||
outline: 1px dotted greenyellow;
|
outline: 1px dotted greenyellow;
|
||||||
}
|
}
|
||||||
&__label {
|
&__label {
|
||||||
// text-decoration: underline;
|
|
||||||
font-size: $small-font-size;
|
font-size: $small-font-size;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
color: $font-color-secondary;
|
color: $font-color-secondary;
|
||||||
}
|
}
|
||||||
&__link {
|
|
||||||
text-decoration: none;
|
|
||||||
display: block; // width: calc(100% - 20px);
|
|
||||||
width: 100%; // height: 30px;
|
|
||||||
padding: 5px 10px; // border: solid 1px black;
|
|
||||||
&:not(:last-child) {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__margin-top {
|
|
||||||
margin-top: 25px;
|
|
||||||
}
|
|
||||||
&__blue {
|
&__blue {
|
||||||
background-color: $color-primary;
|
background-color: $color-primary;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
@ -47,4 +33,26 @@
|
||||||
background-color: lighten($red-button-color, 15);
|
background-color: lighten($red-button-color, 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&__link {
|
||||||
|
text-decoration: none;
|
||||||
|
display: block; // width: calc(100% - 20px);
|
||||||
|
width: 100%; // height: 30px;
|
||||||
|
padding: 5px 10px; // border: solid 1px black;
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
&--icon {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
&--disabled {
|
||||||
|
cursor: default;
|
||||||
|
background-color: darken($color-primary, 4);
|
||||||
|
&:hover {
|
||||||
|
background-color: darken($color-primary, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__margin-top {
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
import { Component, OnInit, Input } from '@angular/core';
|
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
|
||||||
import { Store } from '@ngxs/store';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
import { Store, Select } from '@ngxs/store';
|
||||||
|
import { faCheckSquare } from "@fortawesome/free-regular-svg-icons";
|
||||||
|
|
||||||
import { NotificationService } from '../../../../services/notification.service';
|
import { NotificationService } from '../../../../services/notification.service';
|
||||||
import { StreamElement, StreamTypeEnum, AddStream, RemoveAllStreams } from '../../../../states/streams.state';
|
import { StreamElement, StreamTypeEnum, AddStream, RemoveAllStreams } from '../../../../states/streams.state';
|
||||||
|
@ -12,11 +14,24 @@ import { NavigationService } from '../../../../services/navigation.service';
|
||||||
templateUrl: './my-account.component.html',
|
templateUrl: './my-account.component.html',
|
||||||
styleUrls: ['./my-account.component.scss']
|
styleUrls: ['./my-account.component.scss']
|
||||||
})
|
})
|
||||||
export class MyAccountComponent implements OnInit {
|
export class MyAccountComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
availableStreams: StreamElement[] = [];
|
faCheckSquare = faCheckSquare;
|
||||||
|
|
||||||
@Input() account: AccountWrapper;
|
availableStreams: StreamWrapper[] = [];
|
||||||
|
|
||||||
|
private _account: AccountWrapper;
|
||||||
|
@Input('account')
|
||||||
|
set account(acc: AccountWrapper) {
|
||||||
|
this._account = acc;
|
||||||
|
this.loadStreams(acc);
|
||||||
|
}
|
||||||
|
get account(): AccountWrapper {
|
||||||
|
return this._account;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Select(state => state.streamsstatemodel.streams) streamElements$: Observable<StreamElement[]>;
|
||||||
|
private streamChangedSub: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly store: Store,
|
private readonly store: Store,
|
||||||
|
@ -24,18 +39,40 @@ export class MyAccountComponent implements OnInit {
|
||||||
private notificationService: NotificationService) { }
|
private notificationService: NotificationService) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const instance = this.account.info.instance;
|
this.streamChangedSub = this.streamElements$.subscribe((streams: StreamElement[]) => {
|
||||||
this.availableStreams.length = 0;
|
this.loadStreams(this.account);
|
||||||
this.availableStreams.push(new StreamElement(StreamTypeEnum.global, 'Federated Timeline', this.account.info.id, null, null, instance));
|
});
|
||||||
this.availableStreams.push(new StreamElement(StreamTypeEnum.local, 'Local Timeline', this.account.info.id, null, null, instance));
|
|
||||||
this.availableStreams.push(new StreamElement(StreamTypeEnum.personnal, 'Home', this.account.info.id, null, null, instance));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addStream(stream: StreamElement): boolean {
|
ngOnDestroy(): void {
|
||||||
if (stream) {
|
if(this.streamChangedSub) {
|
||||||
|
this.streamChangedSub.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadStreams(account: AccountWrapper){
|
||||||
|
const instance = account.info.instance;
|
||||||
|
this.availableStreams.length = 0;
|
||||||
|
this.availableStreams.push(new StreamWrapper(new StreamElement(StreamTypeEnum.global, 'Federated Timeline', account.info.id, null, null, instance)));
|
||||||
|
this.availableStreams.push(new StreamWrapper(new StreamElement(StreamTypeEnum.local, 'Local Timeline', account.info.id, null, null, instance)));
|
||||||
|
this.availableStreams.push(new StreamWrapper(new StreamElement(StreamTypeEnum.personnal, 'Home', account.info.id, null, null, instance)));
|
||||||
|
|
||||||
|
const loadedStreams = <StreamElement[]>this.store.snapshot().streamsstatemodel.streams;
|
||||||
|
this.availableStreams.forEach(s => {
|
||||||
|
if(loadedStreams.find(x => x.id === s.id)){
|
||||||
|
s.isAdded = true;
|
||||||
|
} else {
|
||||||
|
s.isAdded = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addStream(stream: StreamWrapper): boolean {
|
||||||
|
if (stream && !stream.isAdded) {
|
||||||
this.store.dispatch([new AddStream(stream)]).toPromise()
|
this.store.dispatch([new AddStream(stream)]).toPromise()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.notificationService.notify(`stream added`, false);
|
stream.isAdded = true;
|
||||||
|
//this.notificationService.notify(`stream added`, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -48,3 +85,11 @@ export class MyAccountComponent implements OnInit {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StreamWrapper extends StreamElement {
|
||||||
|
constructor(stream: StreamElement) {
|
||||||
|
super(stream.type, stream.name, stream.accountId, stream.tag, stream.list, stream.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
isAdded: boolean;
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
<div class="left-bar">
|
<div class="left-bar">
|
||||||
<a class="left-bar-button left-bar-button--status left-bar-link" href title="write new message" (click)="createNewStatus()" *ngIf="hasAccounts">
|
<a class="left-bar-button left-bar-button--status left-bar-link" href title="write new message" (click)="createNewStatus()" (contextmenu)="createNewStatus()" *ngIf="hasAccounts">
|
||||||
<fa-icon [icon]="faCommentAlt"></fa-icon>
|
<fa-icon [icon]="faCommentAlt"></fa-icon>
|
||||||
<!-- <ion-icon name="md-send"></ion-icon> -->
|
<!-- <ion-icon name="md-send"></ion-icon> -->
|
||||||
</a>
|
</a>
|
||||||
<a class="left-bar-button left-bar-button--search left-bar-link" href title="search" (click)="openSearch()" *ngIf="hasAccounts">
|
<a class="left-bar-button left-bar-button--search left-bar-link" href title="search" (click)="openSearch()" (contextmenu)="openSearch()" *ngIf="hasAccounts">
|
||||||
<ion-icon name="md-search"></ion-icon>
|
<ion-icon name="md-search"></ion-icon>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@
|
||||||
</app-account-icon>
|
</app-account-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="left-bar-button left-bar-button--add left-bar-link" [ngClass]="{'no-accounts': hasAccounts === false }" href title="add new account" (click)="addNewAccount()">
|
<a class="left-bar-button left-bar-button--add left-bar-link" [ngClass]="{'no-accounts': hasAccounts === false }" href title="add new account" (click)="addNewAccount()" (contextmenu)="addNewAccount()">
|
||||||
<ion-icon name="md-add"></ion-icon>
|
<ion-icon name="md-add"></ion-icon>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="left-bar-button left-bar-button--cog left-bar-link" href title="settings" (click)="openSettings()" *ngIf="hasAccounts">
|
<a class="left-bar-button left-bar-button--cog left-bar-link" href title="settings" (click)="openSettings()" (contextmenu)="openSettings()" *ngIf="hasAccounts">
|
||||||
<ion-icon name="md-cog"></ion-icon>
|
<ion-icon name="md-cog"></ion-icon>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
|
@ -4,7 +4,9 @@
|
||||||
</a>
|
</a>
|
||||||
<ion-icon *ngIf="isLocked" class="action-bar__lock" name="lock" title="Account can't access this post"></ion-icon>
|
<ion-icon *ngIf="isLocked" class="action-bar__lock" name="lock" title="Account can't access this post"></ion-icon>
|
||||||
|
|
||||||
<a *ngIf="!(isBoostLocked || isLocked)" href class="action-bar__link" title="Boost" [class.boosted]="isBoosted"
|
<a *ngIf="!(isBoostLocked || isLocked)" href class="action-bar__link" title="Boost"
|
||||||
|
[class.boosted]="isBoosted"
|
||||||
|
[class.boosting]="boostIsLoading"
|
||||||
(click)="boost()">
|
(click)="boost()">
|
||||||
<ion-icon name="md-swap"></ion-icon>
|
<ion-icon name="md-swap"></ion-icon>
|
||||||
</a>
|
</a>
|
||||||
|
@ -12,7 +14,9 @@
|
||||||
title="This post cannot be boosted"></ion-icon>
|
title="This post cannot be boosted"></ion-icon>
|
||||||
<ion-icon *ngIf="isLocked" class="action-bar__lock" name="lock" title="Account can't access this post"></ion-icon>
|
<ion-icon *ngIf="isLocked" class="action-bar__lock" name="lock" title="Account can't access this post"></ion-icon>
|
||||||
|
|
||||||
<a *ngIf="!isLocked" href class="action-bar__link" title="Favourite" [class.favorited]="isFavorited"
|
<a *ngIf="!isLocked" href class="action-bar__link" title="Favourite"
|
||||||
|
[class.favorited]="isFavorited"
|
||||||
|
[class.favoriting]="favoriteIsLoading"
|
||||||
(click)="favorite()">
|
(click)="favorite()">
|
||||||
<ion-icon name="md-star"></ion-icon>
|
<ion-icon name="md-star"></ion-icon>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
.action-bar {
|
.action-bar {
|
||||||
// outline: 1px solid greenyellow; // height: 20px;
|
// outline: 1px solid greenyellow; // height: 20px;
|
||||||
margin: 5px 10px 5px $avatar-column-space;
|
margin: 5px 10px 5px $avatar-column-space;
|
||||||
|
@ -9,9 +10,11 @@
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
color: $status-secondary-color;
|
color: $status-secondary-color;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $status-links-color;
|
color: $status-links-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
|
@ -63,3 +66,69 @@
|
||||||
color: darken($favorite-color, 10);
|
color: darken($favorite-color, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes loadingAnimation {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-o-keyframes loadingAnimation {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-moz-keyframes loadingAnimation {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes loadingAnimation {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.boosting,
|
||||||
|
.favoriting {
|
||||||
|
-webkit-animation: loadingAnimation 1s infinite;
|
||||||
|
-moz-animation: loadingAnimation 1s infinite;
|
||||||
|
-o-animation: loadingAnimation 1s infinite;
|
||||||
|
animation: loadingAnimation 1s infinite;
|
||||||
|
|
||||||
|
}
|
|
@ -34,6 +34,9 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
||||||
isBoostLocked: boolean;
|
isBoostLocked: boolean;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
|
||||||
|
favoriteIsLoading: boolean;
|
||||||
|
boostIsLoading: boolean;
|
||||||
|
|
||||||
isContentWarningActive: boolean = false;
|
isContentWarningActive: boolean = false;
|
||||||
|
|
||||||
private isProviderSelected: boolean;
|
private isProviderSelected: boolean;
|
||||||
|
@ -57,8 +60,14 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const status = this.statusWrapper.status;
|
const status = this.statusWrapper.status;
|
||||||
const account = this.statusWrapper.provider;
|
const account = this.statusWrapper.provider;
|
||||||
|
|
||||||
|
if(status.reblog){
|
||||||
|
this.favoriteStatePerAccountId[account.id] = status.reblog.favourited;
|
||||||
|
this.bootedStatePerAccountId[account.id] = status.reblog.reblogged;
|
||||||
|
} 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.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
||||||
this.checkStatus(accounts);
|
this.checkStatus(accounts);
|
||||||
|
@ -113,9 +122,10 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
boost(): boolean {
|
boost(): boolean {
|
||||||
//TODO get rid of that
|
if(this.boostIsLoading) return;
|
||||||
this.selectedAccounts.forEach((account: AccountInfo) => {
|
|
||||||
|
|
||||||
|
this.boostIsLoading = true;
|
||||||
|
const account = this.toolsService.getSelectedAccounts()[0];
|
||||||
const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper);
|
const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper);
|
||||||
usableStatus
|
usableStatus
|
||||||
.then((status: Status) => {
|
.then((status: Status) => {
|
||||||
|
@ -130,19 +140,22 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
||||||
.then((boostedStatus: Status) => {
|
.then((boostedStatus: Status) => {
|
||||||
this.bootedStatePerAccountId[account.id] = boostedStatus.reblogged;
|
this.bootedStatePerAccountId[account.id] = boostedStatus.reblogged;
|
||||||
this.checkIfBoosted();
|
this.checkIfBoosted();
|
||||||
// this.isBoosted = !this.isBoosted;
|
|
||||||
})
|
})
|
||||||
.catch((err: HttpErrorResponse) => {
|
.catch((err: HttpErrorResponse) => {
|
||||||
this.notificationService.notifyHttpError(err);
|
this.notificationService.notifyHttpError(err);
|
||||||
});
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.boostIsLoading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
favorite(): boolean {
|
favorite(): boolean {
|
||||||
this.selectedAccounts.forEach((account: AccountInfo) => {
|
if(this.favoriteIsLoading) return;
|
||||||
|
|
||||||
|
this.favoriteIsLoading = true;
|
||||||
|
const account = this.toolsService.getSelectedAccounts()[0];
|
||||||
const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper);
|
const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper);
|
||||||
usableStatus
|
usableStatus
|
||||||
.then((status: Status) => {
|
.then((status: Status) => {
|
||||||
|
@ -161,8 +174,11 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
||||||
})
|
})
|
||||||
.catch((err: HttpErrorResponse) => {
|
.catch((err: HttpErrorResponse) => {
|
||||||
this.notificationService.notifyHttpError(err);
|
this.notificationService.notifyHttpError(err);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.favoriteIsLoading = false;
|
||||||
});
|
});
|
||||||
});
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,9 +205,4 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
||||||
console.warn('more'); //TODO
|
console.warn('more'); //TODO
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private getSelectedAccounts(): AccountInfo[] {
|
|
||||||
// var regAccounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
|
|
||||||
// return regAccounts;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<div class="profile flexcroll">
|
<div class="profile flexcroll" #statusstream (scroll)="onScroll()">
|
||||||
<app-waiting-animation *ngIf="isLoading" class="waiting-icon"></app-waiting-animation>
|
<app-waiting-animation *ngIf="isLoading" class="waiting-icon"></app-waiting-animation>
|
||||||
|
|
||||||
|
|
||||||
<div *ngIf="account" class="profile-header" [ngStyle]="{'background-image':'url('+account.header+')'}">
|
<div *ngIf="displayedAccount" class="profile-header" [ngStyle]="{'background-image':'url('+displayedAccount.header+')'}">
|
||||||
<div class="profile-header__inner">
|
<div class="profile-header__inner">
|
||||||
<!-- <img class="profile-header__header" src="{{account.header}}" alt="header" /> -->
|
<img class="profile-header__avatar" src="{{displayedAccount.avatar}}" alt="header" />
|
||||||
<img class="profile-header__avatar" src="{{account.avatar}}" alt="header" />
|
<h2 class="profile-header__display-name">{{displayedAccount.display_name}}</h2>
|
||||||
<h2 class="profile-header__display-name">{{account.display_name}}</h2>
|
<h2 class="profile-header__fullhandle"><a href="{{displayedAccount.url}}" target="_blank">@{{displayedAccount.acct}}</a></h2>
|
||||||
<h2 class="profile-header__fullhandle"><a href="{{account.url}}" target="_blank">@{{account.acct}}</a></h2>
|
|
||||||
|
|
||||||
<div class="profile-header__follow" *ngIf="relationship">
|
<div class="profile-header__follow" *ngIf="relationship">
|
||||||
<button class="profile-header__follow--button profile-header__follow--unfollowed" title="follow"
|
<button class="profile-header__follow--button profile-header__follow--unfollowed" title="follow"
|
||||||
|
@ -32,14 +31,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-sub-header ">
|
<div class="profile-sub-header ">
|
||||||
<div *ngIf="account && hasNote" class="profile-description">
|
<div *ngIf="displayedAccount && hasNote" class="profile-description">
|
||||||
<!-- <div *ngIf="account && account.note" class="profile-description"> -->
|
<app-databinded-text class="profile-description__content" [textIsSelectable]="false" [text]="displayedAccount.note"
|
||||||
<app-databinded-text class="profile-description__content" [textIsSelectable]="false" [text]="account.note"
|
|
||||||
(accountSelected)="browseAccount($event)" (hashtagSelected)="browseHashtag($event)">
|
(accountSelected)="browseAccount($event)" (hashtagSelected)="browseHashtag($event)">
|
||||||
</app-databinded-text>
|
</app-databinded-text>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-fields" *ngIf="account && account.fields.length > 0">
|
<div class="profile-fields" *ngIf="displayedAccount && displayedAccount.fields.length > 0">
|
||||||
<div class="profile-fields__field" *ngFor="let field of account.fields">
|
<div class="profile-fields__field" *ngFor="let field of displayedAccount.fields">
|
||||||
<div class="profile-fields__field--value" innerHTML="{{field.value}}" [ngClass]="{'profile-fields__field--validated': field.verified_at }">
|
<div class="profile-fields__field--value" innerHTML="{{field.value}}" [ngClass]="{'profile-fields__field--validated': field.verified_at }">
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-fields__field--name">
|
<div class="profile-fields__field--name">
|
||||||
|
@ -49,8 +47,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="profile-statuses">
|
<div class="profile-statuses">
|
||||||
<app-waiting-animation *ngIf="statusLoading" class="waiting-icon"></app-waiting-animation>
|
|
||||||
|
|
||||||
<div *ngIf="!isLoading && !statusLoading && statuses.length == 0" class="profile-no-toots">
|
<div *ngIf="!isLoading && !statusLoading && statuses.length == 0" class="profile-no-toots">
|
||||||
no toots found
|
no toots found
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,6 +56,9 @@
|
||||||
(browseAccountEvent)="browseAccount($event)" (browseThreadEvent)="browseThread($event)">
|
(browseAccountEvent)="browseAccount($event)" (browseThreadEvent)="browseThread($event)">
|
||||||
</app-status>
|
</app-status>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<app-waiting-animation *ngIf="statusLoading" class="waiting-icon"></app-waiting-animation>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
import { faUser, faHourglassHalf, faUserCheck } from "@fortawesome/free-solid-svg-icons";
|
import { faUser, faHourglassHalf, faUserCheck } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { faUser as faUserRegular } from "@fortawesome/free-regular-svg-icons";
|
import { faUser as faUserRegular } from "@fortawesome/free-regular-svg-icons";
|
||||||
|
@ -25,10 +25,13 @@ export class UserProfileComponent implements OnInit {
|
||||||
faHourglassHalf = faHourglassHalf;
|
faHourglassHalf = faHourglassHalf;
|
||||||
faUserCheck = faUserCheck;
|
faUserCheck = faUserCheck;
|
||||||
|
|
||||||
account: Account;
|
displayedAccount: Account;
|
||||||
hasNote: boolean;
|
hasNote: boolean;
|
||||||
|
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
|
||||||
|
private maxReached = false;
|
||||||
|
private maxId: string;
|
||||||
statusLoading: boolean;
|
statusLoading: boolean;
|
||||||
error: string;
|
error: string;
|
||||||
|
|
||||||
|
@ -41,6 +44,8 @@ export class UserProfileComponent implements OnInit {
|
||||||
private accounts$: Observable<AccountInfo[]>;
|
private accounts$: Observable<AccountInfo[]>;
|
||||||
private accountSub: Subscription;
|
private accountSub: Subscription;
|
||||||
|
|
||||||
|
@ViewChild('statusstream') public statustream: ElementRef;
|
||||||
|
|
||||||
@Output() browseAccountEvent = new EventEmitter<string>();
|
@Output() browseAccountEvent = new EventEmitter<string>();
|
||||||
@Output() browseHashtagEvent = new EventEmitter<string>();
|
@Output() browseHashtagEvent = new EventEmitter<string>();
|
||||||
@Output() browseThreadEvent = new EventEmitter<OpenThreadEvent>();
|
@Output() browseThreadEvent = new EventEmitter<OpenThreadEvent>();
|
||||||
|
@ -61,12 +66,12 @@ export class UserProfileComponent implements OnInit {
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
||||||
if (this.account) {
|
if (this.displayedAccount) {
|
||||||
this.currentlyUsedAccount = accounts.filter(x => x.isSelected)[0];
|
const userAccount = accounts.filter(x => x.isSelected)[0];
|
||||||
|
|
||||||
this.toolsService.findAccount(this.currentlyUsedAccount, this.lastAccountName)
|
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
||||||
.then((account: Account) => {
|
.then((account: Account) => {
|
||||||
this.getFollowStatus(this.currentlyUsedAccount, account);
|
this.getFollowStatus(userAccount, account);
|
||||||
})
|
})
|
||||||
.catch((err: HttpErrorResponse) => {
|
.catch((err: HttpErrorResponse) => {
|
||||||
this.notificationService.notifyHttpError(err);
|
this.notificationService.notifyHttpError(err);
|
||||||
|
@ -82,7 +87,7 @@ export class UserProfileComponent implements OnInit {
|
||||||
private load(accountName: string) {
|
private load(accountName: string) {
|
||||||
this.statuses.length = 0;
|
this.statuses.length = 0;
|
||||||
|
|
||||||
this.account = null;
|
this.displayedAccount = null;
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
this.lastAccountName = accountName;
|
this.lastAccountName = accountName;
|
||||||
|
@ -93,11 +98,11 @@ export class UserProfileComponent implements OnInit {
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.statusLoading = true;
|
this.statusLoading = true;
|
||||||
|
|
||||||
this.account = account;
|
this.displayedAccount = account;
|
||||||
this.hasNote = account && account.note && account.note !== '<p></p>';
|
this.hasNote = account && account.note && account.note !== '<p></p>';
|
||||||
|
|
||||||
const getFollowStatusPromise = this.getFollowStatus(this.currentlyUsedAccount, this.account);
|
const getFollowStatusPromise = this.getFollowStatus(this.currentlyUsedAccount, this.displayedAccount);
|
||||||
const getStatusesPromise = this.getStatuses(this.currentlyUsedAccount, this.account);
|
const getStatusesPromise = this.getStatuses(this.currentlyUsedAccount, this.displayedAccount);
|
||||||
|
|
||||||
return Promise.all([getFollowStatusPromise, getStatusesPromise]);
|
return Promise.all([getFollowStatusPromise, getStatusesPromise]);
|
||||||
})
|
})
|
||||||
|
@ -113,11 +118,25 @@ export class UserProfileComponent implements OnInit {
|
||||||
private getStatuses(userAccount: AccountInfo, account: Account): Promise<void> {
|
private getStatuses(userAccount: AccountInfo, account: Account): 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, false, false, true, null, null, 40)
|
||||||
.then((result: Status[]) => {
|
.then((statuses: Status[]) => {
|
||||||
for (const status of result) {
|
this.loadStatus(userAccount, statuses);
|
||||||
const wrapper = new StatusWrapper(status, userAccount);
|
|
||||||
this.statuses.push(wrapper);
|
// if (statuses.length === 0) {
|
||||||
}
|
// this.maxReached = true;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (const status of statuses) {
|
||||||
|
// const wrapper = new StatusWrapper(status, userAccount);
|
||||||
|
// this.statuses.push(wrapper);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// this.maxId = this.statuses[this.statuses.length - 1].status.id;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
this.statusLoading = false;
|
this.statusLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -147,10 +166,10 @@ export class UserProfileComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
follow(): boolean {
|
follow(): boolean {
|
||||||
this.currentlyUsedAccount = this.toolsService.getSelectedAccounts()[0];
|
const userAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
this.toolsService.findAccount(this.currentlyUsedAccount, this.lastAccountName)
|
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
||||||
.then((account: Account) => {
|
.then((account: Account) => {
|
||||||
return this.mastodonService.follow(this.currentlyUsedAccount, account);
|
return this.mastodonService.follow(userAccount, account);
|
||||||
})
|
})
|
||||||
.then((relationship: Relationship) => {
|
.then((relationship: Relationship) => {
|
||||||
this.relationship = relationship;
|
this.relationship = relationship;
|
||||||
|
@ -162,10 +181,10 @@ export class UserProfileComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
unfollow(): boolean {
|
unfollow(): boolean {
|
||||||
this.currentlyUsedAccount = this.toolsService.getSelectedAccounts()[0];
|
const userAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
this.toolsService.findAccount(this.currentlyUsedAccount, this.lastAccountName)
|
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
||||||
.then((account: Account) => {
|
.then((account: Account) => {
|
||||||
return this.mastodonService.unfollow(this.currentlyUsedAccount, account);
|
return this.mastodonService.unfollow(userAccount, account);
|
||||||
})
|
})
|
||||||
.then((relationship: Relationship) => {
|
.then((relationship: Relationship) => {
|
||||||
this.relationship = relationship;
|
this.relationship = relationship;
|
||||||
|
@ -175,4 +194,44 @@ export class UserProfileComponent implements OnInit {
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onScroll() {
|
||||||
|
var element = this.statustream.nativeElement as HTMLElement;
|
||||||
|
const atBottom = element.scrollHeight <= element.clientHeight + element.scrollTop + 1000;
|
||||||
|
|
||||||
|
if (atBottom) {
|
||||||
|
this.scrolledToBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private scrolledToBottom() {
|
||||||
|
if (this.statusLoading || this.maxReached) return;
|
||||||
|
|
||||||
|
this.statusLoading = true;
|
||||||
|
const userAccount = this.currentlyUsedAccount;
|
||||||
|
this.mastodonService.getAccountStatuses(userAccount, this.displayedAccount.id, false, false, true, this.maxId, null, 40)
|
||||||
|
.then((statuses: Status[]) => {
|
||||||
|
this.loadStatus(userAccount, statuses);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.notificationService.notifyHttpError(err);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.statusLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadStatus(userAccount: AccountInfo, statuses: Status[]){
|
||||||
|
if (statuses.length === 0) {
|
||||||
|
this.maxReached = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const status of statuses) {
|
||||||
|
const wrapper = new StatusWrapper(status, userAccount);
|
||||||
|
this.statuses.push(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.maxId = this.statuses[this.statuses.length - 1].status.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,9 @@ export class MediaService {
|
||||||
|
|
||||||
let medias = this.mediaSubject.value;
|
let medias = this.mediaSubject.value;
|
||||||
medias.push(wrapper);
|
medias.push(wrapper);
|
||||||
|
if(medias.length > 4){
|
||||||
|
medias.splice(0, 1);
|
||||||
|
}
|
||||||
this.mediaSubject.next(medias);
|
this.mediaSubject.next(medias);
|
||||||
|
|
||||||
this.mastodonService.uploadMediaAttachment(account, file, null)
|
this.mastodonService.uploadMediaAttachment(account, file, null)
|
||||||
|
|
|
@ -157,7 +157,7 @@ export interface Status {
|
||||||
favourited: boolean;
|
favourited: boolean;
|
||||||
sensitive: boolean;
|
sensitive: boolean;
|
||||||
spoiler_text: string;
|
spoiler_text: string;
|
||||||
visibility: string;
|
visibility: 'public' | 'unlisted' | 'private' | 'direct';
|
||||||
media_attachments: Attachment[];
|
media_attachments: Attachment[];
|
||||||
mentions: Mention[];
|
mentions: Mention[];
|
||||||
tags: Tag[];
|
tags: Tag[];
|
||||||
|
|
|
@ -44,10 +44,10 @@ export class ToolsService {
|
||||||
.then((result: Results) => {
|
.then((result: Results) => {
|
||||||
if(accountName[0] === '@') accountName = accountName.substr(1);
|
if(accountName[0] === '@') accountName = accountName.substr(1);
|
||||||
|
|
||||||
const foundAccount = result.accounts.filter(
|
const foundAccount = result.accounts.find(
|
||||||
x => x.acct.toLowerCase() === accountName.toLowerCase()
|
x => x.acct.toLowerCase() === accountName.toLowerCase()
|
||||||
|| x.acct.toLowerCase() === accountName.toLowerCase().split('@')[0]
|
|| x.acct.toLowerCase().split('@')[0] === accountName.toLowerCase().split('@')[0]
|
||||||
)[0];
|
);
|
||||||
return foundAccount;
|
return foundAccount;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|