Merge branch 'develop' into topic_bump-electron

This commit is contained in:
Nicolas Constant 2020-09-13 23:28:34 +02:00 committed by GitHub
commit 386058eceb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 686 additions and 6 deletions

1
package-lock.json generated
View File

@ -1,6 +1,7 @@
{
"name": "sengi",
"version": "0.31.1",
"version": "0.27.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -16,6 +16,11 @@
</div>
</div>
<div *ngIf="enhancedTutorialActive" class="enhanced-tutorial"
[class.enhanced-tutorial__visible]="enhancedTutorialVisible">
<app-tutorial-enhanced class="enhanced-tutorial__content" (closeEvent)="closeTutorial()"></app-tutorial-enhanced>
</div>
<app-media-viewer id="media-viewer" *ngIf="openedMediaEvent" [openedMediaEvent]="openedMediaEvent"
(closeSubject)="closeMedia()" (dragenter)="dragenter($event)"></app-media-viewer>

View File

@ -171,4 +171,27 @@ app-streams-selection-footer {
}
}
}
}
.enhanced-tutorial {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.9);
z-index: 9999999;
opacity: 0;
transition: all .4s;
&__visible {
opacity: 1;
}
&__content {
display: block;
padding: 25px;
width: calc(100%);
height: calc(100%);
}
}

View File

@ -62,9 +62,6 @@ export class AppComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
// disable tutorial for future update
localStorage.setItem('tutorial', JSON.stringify(true));
this.paramsSub = this.activatedRoute.queryParams.subscribe(params => {
const code = params['code'];
if (!code) {
@ -130,6 +127,8 @@ export class AppComponent implements OnInit, OnDestroy {
this.columnEditorSub = this.navigationService.activatedPanelSubject.subscribe((event: OpenLeftPanelEvent) => {
if (event.type === LeftPanelType.Closed) {
this.floatingColumnActive = false;
this.checkEnhancedTutorial();
} else {
this.floatingColumnActive = true;
}
@ -158,6 +157,29 @@ export class AppComponent implements OnInit, OnDestroy {
});
}
enhancedTutorialActive: boolean;
enhancedTutorialVisible: boolean;
private checkEnhancedTutorial() {
let enhancedTutorialDesactivated = JSON.parse(localStorage.getItem('tutorial'));
if (!this.floatingColumnActive && !this.tutorialActive && !enhancedTutorialDesactivated) {
setTimeout(() => {
this.enhancedTutorialActive = true;
setTimeout(() => {
this.enhancedTutorialVisible = true;
}, 100);
}, 500);
}
}
closeTutorial(){
localStorage.setItem('tutorial', JSON.stringify(true));
this.enhancedTutorialVisible = false;
setTimeout(() => {
this.enhancedTutorialActive = false;
}, 400);
}
ngOnDestroy(): void {
this.streamSub.unsubscribe();
this.columnEditorSub.unsubscribe();

View File

@ -86,7 +86,10 @@ import { AttachementImageComponent } from './components/stream/status/attachemen
import { EnsureHttpsPipe } from './pipes/ensure-https.pipe';
import { UserFollowsComponent } from './components/stream/user-follows/user-follows.component';
import { AccountComponent } from './components/common/account/account.component';
import { TutorialEnhancedComponent } from './components/tutorial-enhanced/tutorial-enhanced.component';
import { NotificationsTutorialComponent } from './components/tutorial-enhanced/notifications-tutorial/notifications-tutorial.component';
import { LabelsTutorialComponent } from './components/tutorial-enhanced/labels-tutorial/labels-tutorial.component';
import { ThankyouTutorialComponent } from './components/tutorial-enhanced/thankyou-tutorial/thankyou-tutorial.component';
const routes: Routes = [
{ path: "", component: StreamsMainDisplayComponent },
@ -152,7 +155,11 @@ const routes: Routes = [
AttachementImageComponent,
EnsureHttpsPipe,
UserFollowsComponent,
AccountComponent
AccountComponent,
TutorialEnhancedComponent,
NotificationsTutorialComponent,
LabelsTutorialComponent,
ThankyouTutorialComponent
],
entryComponents: [
EmojiPickerComponent

View File

@ -148,7 +148,8 @@
<h4 class="panel__subtitle">About</h4>
<p class="version">
Sengi version: {{version}}<br />
<a href class="version__link" (click)="checkForUpdates()">check for updates</a>
<a href class="version__link" (click)="openTutorial()">open tutorial</a><br/>
<a href class="version__link" (click)="checkForUpdates()">check for updates</a>
<app-waiting-animation *ngIf="isCheckingUpdates" class="waiting-icon"></app-waiting-animation>
</p>

View File

@ -8,6 +8,7 @@ import { UserNotificationService, NotificationSoundDefinition } from '../../../s
import { ServiceWorkerService } from '../../../services/service-worker.service';
import { ContentWarningPolicy, ContentWarningPolicyEnum, TimeLineModeEnum, TimeLineHeaderEnum } from '../../../states/settings.state';
import { NotificationService } from '../../../services/notification.service';
import { NavigationService } from '../../../services/navigation.service';
@Component({
selector: 'app-settings',
@ -60,6 +61,7 @@ export class SettingsComponent implements OnInit {
}
constructor(
private readonly navigationService: NavigationService,
private formBuilder: FormBuilder,
private serviceWorkersService: ServiceWorkerService,
private readonly toolsService: ToolsService,
@ -250,6 +252,12 @@ export class SettingsComponent implements OnInit {
notifyRestartNeeded(){
this.notificationService.notifyRestartNotification('Reload to apply changes');
}
openTutorial(): boolean {
localStorage.setItem('tutorial', JSON.stringify(false));
this.navigationService.closePanel();
return false;
}
}
enum ColumnShortcut {

View File

@ -0,0 +1,69 @@
<div class="tutorial-content flexcroll">
<h2 class="tutorial-content__title-important">Labels</h2>
<p>
Sengi uses labels on statuses to describe them properly: <br />
<br />
<img class="content__expand" src="assets/img/labels.png" /><br />
</p>
<h3 class="tutorial-content__subtitle">Definitions</h3>
<br />
<div class="doc-label-wrapper">
<div class="doc-label doc-label__bot">
BOT
</div>
<div class="doc-label-definition">
The account is tagged as a bot
</div>
</div>
<div class="doc-label-wrapper">
<div class="doc-label doc-label__replies">
REPLIES
</div>
<div class="doc-label-definition">
The status has replies
</div>
</div>
<div class="doc-label-wrapper">
<div class="doc-label doc-label__thread">
THREAD
</div>
<div class="doc-label-definition">
The status is part of a thread
</div>
</div>
<div class="doc-label-wrapper">
<div class="doc-label doc-label__xpost">
X-POST
</div>
<div class="doc-label-definition">
The status was cross-posted using a software like <a href="https://moa.party/" target="_blank">MOA</a>.<br/>
<span class="doc-label-definition__gray">This functionality is limited to Local TL/Accounts due to API limitation.</span>
</div>
</div>
<div class="doc-label-wrapper">
<div class="doc-label doc-label__old">
OLD
</div>
<div class="doc-label-definition">
The status was posted more than 3 months ago.
</div>
</div>
<div class="doc-label-wrapper">
<div class="doc-label doc-label__remote">
REMOTE
</div>
<div class="doc-label-definition">
The status wasn't federated with your instance and was fetched remotely.<br/>
<span class="doc-label-definition__gray">This functionality bypasses the blocking rules of your account/instance, you can disable it in the settings.</span>
</div>
</div>
<p>
</p>
</div>

View File

@ -0,0 +1,60 @@
.doc-label-wrapper {
margin-bottom: 15px;
}
.doc-label {
width: 70px;
// height: 15px;
color: white;
text-align: center;
padding: 3px 0 2px 0;
font-size: 11px;
border-radius: 3px;
float: left;
&__bot {
background-color: #017282;
}
&__replies {
background-color: #59028f;
}
&__thread {
background-color: #007233;
}
&__xpost {
background-color: #9f5d00;
}
&__old {
background-color: #960000;
}
&__remote {
background-color: #264d94;
}
}
.doc-label-definition {
margin-left: 85px;
position: relative;
// top: -1px;
& a {
color: white;
text-decoration: underline;
&:hover, &:active, &:focus {
color: rgb(218, 218, 218);
}
}
&__gray {
display: inline-block;
margin-top: 5px;
color: #0f111a;
color: #9497a5;
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LabelsTutorialComponent } from './labels-tutorial.component';
xdescribe('LabelsTutorialComponent', () => {
let component: LabelsTutorialComponent;
let fixture: ComponentFixture<LabelsTutorialComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LabelsTutorialComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LabelsTutorialComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-labels-tutorial',
templateUrl: './labels-tutorial.component.html',
styleUrls: ['../tutorial-enhanced.component.scss', 'labels-tutorial.component.scss']
})
export class LabelsTutorialComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,32 @@
<div class="tutorial-content flexcroll">
<h2 class="tutorial-content__title-important">And... you're all set! 🎉</h2>
<p>
Please find below more information about Sengi.<br />
<br />
</p>
<h3 class="tutorial-content__subtitle">Notifications</h3>
<p>
By default, Sengi doesn't need notification timelines: it has its own way to do it.<br />
<br />
When your account will receive a new mention, follow, boost, favorite and other kind of notifications, your
avatar's icon will start flashing this way:<br />
<br />
<video class="content__center" role="application" loop autoplay>
<source src="assets/video/flashing.mp4" type="video/mp4">
</video>
<br />
If you right-click on your avatar, the account panel will automatically open and switch to the related
subsection (mentions or notifications) and show you the last entries.<br />
The flashing animation will then automatically stop:<br />
<br />
<video class="content__expand" role="application" loop autoplay>
<source src="assets/video/notification.mp4" type="video/mp4">
</video>
<br />
The aim of this mechanism is to prevent the flood of notification timelines (one per account) in the interface,
especially if you add a lot of accounts in Sengi. <br />
<br />
Of course, you can disable various parts of this mechanism in the settings like "disable avatar notification", "disable account's panel autofocus", etc.<br />
</p>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationsTutorialComponent } from './notifications-tutorial.component';
xdescribe('NotificationsTutorialComponent', () => {
let component: NotificationsTutorialComponent;
let fixture: ComponentFixture<NotificationsTutorialComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NotificationsTutorialComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NotificationsTutorialComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-notifications-tutorial',
templateUrl: './notifications-tutorial.component.html',
styleUrls: ['../tutorial-enhanced.component.scss']
})
export class NotificationsTutorialComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,36 @@
<div class="tutorial-content flexcroll">
<h2 class="tutorial-content__title-important">Thank you!</h2>
<p>
Thank you for downloading Sengi, I hope you will enjoy using this app as much as I did when making it.<br />
<br />
Please find below some useful informations and... that's it! <br />
<br />
</p>
<h3 class="tutorial-content__subtitle">The app's account</h3>
<p>
<a href="https://mastodon.social/@sengi_app" target="_blank">
<img class="content__expand content__expand--smaller" src="assets/img/account.png" /><br />
</a>
<a class="follow" href (click)="follow()" title="follow @Sengi_app with all your account(s)" [class.follow__disabled]="followingDisabled">
Follow @Sengi_app
</a>
</p>
<h3 class="tutorial-content__subtitle">Alternative builds</h3>
<p>
<a class="link link__tab" href="https://hub.docker.com/r/nicolasconstant/sengi" target="_blank">Official Docker
build</a><br />
<a class="link link__tab" href="https://snapcraft.io/sengi" target="_blank">Official Snap build</a><br />
<br />
</p>
<h3 class="tutorial-content__subtitle">Other ressources</h3>
<p>
<a class="link link__tab" href="https://nicolasconstant.github.io/sengi/" target="_blank">Official
page</a><br />
<a class="link link__tab" href="https://github.com/NicolasConstant/sengi" target="_blank">Github
Repository</a><br />
<a class="link link__tab" href="https://write.as/nicolas-constant/" target="_blank">Maintainer's dev
blog</a><br />
<br />
</p>
</div>

View File

@ -0,0 +1,29 @@
.follow {
color: white;
display: block;
border: 1px solid rgb(0, 84, 122);
border-radius: 5px;
width: 200px;
text-align: center;
padding: 10px 0;
margin: 0 auto 30px auto;
transition: all .2s;
box-shadow: inset 0 0 5px rgb(0, 84, 122);
&:hover,
&:focus {
text-decoration: none;
background-color: rgb(0, 84, 122);
}
&__disabled {
opacity: .15;
border: 1px solid gray;
&:hover,
&:focus {
text-decoration: none;
background-color: gray;
}
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ThankyouTutorialComponent } from './thankyou-tutorial.component';
describe('ThankyouTutorialComponent', () => {
let component: ThankyouTutorialComponent;
let fixture: ComponentFixture<ThankyouTutorialComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ThankyouTutorialComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ThankyouTutorialComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,41 @@
import { Component, OnInit } from '@angular/core';
import { ToolsService } from '../../../services/tools.service';
import { MastodonWrapperService } from '../../../services/mastodon-wrapper.service';
import { NotificationService } from '../../../services/notification.service';
@Component({
selector: 'app-thankyou-tutorial',
templateUrl: './thankyou-tutorial.component.html',
styleUrls: ['../tutorial-enhanced.component.scss', './thankyou-tutorial.component.scss']
})
export class ThankyouTutorialComponent implements OnInit {
followingDisabled: boolean;
constructor(
private readonly notificationService: NotificationService,
private readonly mastodonService: MastodonWrapperService,
private readonly toolsService: ToolsService) { }
ngOnInit() {
}
follow(): boolean {
if(this.followingDisabled) return;
this.followingDisabled = true;
const accounts = this.toolsService.getAllAccounts();
for (let acc of accounts) {
this.toolsService.findAccount(acc, "@sengi_app@mastodon.social")
.then(sengi => {
return this.mastodonService.follow(acc, sengi);
})
.catch(err => {
this.followingDisabled = false;
this.notificationService.notifyHttpError(err, acc);
});
}
return false;
}
}

View File

@ -0,0 +1,20 @@
<div class="tutorial">
<div class="tutorial__content">
<app-notifications-tutorial *ngIf="index === 0"></app-notifications-tutorial>
<app-labels-tutorial *ngIf="index === 1"></app-labels-tutorial>
<app-thankyou-tutorial *ngIf="index === 2"></app-thankyou-tutorial>
</div>
<div class="tutorial__footer">
<a href (click)="close()"
title="close tutorial"
class="tutorial__footer--button tutorial__footer--button--close">close</a>
<a href (click)="previous()"
title="previous"
class="tutorial__footer--button tutorial__footer--button--navigate"
[class.tutorial__footer--button--disabled]="!previousAvailable">prev.</a>
<a href (click)="next()"
title="next"
class="tutorial__footer--button tutorial__footer--button--next"
[class.tutorial__footer--button--disabled]="!nextAvailable"><span *ngIf="!tutorialEnded">next</span><span *ngIf="tutorialEnded">close</span></a>
</div>
</div>

View File

@ -0,0 +1,133 @@
@import"variables";
@import "commons";
@import "panel";
$border-color: #1d2335;
$footer-height: 30px;
.tutorial {
border: 1px solid $border-color;
background-color: $color-primary;
height: calc(100%);
border-radius: 2px;
&__content {
height: calc(100% - #{$footer-height});
}
&__footer {
border-top: 1px solid $border-color;
height: $footer-height;
&--button {
text-transform: uppercase;
font-size: 15px;
text-align: center;
display: inline-block;
height: $footer-height;
width: calc(33.33333% - 1px);
color: white;
padding-top: 4px;
transition: all .2s;
&:not(:last-child) {
border-right: 1px solid $border-color;
}
&--close {
&:hover {
text-decoration: none;
background-color: rgb(95, 0, 0);
}
}
&--navigate {
&:hover {
text-decoration: none;
background-color: rgb(0, 117, 172);
}
}
&--next {
background-color: rgb(230, 230, 230);
background-color: rgb(0, 74, 109);
background-color: rgb(0, 84, 122);
color: white;
// font-weight: bold;
&:hover {
text-decoration: none;
background-color: rgb(0, 74, 109);
background-color: rgb(0, 117, 172);
}
}
&--disabled {
color: #4e5672;
&:hover {
cursor: default;
text-decoration: none;
background-color: rgba(0, 0, 0, 0);
}
}
}
}
}
.tutorial-content {
font-size: 14px;
padding: 5px 10px;
overflow-y: auto;
height: calc(100%);
&__title {
font-size: 20px;
}
&__title-important {
font-size: 20px;
margin: 5px 0 15px 10px;
}
&__subtitle {
font-size: 16px;
}
}
.content {
&__center {
margin: auto;
width: 48px;
display: block;
}
&__expand {
margin: auto;
width: calc(100%);
max-width: 380px;
display: block;
&--smaller {
max-width: 270px;
}
}
}
.link {
color: white;
text-decoration: underline;
&:hover,
&:active,
&:focus {
color: rgb(218, 218, 218);
}
&__tab {
padding-left: 10px;
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TutorialEnhancedComponent } from './tutorial-enhanced.component';
xdescribe('TutorialEnhancedComponent', () => {
let component: TutorialEnhancedComponent;
let fixture: ComponentFixture<TutorialEnhancedComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TutorialEnhancedComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TutorialEnhancedComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,58 @@
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-tutorial-enhanced',
templateUrl: './tutorial-enhanced.component.html',
styleUrls: ['./tutorial-enhanced.component.scss']
})
export class TutorialEnhancedComponent implements OnInit {
@Output() closeEvent = new EventEmitter();
previousAvailable = false;
nextAvailable = true;
index = 0;
tutorialEnded = false;
private maxIndex = 3;
constructor() { }
ngOnInit() {
}
close(): boolean {
this.closeEvent.next();
return false;
}
previous(): boolean {
if (this.index > 0) {
this.index--;
this.checkState();
}
return false;
}
next(): boolean {
this.index++;
this.checkState();
return false;
}
private checkState() {
if(this.index >= this.maxIndex - 1){
this.tutorialEnded = true;
} else {
this.tutorialEnded = false;
}
if (this.index === 0) {
this.previousAvailable = false;
} else {
this.previousAvailable = true;
}
if (this.index === this.maxIndex){
this.close();
}
}
}

View File

@ -119,6 +119,11 @@ export class ToolsService {
return regAccounts.filter(x => x.isSelected);
}
getAllAccounts(): AccountInfo[] {
let regAccounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
return regAccounts;
}
getAccountById(accountId: string): AccountInfo {
let regAccounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
return regAccounts.find(x => x.id === accountId);

BIN
src/assets/img/account.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
src/assets/img/labels.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Binary file not shown.