|
@ -2,6 +2,7 @@
|
|||
/release
|
||||
|
||||
# compiled output
|
||||
/release
|
||||
/dist
|
||||
/dist-server
|
||||
/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() {
|
||||
// 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);
|
||||
win.loadURL('http://localhost:9527');
|
||||
|
|
189
package.json
|
@ -1,65 +1,128 @@
|
|||
{
|
||||
"name": "sengi",
|
||||
"version": "0.0.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "main-electron.js",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build --prod",
|
||||
"test": "ng test",
|
||||
"test-nowatch": "ng test --watch=false",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e",
|
||||
"electron": "ng build --prod && electron .",
|
||||
"electron-debug": "ng build && electron ."
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^7.2.7",
|
||||
"@angular/common": "^7.2.7",
|
||||
"@angular/compiler": "^7.2.7",
|
||||
"@angular/core": "^7.2.7",
|
||||
"@angular/forms": "^7.2.7",
|
||||
"@angular/http": "^7.2.7",
|
||||
"@angular/platform-browser": "^7.2.7",
|
||||
"@angular/platform-browser-dynamic": "^7.2.7",
|
||||
"@angular/router": "^7.2.7",
|
||||
"@fortawesome/angular-fontawesome": "^0.3.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.13",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.7.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.7.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.7.0",
|
||||
"@ngxs/storage-plugin": "^3.2.0",
|
||||
"@ngxs/store": "^3.2.0",
|
||||
"bootstrap": "^4.1.3",
|
||||
"core-js": "^2.5.4",
|
||||
"electron": "^4.0.6",
|
||||
"ionicons": "^4.4.3",
|
||||
"rxjs": "^6.4.0",
|
||||
"tslib": "^1.9.0",
|
||||
"zone.js": "^0.8.29"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.13.0",
|
||||
"@angular/cli": "~7.3.3",
|
||||
"@angular/compiler-cli": "^7.2.7",
|
||||
"@angular/language-service": "^7.2.7",
|
||||
"@types/jasmine": "~2.8.6",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "~8.9.4",
|
||||
"codelyzer": "~4.2.1",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~1.7.1",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.0",
|
||||
"karma-jasmine": "~1.1.0",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"karma-junit-reporter": "^1.2.0",
|
||||
"protractor": "~5.3.0",
|
||||
"ts-node": "~5.0.1",
|
||||
"tslint": "~5.9.1",
|
||||
"typescript": "~3.2.4"
|
||||
}
|
||||
"name": "sengi",
|
||||
"version": "0.6.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"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": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build --prod",
|
||||
"test": "ng test",
|
||||
"test-nowatch": "ng test --watch=false",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e",
|
||||
"electron": "ng build --prod && electron .",
|
||||
"electron-debug": "ng build && electron .",
|
||||
"dist": "npm run build && build --publish onTagOrDraft"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^7.2.7",
|
||||
"@angular/common": "^7.2.7",
|
||||
"@angular/compiler": "^7.2.7",
|
||||
"@angular/core": "^7.2.7",
|
||||
"@angular/forms": "^7.2.7",
|
||||
"@angular/http": "^7.2.7",
|
||||
"@angular/platform-browser": "^7.2.7",
|
||||
"@angular/platform-browser-dynamic": "^7.2.7",
|
||||
"@angular/router": "^7.2.7",
|
||||
"@fortawesome/angular-fontawesome": "^0.3.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.13",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.7.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.7.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.7.0",
|
||||
"@ngxs/storage-plugin": "^3.2.0",
|
||||
"@ngxs/store": "^3.2.0",
|
||||
"bootstrap": "^4.1.3",
|
||||
"core-js": "^2.5.4",
|
||||
"ionicons": "^4.4.3",
|
||||
"rxjs": "^6.4.0",
|
||||
"tslib": "^1.9.0",
|
||||
"zone.js": "^0.8.29"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.13.0",
|
||||
"@angular/cli": "~7.3.3",
|
||||
"@angular/compiler-cli": "^7.2.7",
|
||||
"@angular/language-service": "^7.2.7",
|
||||
"@types/jasmine": "~2.8.6",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "~8.9.4",
|
||||
"codelyzer": "~4.2.1",
|
||||
"electron": "^4.0.6",
|
||||
"electron-builder": "^20.39.0",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~1.7.1",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.0",
|
||||
"karma-jasmine": "~1.1.0",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"karma-junit-reporter": "^1.2.0",
|
||||
"protractor": "~5.3.0",
|
||||
"ts-node": "~5.0.1",
|
||||
"tslint": "~5.9.1",
|
||||
"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
|
||||
.pipe(
|
||||
debounceTime(150)
|
||||
debounceTime(1500)
|
||||
)
|
||||
.subscribe(() => {
|
||||
console.warn('disable drag');
|
||||
.subscribe(() => {
|
||||
this.drag = false;
|
||||
})
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
private maxCharLength: number;
|
||||
charCountLeft: number;
|
||||
postCounts: number = 1;
|
||||
|
||||
isSending: boolean;
|
||||
mentionTooFarAwayError: boolean;
|
||||
|
||||
@Input() statusReplyingToWrapper: StatusWrapper;
|
||||
@Output() onClose = new EventEmitter();
|
||||
|
@ -76,6 +76,21 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -100,60 +115,54 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
this.notificationService.notifyHttpError(err);
|
||||
});
|
||||
|
||||
this.instancesInfoService.getDefaultPrivacy(selectedAccount)
|
||||
.then((defaultPrivacy: VisibilityEnum) => {
|
||||
switch (defaultPrivacy) {
|
||||
case VisibilityEnum.Public:
|
||||
this.selectedPrivacy = 'Public';
|
||||
break;
|
||||
case VisibilityEnum.Unlisted:
|
||||
this.selectedPrivacy = 'Unlisted';
|
||||
break;
|
||||
case VisibilityEnum.Private:
|
||||
this.selectedPrivacy = 'Follows-only';
|
||||
break;
|
||||
case VisibilityEnum.Direct:
|
||||
this.selectedPrivacy = 'DM';
|
||||
break;
|
||||
}
|
||||
})
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
this.notificationService.notifyHttpError(err);
|
||||
});
|
||||
if (!this.statusReplyingToWrapper) {
|
||||
this.instancesInfoService.getDefaultPrivacy(selectedAccount)
|
||||
.then((defaultPrivacy: VisibilityEnum) => {
|
||||
this.setVisibility(defaultPrivacy);
|
||||
})
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
this.notificationService.notifyHttpError(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mentionTooFarAwayError: boolean;
|
||||
private setVisibility(defaultPrivacy: VisibilityEnum) {
|
||||
switch (defaultPrivacy) {
|
||||
case VisibilityEnum.Public:
|
||||
this.selectedPrivacy = 'Public';
|
||||
break;
|
||||
case VisibilityEnum.Unlisted:
|
||||
this.selectedPrivacy = 'Unlisted';
|
||||
break;
|
||||
case VisibilityEnum.Private:
|
||||
this.selectedPrivacy = 'Follows-only';
|
||||
break;
|
||||
case VisibilityEnum.Direct:
|
||||
this.selectedPrivacy = 'DM';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private countStatusChar(status: string) {
|
||||
this.mentionTooFarAwayError = false;
|
||||
const parseStatus = this.parseStatus(status);
|
||||
|
||||
const mentions = this.getMentionsFromStatus(status);
|
||||
if(mentions.length > 0){
|
||||
if (mentions.length > 0) {
|
||||
let containAllMention = true;
|
||||
mentions.forEach(m => {
|
||||
if(!parseStatus[0].includes(m)){
|
||||
if (!parseStatus[0].includes(m)) {
|
||||
containAllMention = false;
|
||||
}
|
||||
});
|
||||
|
||||
if(!containAllMention){
|
||||
if (!containAllMention) {
|
||||
this.mentionTooFarAwayError = true;
|
||||
this.charCountLeft = this.maxCharLength - status.length;
|
||||
this.postCounts = 1;
|
||||
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];
|
||||
|
@ -182,6 +191,9 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
globalUniqueMentions.push(mention);
|
||||
}
|
||||
|
||||
const selectedUser = this.toolsService.getSelectedAccounts()[0];
|
||||
globalUniqueMentions = globalUniqueMentions.filter(x => x.toLowerCase() !== `${selectedUser.username}@${selectedUser.instance}`.toLowerCase());
|
||||
|
||||
return globalUniqueMentions;
|
||||
}
|
||||
|
||||
|
@ -196,7 +208,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
this.isSending = true;
|
||||
|
||||
let visibility: VisibilityEnum = VisibilityEnum.Unknown;
|
||||
switch (this.selectedPrivacy) { //FIXME: in case of responding, set the visibility to original
|
||||
switch (this.selectedPrivacy) {
|
||||
case 'Public':
|
||||
visibility = VisibilityEnum.Public;
|
||||
break;
|
||||
|
@ -275,7 +287,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
let aggregateMention = '';
|
||||
let mentions = this.getMentionsFromStatus(status);
|
||||
mentions.forEach(x => {
|
||||
aggregateMention += `${x} `;
|
||||
aggregateMention += `${x} `;
|
||||
});
|
||||
|
||||
const currentMaxCharLength = this.maxCharLength + mentionExtraChars;
|
||||
|
@ -290,7 +302,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
return results;
|
||||
}
|
||||
|
||||
private getMentionExtraChars(status: string): number{
|
||||
private getMentionExtraChars(status: string): number {
|
||||
let mentionExtraChars = 0;
|
||||
let mentions = this.getMentionsFromStatus(status);
|
||||
|
||||
|
@ -305,7 +317,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
|||
return mentionExtraChars;
|
||||
}
|
||||
|
||||
private getMentionsFromStatus(status: string): string[]{
|
||||
private getMentionsFromStatus(status: string): string[] {
|
||||
return status.split(' ').filter(x => x.indexOf('@') === 0 && x.length > 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 { NavigationService, LeftPanelType } from '../../services/navigation.service';
|
||||
import { AccountWrapper } from '../../models/account.models';
|
||||
import { OpenThreadEvent } from '../../services/tools.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-floating-column',
|
||||
templateUrl: './floating-column.component.html',
|
||||
styleUrls: ['./floating-column.component.scss']
|
||||
})
|
||||
export class FloatingColumnComponent implements OnInit {
|
||||
export class FloatingColumnComponent implements OnInit, OnDestroy {
|
||||
|
||||
faTimes = faTimes;
|
||||
overlayActive: boolean;
|
||||
overlayAccountToBrowse: string;
|
||||
|
@ -19,31 +21,59 @@ export class FloatingColumnComponent implements OnInit {
|
|||
|
||||
userAccountUsed: AccountWrapper;
|
||||
|
||||
openPanel: string;
|
||||
openPanel: string = '';
|
||||
|
||||
private activatedPanelSub: Subscription;
|
||||
|
||||
constructor(private readonly navigationService: NavigationService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.navigationService.activatedPanelSubject.subscribe((type: LeftPanelType) => {
|
||||
this.activatedPanelSub = this.navigationService.activatedPanelSubject.subscribe((type: LeftPanelType) => {
|
||||
this.overlayActive = false;
|
||||
switch (type) {
|
||||
case LeftPanelType.Closed:
|
||||
this.openPanel = '';
|
||||
break;
|
||||
case LeftPanelType.AddNewAccount:
|
||||
this.openPanel = 'addNewAccount';
|
||||
if (this.openPanel === 'addNewAccount') {
|
||||
this.closePanel();
|
||||
} else {
|
||||
this.openPanel = 'addNewAccount';
|
||||
}
|
||||
break;
|
||||
case LeftPanelType.CreateNewStatus:
|
||||
this.openPanel = 'createNewStatus';
|
||||
if (this.openPanel === 'createNewStatus') {
|
||||
this.closePanel();
|
||||
} else {
|
||||
this.openPanel = 'createNewStatus';
|
||||
}
|
||||
break;
|
||||
case LeftPanelType.ManageAccount:
|
||||
this.userAccountUsed = this.navigationService.getAccountToManage();
|
||||
this.openPanel = 'manageAccount';
|
||||
let lastUserAccountId = '';
|
||||
if (this.userAccountUsed && this.userAccountUsed.info) {
|
||||
lastUserAccountId = this.userAccountUsed.info.id;
|
||||
}
|
||||
this.userAccountUsed = this.navigationService.getAccountToManage();
|
||||
|
||||
if (this.openPanel === 'manageAccount' && this.userAccountUsed.info.id === lastUserAccountId) {
|
||||
this.closePanel();
|
||||
} else {
|
||||
this.openPanel = 'manageAccount';
|
||||
}
|
||||
break;
|
||||
case LeftPanelType.Search:
|
||||
this.openPanel = 'search';
|
||||
if (this.openPanel === 'search') {
|
||||
this.closePanel();
|
||||
} else {
|
||||
this.openPanel = 'search';
|
||||
}
|
||||
break;
|
||||
case LeftPanelType.Settings:
|
||||
this.openPanel = 'settings';
|
||||
if (this.openPanel === 'settings') {
|
||||
this.closePanel();
|
||||
} else {
|
||||
this.openPanel = 'settings';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.openPanel = '';
|
||||
|
@ -51,6 +81,12 @@ export class FloatingColumnComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if(this.activatedPanelSub) {
|
||||
this.activatedPanelSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
closePanel(): boolean {
|
||||
this.navigationService.closePanel();
|
||||
return false;
|
||||
|
@ -60,21 +96,21 @@ export class FloatingColumnComponent implements OnInit {
|
|||
this.overlayAccountToBrowse = account;
|
||||
this.overlayHashtagToBrowse = null;
|
||||
this.overlayThreadToBrowse = null;
|
||||
this.overlayActive = true;
|
||||
this.overlayActive = true;
|
||||
}
|
||||
|
||||
browseHashtag(hashtag: string): void {
|
||||
this.overlayAccountToBrowse = null;
|
||||
this.overlayHashtagToBrowse = hashtag;
|
||||
this.overlayThreadToBrowse = null;
|
||||
this.overlayActive = true;
|
||||
this.overlayActive = true;
|
||||
}
|
||||
|
||||
browseThread(openThreadEvent: OpenThreadEvent): void {
|
||||
this.overlayAccountToBrowse = null;
|
||||
this.overlayHashtagToBrowse = null;
|
||||
this.overlayThreadToBrowse = openThreadEvent;
|
||||
this.overlayActive = true;
|
||||
this.overlayActive = true;
|
||||
}
|
||||
|
||||
closeOverlay(): boolean {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<div class="my-account__body flexcroll">
|
||||
<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)">
|
||||
{{ stream.name }}
|
||||
<a class="my-account__link my-account__blue" href *ngFor="let stream of availableStreams"
|
||||
(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>
|
||||
<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()">
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
&__body {
|
||||
overflow: auto;
|
||||
height: calc(100%);
|
||||
// width: calc(100%);
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-size: $small-font-size;
|
||||
|
@ -14,24 +13,11 @@
|
|||
outline: 1px dotted greenyellow;
|
||||
}
|
||||
&__label {
|
||||
// text-decoration: underline;
|
||||
font-size: $small-font-size;
|
||||
margin-top: 10px;
|
||||
margin-left: 5px;
|
||||
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 {
|
||||
background-color: $color-primary;
|
||||
color: #fff;
|
||||
|
@ -47,4 +33,26 @@
|
|||
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 { Store } from '@ngxs/store';
|
||||
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
|
||||
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 { StreamElement, StreamTypeEnum, AddStream, RemoveAllStreams } from '../../../../states/streams.state';
|
||||
|
@ -12,11 +14,24 @@ import { NavigationService } from '../../../../services/navigation.service';
|
|||
templateUrl: './my-account.component.html',
|
||||
styleUrls: ['./my-account.component.scss']
|
||||
})
|
||||
export class MyAccountComponent implements OnInit {
|
||||
export class MyAccountComponent implements OnInit, OnDestroy {
|
||||
|
||||
faCheckSquare = faCheckSquare;
|
||||
|
||||
availableStreams: StreamElement[] = [];
|
||||
availableStreams: StreamWrapper[] = [];
|
||||
|
||||
@Input() account: AccountWrapper;
|
||||
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(
|
||||
private readonly store: Store,
|
||||
|
@ -24,19 +39,41 @@ export class MyAccountComponent implements OnInit {
|
|||
private notificationService: NotificationService) { }
|
||||
|
||||
ngOnInit() {
|
||||
const instance = this.account.info.instance;
|
||||
this.availableStreams.length = 0;
|
||||
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));
|
||||
this.streamChangedSub = this.streamElements$.subscribe((streams: StreamElement[]) => {
|
||||
this.loadStreams(this.account);
|
||||
});
|
||||
}
|
||||
|
||||
addStream(stream: StreamElement): boolean {
|
||||
if (stream) {
|
||||
ngOnDestroy(): void {
|
||||
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()
|
||||
.then(() => {
|
||||
this.notificationService.notify(`stream added`, false);
|
||||
});
|
||||
stream.isAdded = true;
|
||||
//this.notificationService.notify(`stream added`, false);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -48,3 +85,11 @@ export class MyAccountComponent implements OnInit {
|
|||
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">
|
||||
<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>
|
||||
<!-- <ion-icon name="md-send"></ion-icon> -->
|
||||
</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>
|
||||
</a>
|
||||
|
||||
|
@ -12,11 +12,11 @@
|
|||
</app-account-icon>
|
||||
</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>
|
||||
</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>
|
||||
</a>
|
||||
</div>
|
|
@ -4,7 +4,9 @@
|
|||
</a>
|
||||
<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()">
|
||||
<ion-icon name="md-swap"></ion-icon>
|
||||
</a>
|
||||
|
@ -12,7 +14,9 @@
|
|||
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>
|
||||
|
||||
<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()">
|
||||
<ion-icon name="md-star"></ion-icon>
|
||||
</a>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@import "variables";
|
||||
|
||||
.action-bar {
|
||||
// outline: 1px solid greenyellow; // height: 20px;
|
||||
margin: 5px 10px 5px $avatar-column-space;
|
||||
|
@ -9,9 +10,11 @@
|
|||
|
||||
&__link {
|
||||
color: $status-secondary-color;
|
||||
|
||||
&:hover {
|
||||
color: $status-links-color;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
@ -50,9 +53,9 @@
|
|||
|
||||
.boosted {
|
||||
color: $boost-color;
|
||||
|
||||
|
||||
&:hover {
|
||||
color: darken($boost-color, 10);
|
||||
color: darken($boost-color, 10);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,4 +65,70 @@
|
|||
&:hover {
|
||||
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;
|
||||
isLocked: boolean;
|
||||
|
||||
favoriteIsLoading: boolean;
|
||||
boostIsLoading: boolean;
|
||||
|
||||
isContentWarningActive: boolean = false;
|
||||
|
||||
private isProviderSelected: boolean;
|
||||
|
@ -48,7 +51,7 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||
constructor(
|
||||
private readonly store: Store,
|
||||
private readonly toolsService: ToolsService,
|
||||
private readonly mastodonService: MastodonService,
|
||||
private readonly mastodonService: MastodonService,
|
||||
private readonly notificationService: NotificationService) {
|
||||
|
||||
this.accounts$ = this.store.select(state => state.registeredaccounts.accounts);
|
||||
|
@ -57,8 +60,14 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||
ngOnInit() {
|
||||
const status = this.statusWrapper.status;
|
||||
const account = this.statusWrapper.provider;
|
||||
this.favoriteStatePerAccountId[account.id] = status.favourited;
|
||||
this.bootedStatePerAccountId[account.id] = status.reblogged;
|
||||
|
||||
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.bootedStatePerAccountId[account.id] = status.reblogged;
|
||||
}
|
||||
|
||||
this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
||||
this.checkStatus(accounts);
|
||||
|
@ -87,7 +96,7 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||
this.isLocked = false;
|
||||
}
|
||||
|
||||
if(status.sensitive || status.spoiler_text){
|
||||
if (status.sensitive || status.spoiler_text) {
|
||||
this.isContentWarningActive = true;
|
||||
}
|
||||
|
||||
|
@ -113,56 +122,63 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
boost(): boolean {
|
||||
//TODO get rid of that
|
||||
this.selectedAccounts.forEach((account: AccountInfo) => {
|
||||
if(this.boostIsLoading) return;
|
||||
|
||||
const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper);
|
||||
usableStatus
|
||||
.then((status: Status) => {
|
||||
if (this.isBoosted && status.reblogged) {
|
||||
return this.mastodonService.unreblog(account, status);
|
||||
} else if(!this.isBoosted && !status.reblogged){
|
||||
return this.mastodonService.reblog(account, status);
|
||||
} else {
|
||||
return Promise.resolve(status);
|
||||
}
|
||||
})
|
||||
.then((boostedStatus: Status) => {
|
||||
this.bootedStatePerAccountId[account.id] = boostedStatus.reblogged;
|
||||
this.checkIfBoosted();
|
||||
// this.isBoosted = !this.isBoosted;
|
||||
})
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
this.notificationService.notifyHttpError(err);
|
||||
});
|
||||
});
|
||||
this.boostIsLoading = true;
|
||||
const account = this.toolsService.getSelectedAccounts()[0];
|
||||
const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper);
|
||||
usableStatus
|
||||
.then((status: Status) => {
|
||||
if (this.isBoosted && status.reblogged) {
|
||||
return this.mastodonService.unreblog(account, status);
|
||||
} else if (!this.isBoosted && !status.reblogged) {
|
||||
return this.mastodonService.reblog(account, status);
|
||||
} else {
|
||||
return Promise.resolve(status);
|
||||
}
|
||||
})
|
||||
.then((boostedStatus: Status) => {
|
||||
this.bootedStatePerAccountId[account.id] = boostedStatus.reblogged;
|
||||
this.checkIfBoosted();
|
||||
})
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
this.notificationService.notifyHttpError(err);
|
||||
})
|
||||
.then(() => {
|
||||
this.boostIsLoading = false;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
favorite(): boolean {
|
||||
this.selectedAccounts.forEach((account: AccountInfo) => {
|
||||
|
||||
const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper);
|
||||
usableStatus
|
||||
.then((status: Status) => {
|
||||
if (this.isFavorited && status.favourited) {
|
||||
return this.mastodonService.unfavorite(account, status);
|
||||
} else if(!this.isFavorited && !status.favourited) {
|
||||
return this.mastodonService.favorite(account, status);
|
||||
} else {
|
||||
return Promise.resolve(status);
|
||||
}
|
||||
})
|
||||
.then((favoritedStatus: Status) => {
|
||||
this.favoriteStatePerAccountId[account.id] = favoritedStatus.favourited;
|
||||
this.checkIfFavorited();
|
||||
// this.isFavorited = !this.isFavorited;
|
||||
})
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
this.notificationService.notifyHttpError(err);
|
||||
});
|
||||
});
|
||||
if(this.favoriteIsLoading) return;
|
||||
|
||||
this.favoriteIsLoading = true;
|
||||
const account = this.toolsService.getSelectedAccounts()[0];
|
||||
const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper);
|
||||
usableStatus
|
||||
.then((status: Status) => {
|
||||
if (this.isFavorited && status.favourited) {
|
||||
return this.mastodonService.unfavorite(account, status);
|
||||
} else if (!this.isFavorited && !status.favourited) {
|
||||
return this.mastodonService.favorite(account, status);
|
||||
} else {
|
||||
return Promise.resolve(status);
|
||||
}
|
||||
})
|
||||
.then((favoritedStatus: Status) => {
|
||||
this.favoriteStatePerAccountId[account.id] = favoritedStatus.favourited;
|
||||
this.checkIfFavorited();
|
||||
// this.isFavorited = !this.isFavorited;
|
||||
})
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
this.notificationService.notifyHttpError(err);
|
||||
})
|
||||
.then(() => {
|
||||
this.favoriteIsLoading = false;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -189,9 +205,4 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
|||
console.warn('more'); //TODO
|
||||
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>
|
||||
|
||||
|
||||
<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">
|
||||
<!-- <img class="profile-header__header" src="{{account.header}}" alt="header" /> -->
|
||||
<img class="profile-header__avatar" src="{{account.avatar}}" alt="header" />
|
||||
<h2 class="profile-header__display-name">{{account.display_name}}</h2>
|
||||
<h2 class="profile-header__fullhandle"><a href="{{account.url}}" target="_blank">@{{account.acct}}</a></h2>
|
||||
<img class="profile-header__avatar" src="{{displayedAccount.avatar}}" alt="header" />
|
||||
<h2 class="profile-header__display-name">{{displayedAccount.display_name}}</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"
|
||||
|
@ -32,14 +31,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="profile-sub-header ">
|
||||
<div *ngIf="account && hasNote" class="profile-description">
|
||||
<!-- <div *ngIf="account && account.note" class="profile-description"> -->
|
||||
<app-databinded-text class="profile-description__content" [textIsSelectable]="false" [text]="account.note"
|
||||
<div *ngIf="displayedAccount && hasNote" class="profile-description">
|
||||
<app-databinded-text class="profile-description__content" [textIsSelectable]="false" [text]="displayedAccount.note"
|
||||
(accountSelected)="browseAccount($event)" (hashtagSelected)="browseHashtag($event)">
|
||||
</app-databinded-text>
|
||||
</div>
|
||||
<div class="profile-fields" *ngIf="account && account.fields.length > 0">
|
||||
<div class="profile-fields__field" *ngFor="let field of account.fields">
|
||||
<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="{{field.value}}" [ngClass]="{'profile-fields__field--validated': field.verified_at }">
|
||||
</div>
|
||||
<div class="profile-fields__field--name">
|
||||
|
@ -49,8 +47,6 @@
|
|||
</div>
|
||||
|
||||
<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">
|
||||
no toots found
|
||||
</div>
|
||||
|
@ -60,6 +56,9 @@
|
|||
(browseAccountEvent)="browseAccount($event)" (browseThreadEvent)="browseThread($event)">
|
||||
</app-status>
|
||||
</div>
|
||||
|
||||
|
||||
<app-waiting-animation *ngIf="statusLoading" class="waiting-icon"></app-waiting-animation>
|
||||
</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 { faUser, faHourglassHalf, faUserCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faUser as faUserRegular } from "@fortawesome/free-regular-svg-icons";
|
||||
|
@ -25,10 +25,13 @@ export class UserProfileComponent implements OnInit {
|
|||
faHourglassHalf = faHourglassHalf;
|
||||
faUserCheck = faUserCheck;
|
||||
|
||||
account: Account;
|
||||
displayedAccount: Account;
|
||||
hasNote: boolean;
|
||||
|
||||
isLoading: boolean;
|
||||
|
||||
private maxReached = false;
|
||||
private maxId: string;
|
||||
statusLoading: boolean;
|
||||
error: string;
|
||||
|
||||
|
@ -41,6 +44,8 @@ export class UserProfileComponent implements OnInit {
|
|||
private accounts$: Observable<AccountInfo[]>;
|
||||
private accountSub: Subscription;
|
||||
|
||||
@ViewChild('statusstream') public statustream: ElementRef;
|
||||
|
||||
@Output() browseAccountEvent = new EventEmitter<string>();
|
||||
@Output() browseHashtagEvent = new EventEmitter<string>();
|
||||
@Output() browseThreadEvent = new EventEmitter<OpenThreadEvent>();
|
||||
|
@ -61,12 +66,12 @@ export class UserProfileComponent implements OnInit {
|
|||
|
||||
ngOnInit() {
|
||||
this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
||||
if (this.account) {
|
||||
this.currentlyUsedAccount = accounts.filter(x => x.isSelected)[0];
|
||||
if (this.displayedAccount) {
|
||||
const userAccount = accounts.filter(x => x.isSelected)[0];
|
||||
|
||||
this.toolsService.findAccount(this.currentlyUsedAccount, this.lastAccountName)
|
||||
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
||||
.then((account: Account) => {
|
||||
this.getFollowStatus(this.currentlyUsedAccount, account);
|
||||
this.getFollowStatus(userAccount, account);
|
||||
})
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
this.notificationService.notifyHttpError(err);
|
||||
|
@ -82,7 +87,7 @@ export class UserProfileComponent implements OnInit {
|
|||
private load(accountName: string) {
|
||||
this.statuses.length = 0;
|
||||
|
||||
this.account = null;
|
||||
this.displayedAccount = null;
|
||||
this.isLoading = true;
|
||||
|
||||
this.lastAccountName = accountName;
|
||||
|
@ -93,11 +98,11 @@ export class UserProfileComponent implements OnInit {
|
|||
this.isLoading = false;
|
||||
this.statusLoading = true;
|
||||
|
||||
this.account = account;
|
||||
this.displayedAccount = account;
|
||||
this.hasNote = account && account.note && account.note !== '<p></p>';
|
||||
|
||||
const getFollowStatusPromise = this.getFollowStatus(this.currentlyUsedAccount, this.account);
|
||||
const getStatusesPromise = this.getStatuses(this.currentlyUsedAccount, this.account);
|
||||
const getFollowStatusPromise = this.getFollowStatus(this.currentlyUsedAccount, this.displayedAccount);
|
||||
const getStatusesPromise = this.getStatuses(this.currentlyUsedAccount, this.displayedAccount);
|
||||
|
||||
return Promise.all([getFollowStatusPromise, getStatusesPromise]);
|
||||
})
|
||||
|
@ -113,11 +118,25 @@ export class UserProfileComponent implements OnInit {
|
|||
private getStatuses(userAccount: AccountInfo, account: Account): Promise<void> {
|
||||
this.statusLoading = true;
|
||||
return this.mastodonService.getAccountStatuses(userAccount, account.id, false, false, true, null, null, 40)
|
||||
.then((result: Status[]) => {
|
||||
for (const status of result) {
|
||||
const wrapper = new StatusWrapper(status, userAccount);
|
||||
this.statuses.push(wrapper);
|
||||
}
|
||||
.then((statuses: Status[]) => {
|
||||
this.loadStatus(userAccount, statuses);
|
||||
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
|
@ -147,10 +166,10 @@ export class UserProfileComponent implements OnInit {
|
|||
}
|
||||
|
||||
follow(): boolean {
|
||||
this.currentlyUsedAccount = this.toolsService.getSelectedAccounts()[0];
|
||||
this.toolsService.findAccount(this.currentlyUsedAccount, this.lastAccountName)
|
||||
const userAccount = this.toolsService.getSelectedAccounts()[0];
|
||||
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
||||
.then((account: Account) => {
|
||||
return this.mastodonService.follow(this.currentlyUsedAccount, account);
|
||||
return this.mastodonService.follow(userAccount, account);
|
||||
})
|
||||
.then((relationship: Relationship) => {
|
||||
this.relationship = relationship;
|
||||
|
@ -162,10 +181,10 @@ export class UserProfileComponent implements OnInit {
|
|||
}
|
||||
|
||||
unfollow(): boolean {
|
||||
this.currentlyUsedAccount = this.toolsService.getSelectedAccounts()[0];
|
||||
this.toolsService.findAccount(this.currentlyUsedAccount, this.lastAccountName)
|
||||
const userAccount = this.toolsService.getSelectedAccounts()[0];
|
||||
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
||||
.then((account: Account) => {
|
||||
return this.mastodonService.unfollow(this.currentlyUsedAccount, account);
|
||||
return this.mastodonService.unfollow(userAccount, account);
|
||||
})
|
||||
.then((relationship: Relationship) => {
|
||||
this.relationship = relationship;
|
||||
|
@ -175,4 +194,44 @@ export class UserProfileComponent implements OnInit {
|
|||
});
|
||||
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;
|
||||
medias.push(wrapper);
|
||||
if(medias.length > 4){
|
||||
medias.splice(0, 1);
|
||||
}
|
||||
this.mediaSubject.next(medias);
|
||||
|
||||
this.mastodonService.uploadMediaAttachment(account, file, null)
|
||||
|
|
|
@ -157,7 +157,7 @@ export interface Status {
|
|||
favourited: boolean;
|
||||
sensitive: boolean;
|
||||
spoiler_text: string;
|
||||
visibility: string;
|
||||
visibility: 'public' | 'unlisted' | 'private' | 'direct';
|
||||
media_attachments: Attachment[];
|
||||
mentions: Mention[];
|
||||
tags: Tag[];
|
||||
|
|
|
@ -44,10 +44,10 @@ export class ToolsService {
|
|||
.then((result: Results) => {
|
||||
if(accountName[0] === '@') accountName = accountName.substr(1);
|
||||
|
||||
const foundAccount = result.accounts.filter(
|
||||
const foundAccount = result.accounts.find(
|
||||
x => x.acct.toLowerCase() === accountName.toLowerCase()
|
||||
|| x.acct.toLowerCase() === accountName.toLowerCase().split('@')[0]
|
||||
)[0];
|
||||
|| x.acct.toLowerCase().split('@')[0] === accountName.toLowerCase().split('@')[0]
|
||||
);
|
||||
return foundAccount;
|
||||
});
|
||||
}
|
||||
|
|