Merge pull request #612 from NicolasConstant/topic_add-more-user-actions

Topic add more user actions
This commit is contained in:
Nicolas Constant 2023-08-20 03:33:32 -04:00 committed by GitHub
commit 8cee7289eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 235 additions and 15 deletions

View File

@ -79,6 +79,8 @@ export class PollEditorComponent implements OnInit {
}
private loadPollParameters(poll: Poll) {
if(!this.oldPoll) return;
const isMulti = poll.multiple;
this.entries.length = 0;

View File

@ -15,7 +15,9 @@
<button type="submit" class="form-button"
title="add account"
[class.comrade__button]="isComrade">
<span *ngIf="!isLoading">Submit</span>
<span *ngIf="!isLoading && !this.isInstanceMultiAccountLoading">Submit</span>
<span *ngIf="!isLoading && this.isInstanceMultiAccountLoading" class="faq__warning">See FAQ</span>
<app-waiting-animation *ngIf="isLoading" class="waiting-icon"></app-waiting-animation>
</button>
@ -29,5 +31,12 @@
allowfullscreen></iframe>
</div>
<div class="faq" *ngIf="isInstanceMultiAccount">
<p>
FAQ<br/>
<a href="https://github.com/NicolasConstant/sengi/wiki/How-to-add-multiple-accounts-from-the-same-instance" target="_blank">How to add multiple accounts from the same instance?</a>
</p>
</div>
</div>
</div>

View File

@ -109,4 +109,21 @@ $comrade_red: #a50000;
background-color: $comrade_red;
background-position: 0 0;
}
}
.faq {
margin: 20px 0 0 0;
& a {
color: #ffcc00;
text-decoration: underline;
&:hover {
color: #ffe88a;
}
}
&__warning {
color: #ffdc52;
}
}

View File

@ -6,13 +6,14 @@ import { RegisteredAppsStateModel, AppInfo, AddRegisteredApp } from '../../../st
import { AuthService, CurrentAuthProcess } from '../../../services/auth.service';
import { AppData } from '../../../services/models/mastodon.interfaces';
import { NotificationService } from '../../../services/notification.service';
import { ToolsService } from '../../../services/tools.service';
@Component({
selector: 'app-add-new-account',
templateUrl: './add-new-account.component.html',
styleUrls: ['./add-new-account.component.scss']
})
export class AddNewAccountComponent implements OnInit {
export class AddNewAccountComponent implements OnInit {
private blockList = ['gab.com', 'gab.ai', 'cyzed.com'];
private comradeList = ['juche.town'];
@ -24,12 +25,14 @@ export class AddNewAccountComponent implements OnInit {
set setInstance(value: string) {
this.instance = value.replace('http://', '').replace('https://', '').replace('/', '').toLowerCase().trim();
this.checkComrad();
this.checkInstanceMultiAccount(value);
}
get setInstance(): string {
return this.instance;
}
constructor(
private readonly toolsService: ToolsService,
private readonly notificationService: NotificationService,
private readonly authService: AuthService,
private readonly store: Store) { }
@ -51,8 +54,27 @@ export class AddNewAccountComponent implements OnInit {
this.isComrade = false;
}
isInstanceMultiAccount: boolean;
isInstanceMultiAccountLoading: boolean;
checkInstanceMultiAccount(value: string) {
if(value) {
const instances: string[] = this.toolsService.getAllAccounts().map(x => x.instance);
if(instances && instances.indexOf(value) > -1){
this.isInstanceMultiAccount = true;
this.isInstanceMultiAccountLoading = true;
setTimeout(() => {
this.isInstanceMultiAccountLoading = false;
}, 2000);
} else {
this.isInstanceMultiAccount = false;
this.isInstanceMultiAccountLoading = false;
}
}
}
onSubmit(): boolean {
if(this.isLoading || !this.instance) return false;
if(this.isLoading || !this.instance || this.isInstanceMultiAccountLoading) return false;
this.isLoading = true;

View File

@ -27,7 +27,13 @@
<ng-template contextMenuItem (execute)="unmuteConversation()" *ngIf="statusWrapper && isOwnerSelected && displayedStatus.muted">
Unmute conversation
</ng-template>
<ng-template contextMenuItem divider="true"></ng-template>
<ng-template contextMenuItem (execute)="hideBoosts()" *ngIf="!isOwnerSelected && this.relationship && this.relationship.following && this.relationship.showing_reblogs">
Hide boosts from @{{ this.username }}
</ng-template>
<ng-template contextMenuItem (execute)="unhideBoosts()" *ngIf="!isOwnerSelected && this.relationship && this.relationship.following && !this.relationship.showing_reblogs">
Unhide boosts from @{{ this.username }}
</ng-template>
<ng-template contextMenuItem divider="true" *ngIf="!isOwnerSelected"></ng-template>
<ng-template contextMenuItem (execute)="muteAccount()" *ngIf="!isOwnerSelected && this.relationship && !this.relationship.muting">
Mute @{{ this.username }}
</ng-template>
@ -40,6 +46,14 @@
<ng-template contextMenuItem (execute)="unblockAccount()" *ngIf="!isOwnerSelected && this.relationship && this.relationship.blocking">
Unblock @{{ this.username }}
</ng-template>
<ng-template contextMenuItem divider="true" *ngIf="!isOwnerSelected"></ng-template>
<ng-template contextMenuItem (execute)="blockDomain()" *ngIf="!isOwnerSelected && this.relationship && !this.relationship.domain_blocking">
Block domain {{ this.domain }}
</ng-template>
<ng-template contextMenuItem (execute)="unblockDomain()" *ngIf="!isOwnerSelected && this.relationship && this.relationship.domain_blocking">
Unblock domain {{ this.domain }}
</ng-template>
<ng-template contextMenuItem divider="true" *ngIf="isOwnerSelected"></ng-template>
<ng-template contextMenuItem (execute)="pinOnProfile()" *ngIf="statusWrapper && isOwnerSelected && !displayedStatus.pinned && displayedStatus.visibility === 'public'">
Pin on profile
</ng-template>

View File

@ -25,6 +25,7 @@ export class StatusUserContextMenuComponent implements OnInit, OnDestroy {
private loadedAccounts: AccountInfo[];
displayedStatus: Status;
username: string;
domain: string;
isOwnerSelected: boolean;
isEditingAvailable: boolean;
@ -74,6 +75,7 @@ export class StatusUserContextMenuComponent implements OnInit, OnDestroy {
}
this.username = account.acct.split('@')[0];
this.domain = account.acct.split('@')[1];
this.fullHandle = this.toolsService.getAccountFullHandle(account);
}
@ -167,6 +169,38 @@ export class StatusUserContextMenuComponent implements OnInit, OnDestroy {
return false;
}
hideBoosts(): boolean {
const acc = this.toolsService.getSelectedAccounts()[0];
this.toolsService.findAccount(acc, this.fullHandle)
.then(async (target: Account) => {
const relationship = await this.mastodonService.hideBoosts(acc, target);
this.relationship = relationship;
this.relationshipChanged.next(relationship);
})
.catch(err => {
this.notificationService.notifyHttpError(err, acc);
});
return false;
}
unhideBoosts(): boolean {
const acc = this.toolsService.getSelectedAccounts()[0];
this.toolsService.findAccount(acc, this.fullHandle)
.then(async (target: Account) => {
const relationship = await this.mastodonService.unhideBoosts(acc, target);
this.relationship = relationship;
this.relationshipChanged.next(relationship);
})
.catch(err => {
this.notificationService.notifyHttpError(err, acc);
});
return false;
}
muteAccount(): boolean {
const acc = this.toolsService.getSelectedAccounts()[0];
@ -241,6 +275,37 @@ export class StatusUserContextMenuComponent implements OnInit, OnDestroy {
return false;
}
blockDomain(): boolean {
const response = confirm(`Are you really sure you want to block the entire ${this.domain} domain? You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.`);
if (response) {
const acc = this.toolsService.getSelectedAccounts()[0];
this.mastodonService.blockDomain(acc, this.domain)
.then(_ => {
this.relationship.domain_blocking = true;
})
.catch(err => {
this.notificationService.notifyHttpError(err, acc);
});
}
return false;
}
unblockDomain(): boolean {
const acc = this.toolsService.getSelectedAccounts()[0];
this.mastodonService.blockDomain(acc, this.domain)
.then(_ => {
this.relationship.domain_blocking = false;
})
.catch(err => {
this.notificationService.notifyHttpError(err, acc);
});
return false;
}
muteConversation(): boolean {
const selectedAccount = this.toolsService.getSelectedAccounts()[0];

View File

@ -109,6 +109,11 @@
<span class="status__content-warning--title">sensitive content</span>
<span innerHTML="{{ contentWarningText }}"></span>
</a>
<div class="status__content-warning__closed" *ngIf="!isContentWarned && contentWarningText" title="content warning">
<span innerHTML="{{ contentWarningText }}"></span>
</div>
<app-databinded-text #databindedtext class="status__content" *ngIf="!isContentWarned" [text]="statusContent" [selected]="isSelected"
(accountSelected)="accountSelected($event)" (hashtagSelected)="hashtagSelected($event)"
(textSelected)="textSelected()"></app-databinded-text>

View File

@ -172,6 +172,26 @@
border: 3px solid $status-secondary-color;
color: whitesmoke;
&__closed {
//margin: 0 5px 0 $avatar-column-space;
margin: 0 5px 0 calc(#{$avatar-column-space} - 1px);
padding: 3px 5px 3px 5px;
margin-bottom: 5px;
overflow-wrap: break-word;
font-size: 12px;
border-radius: 4px;
// color: #6d8fd3;
// color: #7282a1;
// color: #838da1;
color: #919bb1;
// background-color: #273149;
// background-color: #1f273a;
background-color: #171d2b;
}
&--title {
color: $content-warning-font-color;
font-size: 11px;

View File

@ -22,14 +22,14 @@ export class MastodonWrapperService {
private readonly mastodonService: MastodonService) { }
refreshAccountIfNeeded(accountInfo: AccountInfo): Promise<AccountInfo> {
if(this.refreshingToken[accountInfo.id]){
if (this.refreshingToken[accountInfo.id]) {
return this.refreshingToken[accountInfo.id];
}
let isExpired = false;
let storedAccountInfo = this.getStoreAccountInfo(accountInfo.id);
if(!storedAccountInfo || !(storedAccountInfo.token))
if (!storedAccountInfo || !(storedAccountInfo.token))
return Promise.resolve(accountInfo);
try {
@ -96,7 +96,7 @@ export class MastodonWrapperService {
return this.mastodonService.getInstance(instance);
}
translate(account: AccountInfo, statusId: string, lang: string): Promise<Translation>{
translate(account: AccountInfo, statusId: string, lang: string): Promise<Translation> {
return this.refreshAccountIfNeeded(account)
.then((refreshedAccount: AccountInfo) => {
return this.mastodonService.translate(refreshedAccount, statusId, lang);
@ -146,7 +146,7 @@ export class MastodonWrapperService {
}
search(account: AccountInfo, query: string, version: 'v1' | 'v2', resolve: boolean = false): Promise<Results> {
if(query.includes('twitter.com')){
if (query.includes('twitter.com')) {
query = this.processTwitterQuery(query);
}
@ -158,17 +158,17 @@ export class MastodonWrapperService {
private processTwitterQuery(query: string): string {
const settings = this.settingsService.getSettings();
if(!settings.twitterBridgeInstance) return query;
if (!settings.twitterBridgeInstance) return query;
let name;
if(query.includes('twitter.com/')){
if (query.includes('twitter.com/')) {
console.log(query.replace('https://', '').replace('http://', '').split('/'));
name = query.replace('https://', '').replace('http://', '').split('/')[1];
}
if(query.includes('@twitter.com')){
if (query.includes('@twitter.com')) {
console.log(query.split('@'));
name = query.split('@')[0];
if(name === '' || name == null){
if (name === '' || name == null) {
name = query.split('@')[1];
}
}
@ -208,7 +208,7 @@ export class MastodonWrapperService {
}
searchAccount(account: AccountInfo, query: string, limit: number = 40, following: boolean = false, resolve = true): Promise<Account[]> {
if(query.includes('twitter.com')){
if (query.includes('twitter.com')) {
query = this.processTwitterQuery(query);
}
@ -281,6 +281,20 @@ export class MastodonWrapperService {
});
}
hideBoosts(currentlyUsedAccount: AccountInfo, account: Account): Promise<Relationship> {
return this.refreshAccountIfNeeded(currentlyUsedAccount)
.then((refreshedAccount: AccountInfo) => {
return this.mastodonService.hideBoosts(refreshedAccount, account);
});
}
unhideBoosts(currentlyUsedAccount: AccountInfo, account: Account): Promise<Relationship> {
return this.refreshAccountIfNeeded(currentlyUsedAccount)
.then((refreshedAccount: AccountInfo) => {
return this.mastodonService.unhideBoosts(refreshedAccount, account);
});
}
followHashtag(currentlyUsedAccount: AccountInfo, hashtag: string): Promise<Tag> {
return this.refreshAccountIfNeeded(currentlyUsedAccount)
.then((refreshedAccount: AccountInfo) => {
@ -407,6 +421,20 @@ export class MastodonWrapperService {
});
}
blockDomain(account: AccountInfo, domain: string): Promise<void> {
return this.refreshAccountIfNeeded(account)
.then((refreshedAccount: AccountInfo) => {
return this.mastodonService.blockDomain(refreshedAccount, domain);
});
}
unblockDomain(account: AccountInfo, domain: string): Promise<void> {
return this.refreshAccountIfNeeded(account)
.then((refreshedAccount: AccountInfo) => {
return this.mastodonService.unblockDomain(refreshedAccount, domain);
});
}
pinOnProfile(account: AccountInfo, statusId: string): Promise<Status> {
return this.refreshAccountIfNeeded(account)
.then((refreshedAccount: AccountInfo) => {
@ -470,14 +498,14 @@ export class MastodonWrapperService {
});
}
getFollowing(account: AccountInfo, accountId: number, maxId: string, sinceId: string, limit: number = 40): Promise<FollowingResult> {
getFollowing(account: AccountInfo, accountId: number, maxId: string, sinceId: string, limit: number = 40): Promise<FollowingResult> {
return this.refreshAccountIfNeeded(account)
.then((refreshedAccount: AccountInfo) => {
return this.mastodonService.getFollowing(refreshedAccount, accountId, maxId, sinceId, limit);
});
}
getFollowers(account: AccountInfo, accountId: number, maxId: string, sinceId: string, limit: number = 40): Promise<FollowingResult> {
getFollowers(account: AccountInfo, accountId: number, maxId: string, sinceId: string, limit: number = 40): Promise<FollowingResult> {
return this.refreshAccountIfNeeded(account)
.then((refreshedAccount: AccountInfo) => {
return this.mastodonService.getFollowers(refreshedAccount, accountId, maxId, sinceId, limit);

View File

@ -357,6 +357,26 @@ export class MastodonService {
}
hideBoosts(currentlyUsedAccount: AccountInfo, account: Account): Promise<Relationship> {
const route = `https://${currentlyUsedAccount.instance}${this.apiRoutes.follow}`.replace('{0}', account.id.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${currentlyUsedAccount.token.access_token}` });
let input = new FormData();
input.append('reblogs', 'false');
return this.httpClient.post<Relationship>(route, input, { headers: headers }).toPromise();
}
unhideBoosts(currentlyUsedAccount: AccountInfo, account: Account): Promise<Relationship> {
const route = `https://${currentlyUsedAccount.instance}${this.apiRoutes.follow}`.replace('{0}', account.id.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${currentlyUsedAccount.token.access_token}` });
let input = new FormData();
input.append('reblogs', 'true');
return this.httpClient.post<Relationship>(route, input, { headers: headers }).toPromise();
}
followHashtag(currentlyUsedAccount: AccountInfo, hashtag: string): Promise<Tag> {
const route = `https://${currentlyUsedAccount.instance}${this.apiRoutes.followHashtag}`.replace('{0}', hashtag);
const headers = new HttpHeaders({ 'Authorization': `Bearer ${currentlyUsedAccount.token.access_token}` });
@ -524,6 +544,23 @@ export class MastodonService {
return this.httpClient.post<Relationship>(route, null, { headers: headers }).toPromise();
}
blockDomain(account: AccountInfo, domain: string): Promise<void> {
let route = `https://${account.instance}${this.apiRoutes.blockDomain}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
let input = new FormData();
input.append('domain', domain);
return this.httpClient.post<void>(route, input, { headers: headers }).toPromise();
}
unblockDomain(account: AccountInfo, domain: string): Promise<void> {
let route = `https://${account.instance}${this.apiRoutes.blockDomain}?domain=${domain}`;
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}`});
return this.httpClient.delete<void>(route, { headers: headers }).toPromise();
}
pinOnProfile(account: AccountInfo, statusId: string): Promise<Status> {
let route = `https://${account.instance}${this.apiRoutes.pinStatus}`.replace('{0}', statusId.toString());
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });

View File

@ -12,6 +12,7 @@ export class ApiRoutes {
unfollow = '/api/v1/accounts/{0}/unfollow';
block = '/api/v1/accounts/{0}/block';
unblock = '/api/v1/accounts/{0}/unblock';
blockDomain = '/api/v1/domain_blocks';
mute = '/api/v1/accounts/{0}/mute';
unmute = '/api/v1/accounts/{0}/unmute';
muteStatus = '/api/v1/statuses/{0}/mute';