Merge pull request #266 from NicolasConstant/topic_keep-status-redaction-state

Topic keep status redaction state
This commit is contained in:
Nicolas Constant 2020-04-26 22:01:26 -04:00 committed by GitHub
commit c7b543c780
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 130 additions and 23 deletions

View File

@ -195,4 +195,32 @@ describe('CreateStatusComponent', () => {
expect(result[1].length).toBeLessThanOrEqual(527);
expect(result[1]).toBe('http://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/');
});
it('should tranform external mentions properly - mastodon', () => {
let mastodonMention = '<p>test <span class="h-card"><a href="https://mastodon.social/@sengi_app" class="u-url mention">@<span>sengi_app</span></a></span> qsdqds qsd qsd qsd q <span class="h-card"><a href="https://mastodon.social/@test" class="u-url mention">@<span>test</span></a></span> <span class="h-card"><a href="https://mastodon.social/@no" class="u-url">@<span>no</span></a></span></p>';
const result = <string>(<any>component).tranformHtmlRepliesToReplies(mastodonMention);
expect(result).toBe('<p>test @sengi_app@mastodon.social qsdqds qsd qsd qsd q @test@mastodon.social <span class="h-card"><a href="https://mastodon.social/@no" class="u-url">@<span>no</span></a></span></p>');
});
it('should tranform external mentions properly - mastodon 2', () => {
let mastodonMention = '<p>test <span class="h-card"><a href="https://pleroma.site/users/sengi_app" class="u-url mention">@<span>sengi_app</span></a></span> qsdqds qsd qsd qsd q <span class="h-card"><a href="https://pleroma.site/users/test" class="u-url mention">@<span>test</span></a></span> <span class="h-card"><a href="https://pleroma.site/users/no" class="u-url">@<span>no</span></a></span></p>';
const result = <string>(<any>component).tranformHtmlRepliesToReplies(mastodonMention);
expect(result).toBe('<p>test @sengi_app@pleroma.site qsdqds qsd qsd qsd q @test@pleroma.site <span class="h-card"><a href="https://pleroma.site/users/no" class="u-url">@<span>no</span></a></span></p>');
});
it('should tranform external mentions properly - pleroma', () => {
let pleromaMention = '<p>test <span class="h-card"><a data-user="50504" class="u-url mention" href="https://mastodon.social/@sengi_app" rel="ugc">@<span>sengi_app</span></a></span> qsdqds qsd qsd qsd q <span class="h-card"><a data-user="50504" class="u-url mention" href="https://mastodon.social/@test" rel="ugc">@<span>test</span></a></span> <span class="h-card"><a href="https://mastodon.social/@no" class="u-url">@<span>no</span></a></span></p>';
const result = <string>(<any>component).tranformHtmlRepliesToReplies(pleromaMention);
expect(result).toBe('<p>test @sengi_app@mastodon.social qsdqds qsd qsd qsd q @test@mastodon.social <span class="h-card"><a href="https://mastodon.social/@no" class="u-url">@<span>no</span></a></span></p>');
});
it('should tranform external mentions properly - pleroma 2', () => {
let pleromaMention = '<p>test <span class="h-card"><a data-user="50504" class="u-url mention" href="https://pleroma.site/users/sengi_app" rel="ugc">@<span>sengi_app</span></a></span> qsdqds qsd qsd qsd q <span class="h-card"><a data-user="50504" class="u-url mention" href="https://pleroma.site/users/test" rel="ugc">@<span>test</span></a></span> <span class="h-card"><a href="https://pleroma.site/users/no" class="u-url">@<span>no</span></a></span></p>';
const result = <string>(<any>component).tranformHtmlRepliesToReplies(pleromaMention);
expect(result).toBe('<p>test @sengi_app@pleroma.site qsdqds qsd qsd qsd q @test@pleroma.site <span class="h-card"><a href="https://pleroma.site/users/no" class="u-url">@<span>no</span></a></span></p>');
});
});

View File

@ -1,5 +1,7 @@
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ElementRef, ViewChild, ViewContainerRef, ComponentRef, HostListener } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { Overlay, OverlayConfig, FullscreenOverlayContainer, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
import { Store } from '@ngxs/store';
import { Subscription, Observable } from 'rxjs';
import { UP_ARROW, DOWN_ARROW, ENTER, ESCAPE } from '@angular/cdk/keycodes';
@ -17,12 +19,11 @@ import { AccountInfo } from '../../states/accounts.state';
import { InstancesInfoService } from '../../services/instances-info.service';
import { MediaService } from '../../services/media.service';
import { AutosuggestSelection, AutosuggestUserActionEnum } from './autosuggest/autosuggest.component';
import { Overlay, OverlayConfig, FullscreenOverlayContainer, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
import { EmojiPickerComponent } from './emoji-picker/emoji-picker.component';
import { PollEditorComponent } from './poll-editor/poll-editor.component';
import { StatusSchedulerComponent } from './status-scheduler/status-scheduler.component';
import { ScheduledStatusService } from '../../services/scheduled-status.service';
import { StatusesStateService } from '../../services/statuses-state.service';
@Component({
selector: 'app-create-status',
@ -53,6 +54,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
private _status: string = '';
@Input('status')
set status(value: string) {
this.statusStateService.setStatusContent(value, this.statusReplyingToWrapper);
this.countStatusChar(value);
this.detectAutosuggestion(value);
this._status = value;
@ -65,14 +67,38 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
return this._status;
}
private trim(s, mask) {
while (~mask.indexOf(s[0])) {
s = s.slice(1);
}
while (~mask.indexOf(s[s.length - 1])) {
s = s.slice(0, -1);
}
return s;
}
@Input('redraftedStatus')
set redraftedStatus(value: StatusWrapper) {
if (value) {
if (value) {
this.statusLoaded = false;
const newLine = String.fromCharCode(13, 10);
let content = value.status.content;
content = this.tranformHtmlRepliesToReplies(content);
while (content.includes('<p>') || content.includes('</p>') || content.includes('<br>') || content.includes('<br/>') || content.includes('<br />')) {
content = content.replace('<p>', '').replace('</p>', newLine + newLine).replace('<br />', newLine).replace('<br/>', newLine).replace('<br>', newLine);
}
content = this.trim(content, newLine);
let parser = new DOMParser();
var dom = parser.parseFromString(value.status.content, 'text/html')
var dom = parser.parseFromString(content, 'text/html')
this.status = dom.body.textContent;
this.statusStateService.setStatusContent(this.status, this.statusReplyingToWrapper);
this.setVisibilityFromStatus(value.status);
this.title = value.status.spoiler_text;
this.statusLoaded = true;
@ -83,17 +109,6 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
.then((status: Status) => {
let cwResult = this.toolsService.checkContentWarning(status);
this.statusReplyingToWrapper = new StatusWrapper(cwResult.status, value.provider, cwResult.applyCw, cwResult.hide);
const mentions = this.getMentions(this.statusReplyingToWrapper.status, this.statusReplyingToWrapper.provider);
for (const mention of mentions) {
const name = `@${mention.split('@')[0]}`;
if (this.status.includes(name)) {
this.status = this.status.replace(name, `@${mention}`);
} else {
this.status = `@${mention} ` + this.status;
}
}
})
.catch(err => {
this.notificationService.notifyHttpError(err, value.provider);
@ -159,6 +174,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
private selectedAccount: AccountInfo;
constructor(
private statusStateService: StatusesStateService,
private readonly scheduledStatusService: ScheduledStatusService,
private readonly contextMenuService: ContextMenuService,
private readonly store: Store,
@ -169,7 +185,9 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
private readonly mediaService: MediaService,
private readonly overlay: Overlay,
public viewContainerRef: ViewContainerRef) {
this.accounts$ = this.store.select(state => state.registeredaccounts.accounts);
this.status = this.statusStateService.getStatusContent(this.statusReplyingToWrapper);
}
ngOnInit() {
@ -185,9 +203,14 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
this.statusReplyingTo = this.statusReplyingToWrapper.status;
}
const uniqueMentions = this.getMentions(this.statusReplyingTo, this.statusReplyingToWrapper.provider);
for (const mention of uniqueMentions) {
this.status += `@${mention} `;
let state = this.statusStateService.getStatusContent(this.statusReplyingToWrapper);
if (state && state !== '') {
this.status = state;
} else {
const uniqueMentions = this.getMentions(this.statusReplyingTo, this.statusReplyingToWrapper.provider);
for (const mention of uniqueMentions) {
this.status += `@${mention} `;
}
}
this.setVisibilityFromStatus(this.statusReplyingTo);
@ -533,6 +556,8 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
if (this.scheduleIsActive) {
this.scheduledStatusService.statusAdded(acc);
}
this.statusStateService.resetStatusContent(this.statusReplyingToWrapper);
})
.catch((err: HttpErrorResponse) => {
this.notificationService.notifyHttpError(err, acc);
@ -810,4 +835,19 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
this.scheduleIsActive = !this.scheduleIsActive;
return false;
}
private tranformHtmlRepliesToReplies(data: string): string {
const mastodonMentionRegex = /<span class="h-card"><a href="https:\/\/([a-zA-Z0-9.]{0,255})\/[a-zA-Z0-9_@/-]{0,255}" class="u-url mention">@<span>([a-zA-Z0-9_-]{0,255})<\/span><\/a><\/span>/gmi;
const pleromaMentionRegex = /<span class="h-card"><a data-user="[a-zA-Z0-9]{0,255}" class="u-url mention" href="https:\/\/([a-zA-Z0-9.]{0,255})\/[a-zA-Z0-9_@/-]{0,255}" rel="ugc">@<span>([a-zA-Z0-9_-]{0,255})<\/span><\/a><\/span>/gmi;
while(data.match(mastodonMentionRegex)){
data = data.replace(mastodonMentionRegex, '@$2@$1');
}
while(data.match(pleromaMentionRegex)){
data = data.replace(pleromaMentionRegex, '@$2@$1');
}
return data;
}
}

View File

@ -16,4 +16,6 @@
Now <span class="underline">right-click</span> on your avatar to open your account and be able to add some timelines!
</p>
</div>
<!-- </div> -->
<!-- </div> -->
<img class="sengi-logo" src="assets/icons/icon-384x384.png" />

View File

@ -25,20 +25,20 @@
&__arrow {
position: fixed;
top: 15px;
top: 20px;
left: 60px;
}
&__title{
position: relative;
top: 30px;
left: 70px;
left: 85px;
}
&__description {
position: relative;
top: 45px;
left: 75px;
top: 60px;
left: 70px;
text-align: center;
@ -80,4 +80,11 @@
// word-break: break-all;
white-space: normal;
}
}
.sengi-logo {
width: 230px;
position: fixed;
bottom: 35px;
right: 10px;
}

View File

@ -1,11 +1,13 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { StatusWrapper } from '../models/common.model';
@Injectable({
providedIn: 'root'
})
export class StatusesStateService {
private cachedStatusText: { [statusId: string]: string } = {};
private cachedStatusStates: { [statusId: string]: { [accountId: string]: StatusState } } = {};
public stateNotification = new Subject<StatusState>();
@ -62,6 +64,34 @@ export class StatusesStateService {
this.stateNotification.next(this.cachedStatusStates[statusId][accountId]);
}
setStatusContent(data: string, replyingToStatus: StatusWrapper){
if(replyingToStatus){
this.cachedStatusText[replyingToStatus.status.uri] = data;
} else {
this.cachedStatusText['none'] = data;
}
}
getStatusContent(replyingToStatus: StatusWrapper): string{
let data: string;
if(replyingToStatus){
data = this.cachedStatusText[replyingToStatus.status.uri];
} else {
data = this.cachedStatusText['none'];
}
if(!data) return '';
return data;
}
resetStatusContent(replyingToStatus: StatusWrapper){
if(replyingToStatus){
this.cachedStatusText[replyingToStatus.status.uri] = '';
} else {
this.cachedStatusText['none'] = '';
}
}
}
export class StatusState {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 10 KiB