Merge pull request #610 from NicolasConstant/topic_enhance-language-feature

Topic enhance language feature
This commit is contained in:
Nicolas Constant 2023-08-19 16:45:03 -04:00 committed by GitHub
commit a0cb240446
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 184 additions and 11 deletions

View File

@ -68,6 +68,8 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
this.detectAutosuggestion(value); this.detectAutosuggestion(value);
this._status = value; this._status = value;
this.languageService.autoDetectLang(value);
setTimeout(() => { setTimeout(() => {
this.autoGrow(); this.autoGrow();
}, 0); }, 0);

View File

@ -62,12 +62,20 @@
<a href (click)="onRemoveLang(l)" class="form-button language__entry__action sound__play">remove</a> <a href (click)="onRemoveLang(l)" class="form-button language__entry__action sound__play">remove</a>
</div> </div>
<input type="text" (input)="onSearchLang($event.target.value)" [(ngModel)]="searchLang" <input type="text" (input)="onSearchLang($event.target.value)" [(ngModel)]="searchLang"
placeholder="Find Language" autocomplete="off" class="form-control form-control-sm language__search"/> placeholder="Find Language" autocomplete="off"
class="form-control form-control-sm language__search" />
<div *ngFor="let l of searchedLangs" class="language__entry"> <div *ngFor="let l of searchedLangs" class="language__entry">
<span class="language__entry__name">{{ l.name }} ({{l.iso639}})</span> <span class="language__entry__name">{{ l.name }} ({{l.iso639}})</span>
<a href (click)="onAddLang(l)" class="form-button language__entry__action sound__play">add</a> <a href (click)="onAddLang(l)" class="form-button language__entry__action sound__play">add</a>
</div> </div>
</div> </div>
<input class="sub-section__checkbox" [(ngModel)]="disableLangAutodetectEnabled"
(change)="onDisableLangAutodetectChanged()" type="checkbox" name="disableLangAutodetec"
value="disableLangAutodetec" id="disableLangAutodetec">
<label class="noselect sub-section__label" for="disableLangAutodetec">disable language autodetection</label>
</div> </div>
<h4 class="panel__subtitle">Twitter Bridge</h4> <h4 class="panel__subtitle">Twitter Bridge</h4>
<div class="sub-section"> <div class="sub-section">
@ -159,7 +167,8 @@
<input class="sub-section__checkbox" [checked]="timeLineHeader === 6" (change)="onTimeLineHeaderChange(6)" <input class="sub-section__checkbox" [checked]="timeLineHeader === 6" (change)="onTimeLineHeaderChange(6)"
type="radio" name="timelineheader-6" value="timelineheader-6" id="timelineheader-6"> type="radio" name="timelineheader-6" value="timelineheader-6" id="timelineheader-6">
<label class="noselect sub-section__label" for="timelineheader-6">Title - Account Icon - Username - Domain Name</label> <label class="noselect sub-section__label" for="timelineheader-6">Title - Account Icon - Username - Domain
Name</label>
<br> <br>
<span class="sub-section__title">loading behavior:</span><br /> <span class="sub-section__title">loading behavior:</span><br />
@ -186,7 +195,8 @@
<input class="sub-section__checkbox" [(ngModel)]="autoFollowOnListEnabled" <input class="sub-section__checkbox" [(ngModel)]="autoFollowOnListEnabled"
(change)="onAutoFollowOnListChanged()" type="checkbox" name="onAutoFollowOnListChanged" (change)="onAutoFollowOnListChanged()" type="checkbox" name="onAutoFollowOnListChanged"
value="onAutoFollowOnListChanged" id="onAutoFollowOnListChanged"> value="onAutoFollowOnListChanged" id="onAutoFollowOnListChanged">
<label class="noselect sub-section__label" for="onAutoFollowOnListChanged">autofollow accounts when adding to list</label> <label class="noselect sub-section__label" for="onAutoFollowOnListChanged">autofollow accounts when
adding to list</label>
<br> <br>
</div> </div>
</div> </div>
@ -199,6 +209,17 @@
<label class="noselect sub-section__label" for="disableRemoteFetching">disable remote status <label class="noselect sub-section__label" for="disableRemoteFetching">disable remote status
fetching</label> fetching</label>
<br> <br>
<input class="sub-section__checkbox" [(ngModel)]="enableAltLabelEnabled"
(change)="onEnableAltLabelChanged()" type="checkbox" name="enableAltLabel"
value="enableAltLabel" id="enableAltLabel">
<label class="noselect sub-section__label" for="enableAltLabel">enable alt label</label>
<br>
<input class="sub-section__checkbox" [(ngModel)]="enableFreezeAvatarEnabled"
(change)="onEnableFreezeAvatarChanged()" type="checkbox" name="enableFreezeAvatar"
value="enableFreezeAvatar" id="enableFreezeAvatar">
<label class="noselect sub-section__label" for="enableFreezeAvatar">freeze animated avatar</label>
</div> </div>
<h4 class="panel__subtitle">About</h4> <h4 class="panel__subtitle">About</h4>

View File

@ -29,6 +29,9 @@ export class SettingsComponent implements OnInit, OnDestroy {
disableRemoteStatusFetchingEnabled: boolean; disableRemoteStatusFetchingEnabled: boolean;
disableAvatarNotificationsEnabled: boolean; disableAvatarNotificationsEnabled: boolean;
disableSoundsEnabled: boolean; disableSoundsEnabled: boolean;
disableLangAutodetectEnabled: boolean;
enableAltLabelEnabled: boolean;
enableFreezeAvatarEnabled: boolean;
version: string; version: string;
hasPleromaAccount: boolean; hasPleromaAccount: boolean;
@ -146,6 +149,9 @@ export class SettingsComponent implements OnInit, OnDestroy {
this.twitterBridgeInstance = settings.twitterBridgeInstance; this.twitterBridgeInstance = settings.twitterBridgeInstance;
this.configuredLangs = this.languageService.getConfiguredLanguages(); this.configuredLangs = this.languageService.getConfiguredLanguages();
this.disableLangAutodetectEnabled = settings.disableLangAutodetec;
this.enableAltLabelEnabled = settings.enableAltLabel;
this.enableFreezeAvatarEnabled = settings.enableFreezeAvatar;
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -273,6 +279,27 @@ export class SettingsComponent implements OnInit, OnDestroy {
return false; return false;
} }
onEnableFreezeAvatarChanged(){
this.notifyRestartNeeded();
let settings = this.settingsService.getSettings();
settings.enableFreezeAvatar = this.enableFreezeAvatarEnabled;
this.settingsService.saveSettings(settings);
}
onEnableAltLabelChanged(){
this.notifyRestartNeeded();
let settings = this.settingsService.getSettings();
settings.enableAltLabel = this.enableAltLabelEnabled;
this.settingsService.saveSettings(settings);
}
onDisableLangAutodetectChanged() {
this.notifyRestartNeeded();
let settings = this.settingsService.getSettings();
settings.disableLangAutodetec = this.disableLangAutodetectEnabled;
this.settingsService.saveSettings(settings);
}
onDisableAutofocusChanged() { onDisableAutofocusChanged() {
this.notifyRestartNeeded(); this.notifyRestartNeeded();
let settings = this.settingsService.getSettings(); let settings = this.settingsService.getSettings();

View File

@ -1,4 +1,5 @@
<div class="image"> <div class="image">
<div class="image__alt" *ngIf="displayAltLabel">ALT</div>
<a href class="image__link" (click)="openExternal()" (auxclick)="openExternal()" title="open image"> <a href class="image__link" (click)="openExternal()" (auxclick)="openExternal()" title="open image">
<fa-icon class="image__link--icon" [icon]="faLink"></fa-icon> <fa-icon class="image__link--icon" [icon]="faLink"></fa-icon>
</a> </a>

View File

@ -29,6 +29,24 @@
opacity: 1; opacity: 1;
cursor: pointer; cursor: pointer;
} }
&__alt {
display: inline;
color: white;
z-index: 10;
position: absolute;
bottom: 5px;
left: 5px;
font-size: 10px;
font-weight: bolder;
background-color: rgba($color: #000000, $alpha: 0.5);
border-radius: 3px;
padding: 2px 5px;
}
} }
img, img,

View File

@ -1,6 +1,7 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { faLink } from "@fortawesome/free-solid-svg-icons"; import { faLink } from "@fortawesome/free-solid-svg-icons";
import { SettingsService } from '../../../../../services/settings.service';
import { Attachment } from '../../../../../services/models/mastodon.interfaces'; import { Attachment } from '../../../../../services/models/mastodon.interfaces';
@Component({ @Component({
@ -10,11 +11,16 @@ import { Attachment } from '../../../../../services/models/mastodon.interfaces';
}) })
export class AttachementImageComponent implements OnInit { export class AttachementImageComponent implements OnInit {
faLink = faLink; faLink = faLink;
displayAltLabel: boolean;
@Input() attachment: Attachment; @Input() attachment: Attachment;
@Output() openEvent = new EventEmitter(); @Output() openEvent = new EventEmitter();
constructor() { } constructor(
private readonly settingsService: SettingsService
) {
this.displayAltLabel = this.settingsService.getSettings().enableAltLabel;
}
ngOnInit() { ngOnInit() {
} }

View File

@ -64,6 +64,7 @@ export class StatusTranslateComponent implements OnInit, OnDestroy {
.then(canTranslate => { .then(canTranslate => {
if (canTranslate if (canTranslate
&& !this.status.isRemote && !this.status.isRemote
&& this.status.status.language
&& this.configuredLanguages.length > 0 && this.configuredLanguages.length > 0
&& this.configuredLanguages.findIndex(x => x.iso639 === this.status.status.language) === -1) { && this.configuredLanguages.findIndex(x => x.iso639 === this.status.status.language) === -1) {

View File

@ -2,7 +2,7 @@
<div class="reblog" *ngIf="reblog"> <div class="reblog" *ngIf="reblog">
<a class="reblog__profile-link" href title="{{ status.account.acct }}" <a class="reblog__profile-link" href title="{{ status.account.acct }}"
(click)="openAccount(status.account)" (click)="openAccount(status.account)"
(auxclick)="openUrl(status.account.url)"><span innerHTML="{{ status.account | accountEmoji }}"></span> <img *ngIf="reblog" class="reblog__avatar" src="{{ status.account.avatar | ensureHttps }}" /></a> boosted (auxclick)="openUrl(status.account.url)"><span innerHTML="{{ status.account | accountEmoji }}"></span> <img *ngIf="reblog" class="reblog__avatar" src="{{ getAvatar(status.account) | ensureHttps }}" /></a> boosted
</div> </div>
<div *ngIf="statusWrapper.status.pinned && !notificationType" class="pinned"> <div *ngIf="statusWrapper.status.pinned && !notificationType" class="pinned">
<div class="notification--icon"> <div class="notification--icon">
@ -60,9 +60,9 @@
<div [ngClass]="{'notification--status': notificationAccount }"> <div [ngClass]="{'notification--status': notificationAccount }">
<a href class="status__profile-link" title="{{displayedStatus.account.acct}}" <a href class="status__profile-link" title="{{displayedStatus.account.acct}}"
(click)="openAccount(displayedStatus.account)" (auxclick)="openUrl(displayedStatus.account.url)"> (click)="openAccount(displayedStatus.account)" (auxclick)="openUrl(displayedStatus.account.url)">
<img [class.status__avatar--boosted]="reblog || notificationAccount" class="status__avatar" src="{{ displayedStatus.account.avatar | ensureHttps }}" /> <img [class.status__avatar--boosted]="reblog || notificationAccount" class="status__avatar" src="{{ getAvatar(displayedStatus.account) | ensureHttps }}" />
<!-- <img *ngIf="reblog" class="status__avatar--reblog" src="{{ status.account.avatar }}" /> --> <!-- <img *ngIf="reblog" class="status__avatar--reblog" src="{{ status.account.avatar }}" /> -->
<img *ngIf="notificationAccount" class="notification--avatar" src="{{ notificationAccount.avatar | ensureHttps }}" /> <img *ngIf="notificationAccount" class="notification--avatar" src="{{ getAvatar(notificationAccount) | ensureHttps }}" />
<span class="status__name"> <span class="status__name">
<span class="status__name--displayname" <span class="status__name--displayname"
innerHTML="{{displayedStatus.account | accountEmoji}}"></span><span innerHTML="{{displayedStatus.account | accountEmoji}}"></span><span

View File

@ -10,6 +10,7 @@ import { EmojiConverter, EmojiTypeEnum } from '../../../tools/emoji.tools';
import { ContentWarningPolicyEnum } from '../../../states/settings.state'; import { ContentWarningPolicyEnum } from '../../../states/settings.state';
import { StatusesStateService, StatusState } from "../../../services/statuses-state.service"; import { StatusesStateService, StatusState } from "../../../services/statuses-state.service";
import { DatabindedTextComponent } from "./databinded-text/databinded-text.component"; import { DatabindedTextComponent } from "./databinded-text/databinded-text.component";
import { SettingsService } from "../../../services/settings.service";
@Component({ @Component({
selector: "app-status", selector: "app-status",
@ -44,6 +45,8 @@ export class StatusComponent implements OnInit {
isSelected: boolean; isSelected: boolean;
isRemote: boolean; isRemote: boolean;
private freezeAvatarEnabled: boolean;
hideStatus: boolean = false; hideStatus: boolean = false;
@Output() browseAccountEvent = new EventEmitter<string>(); @Output() browseAccountEvent = new EventEmitter<string>();
@ -103,6 +106,7 @@ export class StatusComponent implements OnInit {
constructor( constructor(
public elem: ElementRef, public elem: ElementRef,
private readonly toolsService: ToolsService, private readonly toolsService: ToolsService,
private readonly settingsService: SettingsService,
private readonly statusesStateService: StatusesStateService) { } private readonly statusesStateService: StatusesStateService) { }
ngOnInit() { ngOnInit() {
@ -111,12 +115,22 @@ export class StatusComponent implements OnInit {
this.statusWrapper = notification.editedStatus; this.statusWrapper = notification.editedStatus;
} }
}); });
this.freezeAvatarEnabled = this.settingsService.getSettings().enableFreezeAvatar;
} }
ngOnDestroy() { ngOnDestroy() {
if (this.statusesStateServiceSub) this.statusesStateServiceSub.unsubscribe(); if (this.statusesStateServiceSub) this.statusesStateServiceSub.unsubscribe();
} }
getAvatar(acc: Account): string {
if(this.freezeAvatarEnabled){
return acc.avatar_static;
} else {
return acc.avatar;
}
}
private ensureMentionAreDisplayed(data: string): string { private ensureMentionAreDisplayed(data: string): string {
const mentions = this.displayedStatus.mentions; const mentions = this.displayedStatus.mentions;
if (!mentions || mentions.length === 0) return data; if (!mentions || mentions.length === 0) return data;

View File

@ -30,7 +30,7 @@ export class TimeAgoPipe implements PipeTransform {
const hours = minutes / 60; const hours = minutes / 60;
const days = hours / 24; const days = hours / 24;
// const months = days / 30.416; // const months = days / 30.416;
// const years = days / 365; const years = days / 365;
if (seconds <= 59) { if (seconds <= 59) {
text = Math.round(seconds) + 's'; text = Math.round(seconds) + 's';
@ -38,8 +38,10 @@ export class TimeAgoPipe implements PipeTransform {
text = Math.round(minutes) + 'm'; text = Math.round(minutes) + 'm';
} else if (hours <= 23) { } else if (hours <= 23) {
text = Math.round(hours) + 'h'; text = Math.round(hours) + 'h';
} else { } else if (days < 365) {
text = Math.round(days) + 'd'; text = Math.round(days) + 'd';
} else {
text = Math.round(years) + 'y';
} }
if (minutes < 1) { if (minutes < 1) {

View File

@ -1,10 +1,30 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class MyElectronService { export class MyElectronService {
detectedLangSubject = new Subject<DetectedLang[]>();
constructor() { constructor() {
try {
if ((<any>window).api) {
(<any>window).api.receive("detectedLang", (data) => {
const result = [];
for (const l of data) {
let newLang = new DetectedLang(l[0], l[1]);
result.push(newLang);
}
this.detectedLangSubject.next(result);
});
}
}
catch (err) {
console.error(err);
}
this.detectLang("ceci est une phrase");
} }
setLang(lang: string) { setLang(lang: string) {
@ -17,4 +37,22 @@ export class MyElectronService {
console.error(err); console.error(err);
} }
} }
detectLang(text: string) {
try {
if ((<any>window).api) {
(<any>window).api.send("detectLang", text);
}
}
catch (err) {
console.error(err);
}
}
}
export class DetectedLang {
constructor(
public lang: string,
public score: number
) {}
} }

View File

@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { ILanguage } from '../states/settings.state'; import { ILanguage } from '../states/settings.state';
import { MyElectronService } from './electron.service'; import { DetectedLang, MyElectronService } from './electron.service';
import { SettingsService } from './settings.service'; import { SettingsService } from './settings.service';
@Injectable({ @Injectable({
@ -19,6 +19,42 @@ export class LanguageService {
) { ) {
this.configuredLanguagesChanged.next(this.getConfiguredLanguages()); this.configuredLanguagesChanged.next(this.getConfiguredLanguages());
this.selectedLanguageChanged.next(this.getSelectedLanguage()); this.selectedLanguageChanged.next(this.getSelectedLanguage());
this.electronService.detectedLangSubject.subscribe(l => {
this.detectedLanguage(l);
});
}
private detectedLanguage(lang: DetectedLang[]) {
if (!lang) return;
if (lang.length >= 1) {
const languages = this.getConfiguredLanguages();
let firstLang = lang[0].lang;
let firstLocalLang = languages.find(x => x.iso639 == firstLang);
if (firstLocalLang) {
this.setSelectedLanguage(firstLocalLang);
return;
}
if (lang.length > 1) {
firstLang = lang[1].lang;
firstLocalLang = languages.find(x => x.iso639 == firstLang);
if (firstLocalLang) {
this.setSelectedLanguage(firstLocalLang);
return;
}
}
}
}
autoDetectLang(text: string): void {
if (!text || text.length < 5) return;
if (!this.settingsService.getSettings().disableLangAutodetec) {
this.electronService.detectLang(text);
}
} }
getSelectedLanguage(): ILanguage { getSelectedLanguage(): ILanguage {

View File

@ -84,6 +84,10 @@ export class GlobalSettings {
configuredLanguages: ILanguage[] = []; configuredLanguages: ILanguage[] = [];
selectedLanguage: ILanguage; selectedLanguage: ILanguage;
disableLangAutodetec: boolean;
enableAltLabel: boolean;
enableFreezeAvatar: boolean;
} }
export interface ILanguage { export interface ILanguage {
@ -181,6 +185,9 @@ export class SettingsState {
newSettings.twitterBridgeInstance = oldSettings.twitterBridgeInstance; newSettings.twitterBridgeInstance = oldSettings.twitterBridgeInstance;
newSettings.configuredLanguages = oldSettings.configuredLanguages; newSettings.configuredLanguages = oldSettings.configuredLanguages;
newSettings.selectedLanguage = oldSettings.selectedLanguage; newSettings.selectedLanguage = oldSettings.selectedLanguage;
newSettings.disableLangAutodetec = oldSettings.disableLangAutodetec;
newSettings.enableAltLabel = oldSettings.enableAltLabel;
newSettings.enableFreezeAvatar = oldSettings.enableFreezeAvatar;
return newSettings; return newSettings;
} }