refactor: fix code formatting
|
|
@ -1,5 +1,6 @@
|
|||
<!-- TODO better readable solution ? -->
|
||||
<ng-container *ngIf="(isAllDataLoadedInitially$|async)" [dir]="isRTL ? 'rtl' : 'ltr'">
|
||||
<ng-container *ngIf="(isAllDataLoadedInitially$|async)"
|
||||
[dir]="isRTL ? 'rtl' : 'ltr'">
|
||||
|
||||
<!--TODO move inside page container somehow -->
|
||||
<add-task-bar (blurred)="layoutService.hideAddTaskBar()"
|
||||
|
|
@ -24,15 +25,15 @@
|
|||
</mat-sidenav>
|
||||
|
||||
<mat-sidenav-content>
|
||||
<header class="header-wrapper"
|
||||
[class.isNotScrolled]="!(layoutService.isScrolled$|async)">
|
||||
<header [class.isNotScrolled]="!(layoutService.isScrolled$|async)"
|
||||
class="header-wrapper">
|
||||
<main-header></main-header>
|
||||
<bookmark-bar></bookmark-bar>
|
||||
<banner></banner>
|
||||
</header>
|
||||
|
||||
<div [@warpRoute]="getPage(o)"
|
||||
#routeWrapper
|
||||
<div #routeWrapper
|
||||
[@warpRoute]="getPage(o)"
|
||||
class="route-wrapper">
|
||||
<router-outlet #o="outlet"></router-outlet>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import {ChangeDetectionStrategy, Component, HostListener, ViewChild, ViewContainerRef, OnDestroy} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, Component, HostListener, OnDestroy, ViewChild, ViewContainerRef} from '@angular/core';
|
||||
import {ProjectService} from './features/project/project.service';
|
||||
import {ChromeExtensionInterfaceService} from './core/chrome-extension-interface/chrome-extension-interface.service';
|
||||
import {ShortcutService} from './core-ui/shortcut/shortcut.service';
|
||||
|
|
@ -26,7 +26,7 @@ import {TranslateService} from '@ngx-translate/core';
|
|||
import {GlobalThemeService} from './core/theme/global-theme.service';
|
||||
import {UiHelperService} from './features/ui-helper/ui-helper.service';
|
||||
import {TaskService} from './features/tasks/task.service';
|
||||
import { LanguageService } from './core/language/language.service';
|
||||
import {LanguageService} from './core/language/language.service';
|
||||
|
||||
|
||||
@Component({
|
||||
|
|
@ -41,7 +41,7 @@ import { LanguageService } from './core/language/language.service';
|
|||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AppComponent implements OnDestroy{
|
||||
export class AppComponent implements OnDestroy {
|
||||
isAllDataLoadedInitially$: Observable<boolean> = combineLatest([
|
||||
this._projectService.isRelatedDataLoadedForCurrentProject$,
|
||||
this._store.select(selectIsTaskDataLoaded),
|
||||
|
|
@ -53,9 +53,8 @@ export class AppComponent implements OnDestroy{
|
|||
|
||||
@ViewChild('notesElRef', {read: ViewContainerRef, static: false}) notesElRef: ViewContainerRef;
|
||||
@ViewChild('sideNavElRef', {read: ViewContainerRef, static: false}) sideNavElRef: ViewContainerRef;
|
||||
|
||||
private _subs : Subscription = new Subscription();
|
||||
isRTL : boolean;
|
||||
isRTL: boolean;
|
||||
private _subs: Subscription = new Subscription();
|
||||
|
||||
constructor(
|
||||
private _configService: GlobalConfigService,
|
||||
|
|
@ -70,13 +69,13 @@ export class AppComponent implements OnDestroy{
|
|||
private _globalThemeService: GlobalThemeService,
|
||||
private _uiHelperService: UiHelperService,
|
||||
private _store: Store<any>,
|
||||
private _languageService : LanguageService,
|
||||
private _languageService: LanguageService,
|
||||
public readonly layoutService: LayoutService,
|
||||
public readonly bookmarkService: BookmarkService,
|
||||
public readonly taskService: TaskService,
|
||||
public projectService: ProjectService,
|
||||
) {
|
||||
this._subs = this._languageService.isLangRTL.subscribe( (val) => {
|
||||
this._subs = this._languageService.isLangRTL.subscribe((val) => {
|
||||
this.isRTL = val;
|
||||
document.dir = this.isRTL ? 'rtl' : 'ltr';
|
||||
});
|
||||
|
|
@ -187,6 +186,9 @@ export class AppComponent implements OnDestroy{
|
|||
this.sideNavElRef.element.nativeElement.scrollIntoView({behavior: 'smooth'});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._subs.unsubscribe();
|
||||
}
|
||||
|
||||
private _initElectronErrorHandler() {
|
||||
this._electronService.ipcRenderer.on(IPC.ERROR, (ev, data: {
|
||||
|
|
@ -205,8 +207,4 @@ export class AppComponent implements OnDestroy{
|
|||
console.error(data);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._subs.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,11 +26,6 @@ const BOTH_OVER = 780;
|
|||
})
|
||||
export class LayoutService {
|
||||
isShowAddTaskBar$: Observable<boolean> = this._store$.pipe(select(selectIsShowAddTaskBar));
|
||||
|
||||
private _isShowSideNav$: Observable<boolean> = this._store$.pipe(select(selectIsShowSideNav));
|
||||
private _isShowNotes$: Observable<boolean> = this._store$.pipe(select(selectIsShowNotes));
|
||||
|
||||
|
||||
isNavAlwaysVisible$: Observable<boolean> = this._breakPointObserver.observe([
|
||||
`(min-width: ${NAV_ALWAYS_VISIBLE}px)`,
|
||||
]).pipe(map(result => result.matches));
|
||||
|
|
@ -40,7 +35,9 @@ export class LayoutService {
|
|||
isNotesOver$: Observable<boolean> = this._breakPointObserver.observe([
|
||||
`(min-width: ${BOTH_OVER}px)`,
|
||||
]).pipe(map(result => !result.matches));
|
||||
|
||||
isNavOver$: Observable<boolean> = this.isNotesNextNavOver$.pipe(map(v => !v));
|
||||
isScrolled$ = new BehaviorSubject<boolean>(false);
|
||||
private _isShowSideNav$: Observable<boolean> = this._store$.pipe(select(selectIsShowSideNav));
|
||||
isShowSideNav$: Observable<boolean> = this._isShowSideNav$.pipe(
|
||||
switchMap((isShow) => {
|
||||
return isShow
|
||||
|
|
@ -49,8 +46,6 @@ export class LayoutService {
|
|||
}),
|
||||
);
|
||||
|
||||
isNavOver$: Observable<boolean> = this.isNotesNextNavOver$.pipe(map(v => !v));
|
||||
|
||||
|
||||
// isShowNotes$: Observable<boolean> = this._isShowNotes$.pipe(
|
||||
// switchMap((isShow) => {
|
||||
|
|
@ -59,8 +54,7 @@ export class LayoutService {
|
|||
// : this.isBothAlwaysVisible$;
|
||||
// }),
|
||||
// );
|
||||
|
||||
isScrolled$ = new BehaviorSubject<boolean>(false);
|
||||
private _isShowNotes$: Observable<boolean> = this._store$.pipe(select(selectIsShowNotes));
|
||||
|
||||
constructor(
|
||||
private _store$: Store<LayoutState>,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
<!--<div class="wrapper ">-->
|
||||
<div class="wrapper">
|
||||
<button *ngIf="!(layoutService.isNavAlwaysVisible$|async)"
|
||||
<button (click)="layoutService.toggleSideNav()"
|
||||
*ngIf="!(layoutService.isNavAlwaysVisible$|async)"
|
||||
class="burger-trigger"
|
||||
(click)="layoutService.toggleSideNav()"
|
||||
mat-icon-button>
|
||||
<mat-icon>menu</mat-icon>
|
||||
</button>
|
||||
|
||||
|
||||
<div class="current-project-title"
|
||||
<div [matTooltip]="T.MH.GO_TO_TASK_LIST|translate"
|
||||
class="current-project-title"
|
||||
mat-ripple
|
||||
[matTooltip]="T.MH.GO_TO_TASK_LIST|translate"
|
||||
routerLink="work-view">{{(projectService.currentProject$|async).title}}</div>
|
||||
|
||||
<button mat-icon-button
|
||||
<button [matTooltip]="T.MH.PROJECT_SETTINGS|translate"
|
||||
class="project-settings-btn"
|
||||
[matTooltip]="T.MH.PROJECT_SETTINGS|translate"
|
||||
mat-icon-button
|
||||
routerLink="project-settings">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
|
|
@ -53,45 +53,45 @@
|
|||
|
||||
<nav class="action-nav">
|
||||
<button (click)="layoutService.showAddTaskBar()"
|
||||
mat-icon-button
|
||||
[matTooltip]="T.MH.ADD_NEW_TASK|translate">
|
||||
[matTooltip]="T.MH.ADD_NEW_TASK|translate"
|
||||
mat-icon-button>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
|
||||
<button (click)="bookmarkService.toggleBookmarks()"
|
||||
class="bookmark-btn"
|
||||
mat-icon-button
|
||||
[class.isOpen]="bookmarkService.isShowBookmarks$|async"
|
||||
[matTooltip]="T.MH.TOGGLE_SHOW_BOOKMARKS|translate">
|
||||
[matTooltip]="T.MH.TOGGLE_SHOW_BOOKMARKS|translate"
|
||||
class="bookmark-btn"
|
||||
mat-icon-button>
|
||||
<mat-icon>bookmark</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- <button *ngIf="!(layoutService.isBothAlwaysVisible$|async)"-->
|
||||
<!-- (click)="layoutService.toggleNotes()"-->
|
||||
<!-- mat-icon-button-->
|
||||
<!-- [matTooltip]="T.MH.TOGGLE_SHOW_NOTES|translate">-->
|
||||
<!-- <mat-icon [class.rotate]="(layoutService.isShowNotes$|async)"-->
|
||||
<!-- class="note-ico">comment-->
|
||||
<!-- </mat-icon>-->
|
||||
<!-- </button>-->
|
||||
<!-- <button *ngIf="!(layoutService.isBothAlwaysVisible$|async)"-->
|
||||
<!-- (click)="layoutService.toggleNotes()"-->
|
||||
<!-- mat-icon-button-->
|
||||
<!-- [matTooltip]="T.MH.TOGGLE_SHOW_NOTES|translate">-->
|
||||
<!-- <mat-icon [class.rotate]="(layoutService.isShowNotes$|async)"-->
|
||||
<!-- class="note-ico">comment-->
|
||||
<!-- </mat-icon>-->
|
||||
<!-- </button>-->
|
||||
|
||||
<button class="hide-xs"
|
||||
[matTooltip]="T.WW.HELP_PROCRASTINATION|translate"
|
||||
<button [matTooltip]="T.WW.HELP_PROCRASTINATION|translate"
|
||||
class="hide-xs"
|
||||
mat-icon-button
|
||||
routerLink="/procrastination">
|
||||
<mat-icon>flash_on</mat-icon>
|
||||
</button>
|
||||
|
||||
<div class="play-btn-wrapper">
|
||||
<div class="current-task-title"
|
||||
<div *ngIf="(isShowTaskTitle$|async)"
|
||||
@fade
|
||||
*ngIf="(isShowTaskTitle$|async)">{{(taskService.currentTask$|async)?.title}}
|
||||
class="current-task-title">{{(taskService.currentTask$|async)?.title}}
|
||||
</div>
|
||||
<button (click)="taskService.toggleStartTask()"
|
||||
[color]="(taskService.currentTaskId$|async)? 'accent': 'primary'"
|
||||
[matTooltip]="T.MH.TOGGLE_TRACK_TIME|translate"
|
||||
class="play-btn mat-elevation-z3"
|
||||
mat-mini-fab
|
||||
[matTooltip]="T.MH.TOGGLE_TRACK_TIME|translate">
|
||||
mat-mini-fab>
|
||||
<mat-icon *ngIf="!(taskService.currentTaskId$|async)">play_arrow</mat-icon>
|
||||
<mat-icon *ngIf="(taskService.currentTaskId$|async)">pause</mat-icon>
|
||||
<svg class="circle-svg"
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ button {
|
|||
display: none;
|
||||
}
|
||||
|
||||
@include darkTheme(){
|
||||
@include darkTheme() {
|
||||
background: $dark-theme-bg-lighter;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {combineLatest, Observable} from 'rxjs';
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [fadeAnimation]
|
||||
})
|
||||
export class MainHeaderComponent implements OnInit{
|
||||
export class MainHeaderComponent implements OnInit {
|
||||
T = T;
|
||||
progressCircleRadius = 10;
|
||||
circumference = this.progressCircleRadius * Math.PI * 2;
|
||||
|
|
|
|||
|
|
@ -40,17 +40,17 @@
|
|||
class="project-color"></div>
|
||||
<button (click)="switchProject(project.id, 'work-view')"
|
||||
mat-menu-item>
|
||||
<mat-icon drag-handle
|
||||
class="drag-handle">list
|
||||
<mat-icon class="drag-handle"
|
||||
drag-handle>list
|
||||
</mat-icon>
|
||||
{{project.title}}
|
||||
</button>
|
||||
|
||||
<button
|
||||
(click)="switchProject(project.id, 'project-settings')"
|
||||
class="project-settings-btn"
|
||||
mat-icon-button
|
||||
routerLinkActive="isActiveRoute"
|
||||
(click)="switchProject(project.id, 'project-settings')">
|
||||
routerLinkActive="isActiveRoute">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -81,9 +81,9 @@
|
|||
<span class="text">{{T.MH.SETTINGS|translate}}</span>
|
||||
</button>
|
||||
|
||||
<button class="route-link scroll-down-btn"
|
||||
mat-menu-item
|
||||
(click)="onScrollToNotesClick()">
|
||||
<button (click)="onScrollToNotesClick()"
|
||||
class="route-link scroll-down-btn"
|
||||
mat-menu-item>
|
||||
<mat-icon>notes</mat-icon>
|
||||
<span class="text">{{T.MH.NOTES|translate}}</span>
|
||||
<mat-icon>arrow_downwards</mat-icon>
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ import {T} from '../../../t.const';
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [slideAnimation]
|
||||
})
|
||||
export class BannerComponent{
|
||||
export class BannerComponent {
|
||||
T = T;
|
||||
|
||||
height = 120;
|
||||
private _dirtyReference: string;
|
||||
// TODO maybe improve if initial delay is annoying
|
||||
activeBanner$: Observable<Banner> = this.bannerService.activeBanner$.pipe(
|
||||
concatMap((activeBanner) => {
|
||||
|
|
@ -36,9 +37,11 @@ export class BannerComponent{
|
|||
}
|
||||
})
|
||||
);
|
||||
height = 120;
|
||||
|
||||
private _dirtyReference: string;
|
||||
constructor(
|
||||
public bannerService: BannerService,
|
||||
) {
|
||||
}
|
||||
|
||||
@ViewChild('wrapperEl', {static: false}) set wrapperEl(content: ElementRef) {
|
||||
if (content && content.nativeElement) {
|
||||
|
|
@ -46,11 +49,6 @@ export class BannerComponent{
|
|||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
public bannerService: BannerService,
|
||||
) {
|
||||
}
|
||||
|
||||
dismiss(bannerId) {
|
||||
this.bannerService.dismiss(bannerId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {DateTimeAdapter} from 'ng-pick-datetime';
|
|||
import {DateAdapter} from '@angular/material';
|
||||
import * as moment from 'moment';
|
||||
import {AUTO_SWITCH_LNGS, LanguageCode, LanguageCodeMomentMap} from '../../app.constants';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
|
@ -12,10 +12,10 @@ import { BehaviorSubject, Observable } from 'rxjs';
|
|||
export class LanguageService {
|
||||
|
||||
// Temporary solution for knowing the rtl languages
|
||||
rtlLanguages: LanguageCode[] = [LanguageCode.ar];
|
||||
// I think a better approach is to add a field in every [lang].json file to specify the direction of the language
|
||||
private isRTL : BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
isLangRTL : Observable<boolean> = this.isRTL.asObservable();
|
||||
rtlLanguages : LanguageCode[] = [LanguageCode.ar];
|
||||
private isRTL: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
isLangRTL: Observable<boolean> = this.isRTL.asObservable();
|
||||
|
||||
constructor(
|
||||
private _translateService: TranslateService,
|
||||
|
|
@ -52,7 +52,7 @@ export class LanguageService {
|
|||
this._dateTimeAdapter.setLocale(momLng);
|
||||
}
|
||||
|
||||
private _isRTL (lng : LanguageCode) {
|
||||
private _isRTL(lng: LanguageCode) {
|
||||
return this.rtlLanguages.indexOf(lng) !== -1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,17 +59,10 @@ import {migrateGlobalConfigState} from '../../features/config/migrate-global-con
|
|||
providedIn: 'root',
|
||||
})
|
||||
export class PersistenceService {
|
||||
private _isBlockSaving = false;
|
||||
|
||||
// needs to be assigned before the creations
|
||||
private _baseModels = [];
|
||||
private _projectModels = [];
|
||||
|
||||
// TODO auto generate ls keys from appDataKey where possible
|
||||
project = this._cmBase<ProjectState>(LS_PROJECT_META_LIST, 'project', migrateProjectState);
|
||||
globalConfig = this._cmBase<GlobalConfigState>(LS_GLOBAL_CFG, 'globalConfig', migrateGlobalConfigState);
|
||||
reminders = this._cmBase<Reminder[]>(LS_REMINDER, 'reminders');
|
||||
|
||||
task = this._cmProject<TaskState, Task>(
|
||||
LS_TASK_STATE,
|
||||
'task',
|
||||
|
|
@ -81,7 +74,6 @@ export class PersistenceService {
|
|||
'taskRepeatCfg',
|
||||
taskRepeatCfgReducer,
|
||||
);
|
||||
|
||||
taskArchive = this._cmProject<TaskArchive, TaskWithIssueData>(
|
||||
LS_TASK_ARCHIVE,
|
||||
'taskArchive',
|
||||
|
|
@ -120,7 +112,10 @@ export class PersistenceService {
|
|||
'obstruction',
|
||||
obstructionReducer,
|
||||
);
|
||||
|
||||
private _isBlockSaving = false;
|
||||
// needs to be assigned before the creations
|
||||
private _baseModels = [];
|
||||
private _projectModels = [];
|
||||
|
||||
constructor(
|
||||
private _snackService: SnackService,
|
||||
|
|
@ -407,6 +402,18 @@ export class PersistenceService {
|
|||
}
|
||||
}
|
||||
|
||||
async _loadAppBaseData(): Promise<AppBaseData> {
|
||||
const promises = this._baseModels.map(async (modelCfg) => {
|
||||
const modelState = await modelCfg.load();
|
||||
return {
|
||||
[modelCfg.appDataKey]: modelState,
|
||||
};
|
||||
});
|
||||
const baseDataArray: Partial<AppBaseData>[] = await Promise.all(promises);
|
||||
return Object.assign({}, ...baseDataArray);
|
||||
}
|
||||
|
||||
// TODO maybe refactor to class?
|
||||
|
||||
// ------------------
|
||||
private _cmBase<T>(
|
||||
|
|
@ -424,7 +431,6 @@ export class PersistenceService {
|
|||
return model;
|
||||
}
|
||||
|
||||
// TODO maybe refactor to class?
|
||||
// TODO maybe find a way to exec effects here as well
|
||||
private _cmProject<S, M>(
|
||||
lsKey: string,
|
||||
|
|
@ -512,17 +518,6 @@ export class PersistenceService {
|
|||
};
|
||||
}
|
||||
|
||||
async _loadAppBaseData(): Promise<AppBaseData> {
|
||||
const promises = this._baseModels.map(async (modelCfg) => {
|
||||
const modelState = await modelCfg.load();
|
||||
return {
|
||||
[modelCfg.appDataKey]: modelState,
|
||||
};
|
||||
});
|
||||
const baseDataArray: Partial<AppBaseData>[] = await Promise.all(promises);
|
||||
return Object.assign({}, ...baseDataArray);
|
||||
}
|
||||
|
||||
private async _loadAppDataForProjects(projectIds: string[]): Promise<AppDataForProjects> {
|
||||
const forProjectsData = await Promise.all(this._projectModels.map(async (modelCfg) => {
|
||||
const modelState = await this._loadForProjectIds(projectIds, modelCfg.load);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import '../../../../variables';
|
||||
|
||||
:host{
|
||||
:host {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,18 +18,18 @@
|
|||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input [(ngModel)]="attachmentCopy.title"
|
||||
[placeholder]="(T.G.TITLE|translate)"
|
||||
autofocus="autofocus"
|
||||
matInput
|
||||
name="title"
|
||||
[placeholder]="(T.G.TITLE|translate)"
|
||||
tabindex="1"
|
||||
type="text">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select [(ngModel)]="attachmentCopy.type"
|
||||
name="type"
|
||||
[placeholder]="(T.F.ATTACHMENT.DIALOG_EDIT.SELECT_TYPE|translate)"
|
||||
name="type"
|
||||
required="true"
|
||||
tabindex="1">
|
||||
<mat-option *ngFor="let type of types"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
h1, mat-dialog-actions{
|
||||
h1, mat-dialog-actions {
|
||||
:host-context([dir=rtl]) & {
|
||||
direction: rtl;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,21 +11,21 @@
|
|||
|
||||
<div class="controls-and-list-wrapper">
|
||||
<div class="list-controls">
|
||||
<button *ngIf="!isEditMode"
|
||||
<button (menuClosed)="isContextMenuDisabled = false"
|
||||
(menuOpened)="isContextMenuDisabled = true"
|
||||
*ngIf="!isEditMode"
|
||||
[matMenuTriggerFor]="menu"
|
||||
[style.pointer-events]="isContextMenuDisabled ? 'none' : 'all'"
|
||||
(menuOpened)="isContextMenuDisabled = true"
|
||||
(menuClosed)="isContextMenuDisabled = false"
|
||||
mat-stroked-button
|
||||
[title]="T.F.TASK.CMP.OPEN_TASK_MENU|translate"
|
||||
mat-stroked-button
|
||||
tabindex="1">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="isEditMode"
|
||||
(click)="isEditMode=false"
|
||||
<button (click)="isEditMode=false"
|
||||
*ngIf="isEditMode"
|
||||
[title]="T.F.TASK.CMP.OPEN_TASK_MENU|translate"
|
||||
color="accent"
|
||||
mat-stroked-button
|
||||
[title]="T.F.TASK.CMP.OPEN_TASK_MENU|translate"
|
||||
tabindex="1">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -27,13 +27,6 @@ export class BookmarkBarComponent implements OnDestroy {
|
|||
T = T;
|
||||
isContextMenuDisabled = false;
|
||||
bookmarkBarHeight = 50;
|
||||
|
||||
@ViewChild('bookmarkBar', {read: ElementRef, static: false}) set bookmarkBarEl(content: ElementRef) {
|
||||
if (content && content.nativeElement) {
|
||||
this.bookmarkBarHeight = content.nativeElement.offsetHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private _subs = new Subscription();
|
||||
|
||||
constructor(
|
||||
|
|
@ -57,6 +50,12 @@ export class BookmarkBarComponent implements OnDestroy {
|
|||
);
|
||||
}
|
||||
|
||||
@ViewChild('bookmarkBar', {read: ElementRef, static: false}) set bookmarkBarEl(content: ElementRef) {
|
||||
if (content && content.nativeElement) {
|
||||
this.bookmarkBarHeight = content.nativeElement.offsetHeight;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._subs.unsubscribe();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@
|
|||
<div class="form-wrapper">
|
||||
<mat-form-field>
|
||||
<input [(ngModel)]="bookmarkCopy.title"
|
||||
[placeholder]="(T.G.TITLE|translate)"
|
||||
autofocus="autofocus"
|
||||
matInput
|
||||
name="title"
|
||||
[placeholder]="(T.G.TITLE|translate)"
|
||||
tabindex="1"
|
||||
type="text">
|
||||
</mat-form-field>
|
||||
|
|
@ -28,8 +28,8 @@
|
|||
|
||||
<mat-form-field>
|
||||
<mat-select [(ngModel)]="bookmarkCopy.type"
|
||||
name="type"
|
||||
[placeholder]="(T.F.BOOKMARK.DIALOG_EDIT.SELECT_TYPE|translate)"
|
||||
name="type"
|
||||
required="true"
|
||||
tabindex="1">
|
||||
<mat-option *ngFor="let type of types"
|
||||
|
|
@ -44,8 +44,8 @@
|
|||
<input [(ngModel)]="bookmarkCopy.icon"
|
||||
[formControl]="iconControl"
|
||||
[matAutocomplete]="auto"
|
||||
matInput
|
||||
[placeholder]="(T.F.BOOKMARK.DIALOG_EDIT.SELECT_ICON|translate)"
|
||||
matInput
|
||||
type="text">
|
||||
<mat-icon matPrefix="">{{bookmarkCopy.icon}}</mat-icon>
|
||||
<mat-autocomplete #auto="matAutocomplete"
|
||||
|
|
|
|||
|
|
@ -31,26 +31,10 @@ import {TranslateService} from '@ngx-translate/core';
|
|||
})
|
||||
export class ConfigSectionComponent implements OnInit, OnDestroy {
|
||||
@Input() section: ConfigFormSection<{ [key: string]: any }>;
|
||||
|
||||
@Input() set cfg(v: any) {
|
||||
this._cfg = v;
|
||||
if (v && this._instance) {
|
||||
this._instance.cfg = {...v};
|
||||
}
|
||||
}
|
||||
|
||||
get cfg() {
|
||||
return this._cfg;
|
||||
}
|
||||
|
||||
@Output() save: EventEmitter<{ sectionKey: GlobalConfigSectionKey | ProjectCfgFormKey, config: any }> = new EventEmitter();
|
||||
|
||||
@ViewChild('customForm', {read: ViewContainerRef, static: true}) customFormRef: ViewContainerRef;
|
||||
|
||||
isExpanded = false;
|
||||
|
||||
private _subs = new Subscription();
|
||||
private _cfg: any;
|
||||
private _instance;
|
||||
private _viewDestroyTimeout: number;
|
||||
|
||||
|
|
@ -62,6 +46,19 @@ export class ConfigSectionComponent implements OnInit, OnDestroy {
|
|||
) {
|
||||
}
|
||||
|
||||
private _cfg: any;
|
||||
|
||||
get cfg() {
|
||||
return this._cfg;
|
||||
}
|
||||
|
||||
@Input() set cfg(v: any) {
|
||||
this._cfg = v;
|
||||
if (v && this._instance) {
|
||||
this._instance.cfg = {...v};
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.section && this.section.customSection) {
|
||||
this._loadCustomSection(this.section.customSection);
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ const EXPIRES_SAFETY_MARGIN = 5 * 60 * 1000;
|
|||
providedIn: 'root',
|
||||
})
|
||||
export class GoogleApiService {
|
||||
public isLoggedIn: boolean;
|
||||
private _session$: Observable<GoogleSession> = this._configService.googleSession$;
|
||||
|
||||
private _onTokenExpire$: Observable<number> = this._session$.pipe(
|
||||
switchMap((session) => {
|
||||
if (!session.accessToken) {
|
||||
|
|
@ -37,8 +37,6 @@ export class GoogleApiService {
|
|||
: EMPTY;
|
||||
})
|
||||
);
|
||||
|
||||
public isLoggedIn: boolean;
|
||||
public isLoggedIn$: Observable<boolean> = merge(
|
||||
this._session$,
|
||||
this._onTokenExpire$,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
<form #formRef="ngForm"
|
||||
(submit)="submit()">
|
||||
<mat-slide-toggle [ngModel]="cfg.isEnabled"
|
||||
(ngModelChange)="toggleEnabled($event)"
|
||||
<mat-slide-toggle (ngModelChange)="toggleEnabled($event)"
|
||||
[ngModel]="cfg.isEnabled"
|
||||
name="isEnabled">
|
||||
{{T.F.GOOGLE.SYNC_CFG.ENABLE|translate}}
|
||||
</mat-slide-toggle>
|
||||
|
|
@ -54,9 +54,9 @@
|
|||
class="form-inputs">
|
||||
<mat-form-field>
|
||||
<input [(ngModel)]="tmpSyncFile"
|
||||
[placeholder]="T.F.GOOGLE.SYNC_CFG.SYNC_FILE_NAME|translate"
|
||||
matInput
|
||||
name="tmpSyncFile"
|
||||
[placeholder]="T.F.GOOGLE.SYNC_CFG.SYNC_FILE_NAME|translate"
|
||||
type="text">
|
||||
<mat-icon matSuffix="">file_upload</mat-icon>
|
||||
</mat-form-field>
|
||||
|
|
@ -96,21 +96,21 @@
|
|||
<mat-form-field *ngIf="cfg.isAutoLogin && cfg.isAutoSyncToRemote">
|
||||
<input [(ngModel)]="cfg.syncInterval"
|
||||
[isAllowSeconds]="true"
|
||||
[placeholder]="T.F.GOOGLE.SYNC_CFG.SYNC_INTERVAL|translate"
|
||||
inputDuration
|
||||
matInput
|
||||
name="syncInterval"
|
||||
[placeholder]="T.F.GOOGLE.SYNC_CFG.SYNC_INTERVAL|translate"
|
||||
type="text">
|
||||
<mat-icon matSuffix="">timer</mat-icon>
|
||||
</mat-form-field>
|
||||
</section>
|
||||
|
||||
<a mat-button
|
||||
<a class="md-stroked"
|
||||
color=primary
|
||||
class="md-stroked"
|
||||
external-link
|
||||
target="_blank"
|
||||
href="https://myaccount.google.com/permissions">
|
||||
href="https://myaccount.google.com/permissions"
|
||||
mat-button
|
||||
target="_blank">
|
||||
<mat-icon>remove_circle</mat-icon>
|
||||
Revoke permissions
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -26,8 +26,7 @@ import {GOOGLE_DRIVE_FEATURE_NAME} from './store/google-drive-sync.reducer';
|
|||
DialogConfirmDriveSyncLoadComponent,
|
||||
DialogConfirmDriveSyncSaveComponent,
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
exports: [],
|
||||
})
|
||||
export class GoogleModule {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,19 @@ import {Injectable} from '@angular/core';
|
|||
import {Actions, Effect, ofType} from '@ngrx/effects';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {GlobalConfigActionTypes, UpdateGlobalConfigSection} from '../../config/store/global-config.actions';
|
||||
import {catchError, concatMap, distinctUntilChanged, exhaustMap, filter, map, mapTo, switchMap, take, tap, withLatestFrom} from 'rxjs/operators';
|
||||
import {
|
||||
catchError,
|
||||
concatMap,
|
||||
distinctUntilChanged,
|
||||
exhaustMap,
|
||||
filter,
|
||||
map,
|
||||
mapTo,
|
||||
switchMap,
|
||||
take,
|
||||
tap,
|
||||
withLatestFrom
|
||||
} from 'rxjs/operators';
|
||||
import {combineLatest, EMPTY, from, interval, Observable, of, throwError, zip} from 'rxjs';
|
||||
import {GoogleDriveSyncService} from '../google-drive-sync.service';
|
||||
import {GoogleApiService} from '../google-api.service';
|
||||
|
|
|
|||
|
|
@ -54,9 +54,9 @@
|
|||
<div><span class="author-name">{{comment.user?.login}}</span>
|
||||
<span class="when">{{T.F.GITHUB.ISSUE_CONTENT.AT|translate}} {{comment.created_at|date:'short'}}</span>
|
||||
</div>
|
||||
<div class="markdown"
|
||||
*ngIf="comment.body"
|
||||
[innerHTML]="comment?.body|markdown"></div>
|
||||
<div *ngIf="comment.body"
|
||||
[innerHTML]="comment?.body|markdown"
|
||||
class="markdown"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ export class GithubIssueService {
|
|||
// githubIssues$: Observable<GithubIssue[]> = this._store.pipe(select(selectAllGithubIssues));
|
||||
// githubIssuesEntities$: Observable<Dictionary<GithubIssue>> = this._store.pipe(select(selectGithubIssueEntities));
|
||||
|
||||
private _fineWithDeletionIssueIds = [];
|
||||
|
||||
constructor(
|
||||
private readonly _store: Store<any>,
|
||||
private readonly _persistenceService: PersistenceService,
|
||||
|
|
@ -29,9 +31,8 @@ export class GithubIssueService {
|
|||
) {
|
||||
}
|
||||
|
||||
private _fineWithDeletionIssueIds = [];
|
||||
|
||||
// META
|
||||
|
||||
// ----
|
||||
async loadStateForProject(projectId: string) {
|
||||
const lsGithubIssueState = await this._persistenceService.loadIssuesForProject(projectId, GITHUB_TYPE) as GithubIssueState;
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ export function githubIssueReducer(
|
|||
|
||||
if (tasksWithGithubIssue && tasksWithGithubIssue.length > 0) {
|
||||
const issueIds = tasksWithGithubIssue.map(task => task.issueId)
|
||||
// only remove if data is there in the first place
|
||||
// only remove if data is there in the first place
|
||||
.filter((issueId) => {
|
||||
const ids = state.ids as number[];
|
||||
const isInState = ids.includes(+issueId);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// TODO use as a checklist
|
||||
import {GithubCfg} from './github';
|
||||
import {FormlyFieldConfig} from '@ngx-formly/core';
|
||||
import {T} from '../../../t.const';
|
||||
import {ConfigFormSection, LimitedFormlyFieldConfig} from '../../config/global-config.model';
|
||||
|
||||
|
|
|
|||
|
|
@ -10,15 +10,16 @@
|
|||
|
||||
<mat-dialog-content>
|
||||
<p>{{T.F.JIRA.DIALOG_WORKLOG.SUBMIT_WORKLOG_FOR|translate}} <strong>{{issue.key}} {{issue.summary}}</strong>.</p>
|
||||
<p>{{T.F.JIRA.DIALOG_WORKLOG.CURRENTLY_LOGGED|translate}} <strong>{{(issue.timespent * 1000)|msToString}}</strong></p>
|
||||
<p>{{T.F.JIRA.DIALOG_WORKLOG.CURRENTLY_LOGGED|translate}} <strong>{{(issue.timespent * 1000)|msToString}}</strong>
|
||||
</p>
|
||||
|
||||
<div class="form-wrapper">
|
||||
<mat-form-field>
|
||||
<input [(ngModel)]="timeSpent"
|
||||
[placeholder]="T.F.JIRA.DIALOG_WORKLOG.TIME_SPENT|translate"
|
||||
inputDuration="optional"
|
||||
matInput
|
||||
name="timeSpent"
|
||||
[placeholder]="T.F.JIRA.DIALOG_WORKLOG.TIME_SPENT|translate"
|
||||
required
|
||||
type="text">
|
||||
|
||||
|
|
@ -27,9 +28,9 @@
|
|||
|
||||
<mat-form-field>
|
||||
<input [(ngModel)]="started"
|
||||
[placeholder]="T.F.JIRA.DIALOG_WORKLOG.STARTED|translate "
|
||||
matInput
|
||||
name="date"
|
||||
[placeholder]="T.F.JIRA.DIALOG_WORKLOG.STARTED|translate "
|
||||
required
|
||||
type="datetime-local">
|
||||
<mat-error>{{T.F.JIRA.DIALOG_WORKLOG.INVALID_DATE|translate}}</mat-error>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,13 @@ import {
|
|||
JIRA_REQUEST_TIMEOUT_DURATION
|
||||
} from './jira.const';
|
||||
import {ProjectService} from '../../project/project.service';
|
||||
import {mapIssueResponse, mapIssuesResponse, mapResponse, mapToSearchResults, mapTransitionResponse} from './jira-issue/jira-issue-map.util';
|
||||
import {
|
||||
mapIssueResponse,
|
||||
mapIssuesResponse,
|
||||
mapResponse,
|
||||
mapToSearchResults,
|
||||
mapTransitionResponse
|
||||
} from './jira-issue/jira-issue-map.util';
|
||||
import {JiraOriginalStatus, JiraOriginalTransition, JiraOriginalUser} from './jira-api-responses';
|
||||
import {JiraCfg} from './jira';
|
||||
import {ElectronService} from 'ngx-electron';
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
[formGroup]="credentialsFormGroup">
|
||||
<ng-template matStepLabel>{{T.F.JIRA.STEPPER.CREDENTIALS|translate}}</ng-template>
|
||||
|
||||
<p [ngClass]="HelperClasses.isHideForAdvancedFeatures"
|
||||
[innerHTML]="T.G.EXTENSION_INFO|translate">
|
||||
<p [innerHTML]="T.G.EXTENSION_INFO|translate"
|
||||
[ngClass]="HelperClasses.isHideForAdvancedFeatures">
|
||||
</p>
|
||||
|
||||
<formly-form (modelChange)="jiraCfg=$event"
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
<p [innerHTML]="T.G.EXTENSION_INFO|translate"></p>
|
||||
</div>
|
||||
|
||||
<mat-slide-toggle [ngModel]="cfg.isEnabled"
|
||||
(ngModelChange)="toggleEnabled($event)"
|
||||
<mat-slide-toggle (ngModelChange)="toggleEnabled($event)"
|
||||
[ngClass]="HelperClasses.isHideForNoAdvancedFeatures"
|
||||
[ngModel]="cfg.isEnabled"
|
||||
name="isEnabled"
|
||||
style="margin-bottom: 8px;">
|
||||
{{T.F.JIRA.CFG_CMP.ENABLE|translate}}
|
||||
|
|
@ -36,9 +36,9 @@
|
|||
<mat-form-field>
|
||||
<input [formControl]="issueSuggestionsCtrl"
|
||||
[matAutocomplete]="autoEl"
|
||||
[placeholder]="T.F.JIRA.CFG_CMP.SELECT_ISSUE_FOR_TRANSITIONS|translate"
|
||||
class="border-color-primary"
|
||||
matInput
|
||||
[placeholder]="T.F.JIRA.CFG_CMP.SELECT_ISSUE_FOR_TRANSITIONS|translate">
|
||||
matInput>
|
||||
|
||||
<mat-autocomplete #autoEl="matAutocomplete"
|
||||
(optionSelected)="updateTransitionOptions()"
|
||||
|
|
@ -92,9 +92,9 @@
|
|||
<input [(ngModel)]="cfg.storyPointFieldId"
|
||||
[formControl]="customFieldSuggestionsCtrl"
|
||||
[matAutocomplete]="storyPointAutoEl"
|
||||
[placeholder]="T.F.JIRA.CFG_CMP.STORY_POINTS|translate"
|
||||
class="border-color-primary"
|
||||
matInput
|
||||
[placeholder]="T.F.JIRA.CFG_CMP.STORY_POINTS|translate">
|
||||
matInput>
|
||||
|
||||
<mat-autocomplete #storyPointAutoEl="matAutocomplete"
|
||||
[autoActiveFirstOption]="true">
|
||||
|
|
|
|||
|
|
@ -25,6 +25,52 @@ import {ProjectService} from '../../../project/project.service';
|
|||
})
|
||||
export class JiraCfgComponent implements OnInit, OnDestroy {
|
||||
@Input() section: ConfigFormSection<JiraCfg>;
|
||||
@Output() save: EventEmitter<{ sectionKey: GlobalConfigSectionKey | ProjectCfgFormKey, config: any }> = new EventEmitter();
|
||||
T = T;
|
||||
HelperClasses = HelperClasses;
|
||||
issueSuggestionsCtrl: FormControl = new FormControl();
|
||||
customFieldSuggestionsCtrl: FormControl = new FormControl();
|
||||
customFields: any [] = [];
|
||||
customFieldsPromise: Promise<any>;
|
||||
isLoading$ = new BehaviorSubject(false);
|
||||
fields: FormlyFieldConfig[];
|
||||
form = new FormGroup({});
|
||||
options: FormlyFormOptions = {};
|
||||
filteredIssueSuggestions$: Observable<SearchResultItem[]> = this.issueSuggestionsCtrl.valueChanges.pipe(
|
||||
debounceTime(300),
|
||||
tap(() => this.isLoading$.next(true)),
|
||||
switchMap((searchTerm) => {
|
||||
return (searchTerm && searchTerm.length > 1)
|
||||
? this._jiraApiService.issuePicker$(searchTerm)
|
||||
.pipe(
|
||||
catchError(() => {
|
||||
return [];
|
||||
})
|
||||
)
|
||||
// Note: the outer array signifies the observable stream the other is the value
|
||||
: [[]];
|
||||
}),
|
||||
tap((suggestions) => {
|
||||
this.isLoading$.next(false);
|
||||
}),
|
||||
);
|
||||
filteredCustomFieldSuggestions$: Observable<any[]> = this.customFieldSuggestionsCtrl.valueChanges.pipe(
|
||||
map(value => this._filterCustomFieldSuggestions(value)),
|
||||
);
|
||||
private _subs = new Subscription();
|
||||
|
||||
constructor(
|
||||
private _jiraApiService: JiraApiService,
|
||||
private _snackService: SnackService,
|
||||
private _projectService: ProjectService,
|
||||
) {
|
||||
}
|
||||
|
||||
private _cfg: JiraCfg;
|
||||
|
||||
get cfg() {
|
||||
return this._cfg;
|
||||
}
|
||||
|
||||
// NOTE: this is legit because it might be that there is no issue provider cfg yet
|
||||
@Input() set cfg(cfg: JiraCfg) {
|
||||
|
|
@ -48,59 +94,6 @@ export class JiraCfgComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
get cfg() {
|
||||
return this._cfg;
|
||||
}
|
||||
|
||||
@Output() save: EventEmitter<{ sectionKey: GlobalConfigSectionKey | ProjectCfgFormKey, config: any }> = new EventEmitter();
|
||||
|
||||
T = T;
|
||||
HelperClasses = HelperClasses;
|
||||
|
||||
issueSuggestionsCtrl: FormControl = new FormControl();
|
||||
customFieldSuggestionsCtrl: FormControl = new FormControl();
|
||||
customFields: any [] = [];
|
||||
customFieldsPromise: Promise<any>;
|
||||
|
||||
isLoading$ = new BehaviorSubject(false);
|
||||
|
||||
fields: FormlyFieldConfig[];
|
||||
form = new FormGroup({});
|
||||
options: FormlyFormOptions = {};
|
||||
|
||||
filteredIssueSuggestions$: Observable<SearchResultItem[]> = this.issueSuggestionsCtrl.valueChanges.pipe(
|
||||
debounceTime(300),
|
||||
tap(() => this.isLoading$.next(true)),
|
||||
switchMap((searchTerm) => {
|
||||
return (searchTerm && searchTerm.length > 1)
|
||||
? this._jiraApiService.issuePicker$(searchTerm)
|
||||
.pipe(
|
||||
catchError(() => {
|
||||
return [];
|
||||
})
|
||||
)
|
||||
// Note: the outer array signifies the observable stream the other is the value
|
||||
: [[]];
|
||||
}),
|
||||
tap((suggestions) => {
|
||||
this.isLoading$.next(false);
|
||||
}),
|
||||
);
|
||||
|
||||
filteredCustomFieldSuggestions$: Observable<any[]> = this.customFieldSuggestionsCtrl.valueChanges.pipe(
|
||||
map(value => this._filterCustomFieldSuggestions(value)),
|
||||
);
|
||||
|
||||
private _subs = new Subscription();
|
||||
private _cfg: JiraCfg;
|
||||
|
||||
constructor(
|
||||
private _jiraApiService: JiraApiService,
|
||||
private _snackService: SnackService,
|
||||
private _projectService: ProjectService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.fields = this.section.items;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,8 +93,8 @@
|
|||
{{comment.created|date:'short'}}</span>
|
||||
</div>
|
||||
<div *ngIf="comment.body"
|
||||
class="markdown"
|
||||
[innerHTML]="comment.body|jiraToMarkdown|markdown"></div>
|
||||
[innerHTML]="comment.body|jiraToMarkdown|markdown"
|
||||
class="markdown"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import {JiraAttachment, JiraAuthor, JiraChangelogEntry, JiraComment, JiraIssue, } from './jira-issue.model';
|
||||
import {JiraAttachment, JiraAuthor, JiraChangelogEntry, JiraComment, JiraIssue} from './jira-issue.model';
|
||||
import {
|
||||
JiraIssueOriginal,
|
||||
JiraOriginalAttachment,
|
||||
|
|
|
|||
|
|
@ -39,25 +39,6 @@ import {HANDLED_ERROR_PROP_STR} from '../../../../../app.constants';
|
|||
@Injectable()
|
||||
export class JiraIssueEffects {
|
||||
|
||||
@Effect({dispatch: false}) pollIssueChangesAndBacklogUpdates: any = this._actions$
|
||||
.pipe(
|
||||
ofType(
|
||||
// while load state should be enough this just might fix the error of polling for inactive projects?
|
||||
ProjectActionTypes.LoadProjectRelatedDataSuccess,
|
||||
ProjectActionTypes.UpdateProjectIssueProviderCfg,
|
||||
JiraIssueActionTypes.LoadState,
|
||||
),
|
||||
withLatestFrom(
|
||||
this._projectService.isJiraEnabled$,
|
||||
this._projectService.currentJiraCfg$,
|
||||
),
|
||||
switchMap(([a, isEnabled, jiraCfg]) => {
|
||||
return (isEnabled && jiraCfg.isAutoPollTickets)
|
||||
? this._pollChangesForIssues$
|
||||
: EMPTY;
|
||||
})
|
||||
);
|
||||
|
||||
@Effect() pollNewIssuesToBacklog$: any = this._actions$
|
||||
.pipe(
|
||||
ofType(
|
||||
|
|
@ -78,7 +59,6 @@ export class JiraIssueEffects {
|
|||
: EMPTY;
|
||||
}),
|
||||
);
|
||||
|
||||
@Effect({dispatch: false}) syncIssueStateToLs$: any = this._actions$
|
||||
.pipe(
|
||||
ofType(
|
||||
|
|
@ -99,7 +79,6 @@ export class JiraIssueEffects {
|
|||
),
|
||||
tap(this._saveToLs.bind(this))
|
||||
);
|
||||
|
||||
@Effect({dispatch: false}) addOpenIssuesToBacklog$: any = this._actions$
|
||||
.pipe(
|
||||
ofType(
|
||||
|
|
@ -110,8 +89,6 @@ export class JiraIssueEffects {
|
|||
),
|
||||
tap(this._importNewIssuesToBacklog.bind(this))
|
||||
);
|
||||
|
||||
|
||||
@Effect({dispatch: false}) addWorklog$: any = this._actions$
|
||||
.pipe(
|
||||
ofType(
|
||||
|
|
@ -148,7 +125,6 @@ export class JiraIssueEffects {
|
|||
}
|
||||
})
|
||||
);
|
||||
|
||||
@Effect({dispatch: false}) checkForReassignment: any = this._actions$
|
||||
.pipe(
|
||||
ofType(
|
||||
|
|
@ -209,7 +185,6 @@ export class JiraIssueEffects {
|
|||
}
|
||||
})
|
||||
);
|
||||
|
||||
@Effect({dispatch: false}) checkForStartTransition$: Observable<any> = this._actions$
|
||||
.pipe(
|
||||
ofType(
|
||||
|
|
@ -229,7 +204,6 @@ export class JiraIssueEffects {
|
|||
return this._handleTransitionForIssue('IN_PROGRESS', jiraCfg, issueData);
|
||||
}),
|
||||
);
|
||||
|
||||
@Effect({dispatch: false})
|
||||
checkForDoneTransition$: Observable<any> = this._actions$
|
||||
.pipe(
|
||||
|
|
@ -253,7 +227,6 @@ export class JiraIssueEffects {
|
|||
return this._handleTransitionForIssue('DONE', jiraCfg, issueData);
|
||||
})
|
||||
);
|
||||
|
||||
@Effect({dispatch: false}) loadMissingIssues$: any = this._taskService.tasksWithMissingIssueData$
|
||||
.pipe(
|
||||
withLatestFrom(
|
||||
|
|
@ -272,7 +245,6 @@ export class JiraIssueEffects {
|
|||
tasks.forEach((task) => this._jiraIssueService.loadMissingIssueData(task.issueId));
|
||||
})
|
||||
);
|
||||
|
||||
@Effect() updateTaskTitleIfChanged$: any = this._actions$
|
||||
.pipe(
|
||||
ofType(
|
||||
|
|
@ -298,7 +270,6 @@ export class JiraIssueEffects {
|
|||
})
|
||||
),
|
||||
);
|
||||
|
||||
private _pollChangesForIssues$: Observable<any> = timer(JIRA_INITIAL_POLL_DELAY, JIRA_POLL_INTERVAL).pipe(
|
||||
withLatestFrom(
|
||||
this._store$.pipe(select(selectJiraIssueIds)),
|
||||
|
|
@ -316,6 +287,24 @@ export class JiraIssueEffects {
|
|||
}
|
||||
}),
|
||||
);
|
||||
@Effect({dispatch: false}) pollIssueChangesAndBacklogUpdates: any = this._actions$
|
||||
.pipe(
|
||||
ofType(
|
||||
// while load state should be enough this just might fix the error of polling for inactive projects?
|
||||
ProjectActionTypes.LoadProjectRelatedDataSuccess,
|
||||
ProjectActionTypes.UpdateProjectIssueProviderCfg,
|
||||
JiraIssueActionTypes.LoadState,
|
||||
),
|
||||
withLatestFrom(
|
||||
this._projectService.isJiraEnabled$,
|
||||
this._projectService.currentJiraCfg$,
|
||||
),
|
||||
switchMap(([a, isEnabled, jiraCfg]) => {
|
||||
return (isEnabled && jiraCfg.isAutoPollTickets)
|
||||
? this._pollChangesForIssues$
|
||||
: EMPTY;
|
||||
})
|
||||
);
|
||||
|
||||
constructor(private readonly _actions$: Actions,
|
||||
private readonly _store$: Store<any>,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// TODO use as a checklist
|
||||
import {JiraCfg} from './jira';
|
||||
import {FormlyFieldConfig} from '@ngx-formly/core';
|
||||
import {GITHUB_INITIAL_POLL_DELAY} from '../github/github.const';
|
||||
import {T} from '../../../t.const';
|
||||
import {ConfigFormSection, LimitedFormlyFieldConfig} from '../../config/global-config.model';
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@
|
|||
<div class="row">
|
||||
<mat-form-field>
|
||||
<input #mood="ngModel"
|
||||
[ngModel]="metricForDay.mood"
|
||||
(ngModelChange)="updateMood($event)"
|
||||
[ngModel]="metricForDay.mood"
|
||||
[placeholder]="T.F.METRIC.EVAL_FORM.MOOD|translate"
|
||||
matInput
|
||||
max="10"
|
||||
maxLength="2"
|
||||
min="1"
|
||||
name="mood"
|
||||
[placeholder]="T.F.METRIC.EVAL_FORM.MOOD|translate"
|
||||
required
|
||||
type="number">
|
||||
<mat-hint>{{T.F.METRIC.EVAL_FORM.MOOD_HINT|translate}}</mat-hint>
|
||||
|
|
@ -31,14 +31,14 @@
|
|||
|
||||
<mat-form-field>
|
||||
<input #productivity="ngModel"
|
||||
[ngModel]="metricForDay.productivity"
|
||||
(ngModelChange)="updateProductivity($event)"
|
||||
[ngModel]="metricForDay.productivity"
|
||||
[placeholder]="T.F.METRIC.EVAL_FORM.PRODUCTIVITY|translate"
|
||||
matInput
|
||||
max="10"
|
||||
maxLength="2"
|
||||
min="1"
|
||||
name="productivity"
|
||||
[placeholder]="T.F.METRIC.EVAL_FORM.PRODUCTIVITY|translate"
|
||||
required
|
||||
type="number">
|
||||
<mat-hint>{{T.F.METRIC.EVAL_FORM.PRODUCTIVITY_HINT|translate}}</mat-hint>
|
||||
|
|
@ -51,34 +51,34 @@
|
|||
<chip-list-input (addItem)="addImprovement($event)"
|
||||
(addNewItem)="addNewImprovement($event)"
|
||||
(removeItem)="removeImprovement($event)"
|
||||
[label]="T.F.METRIC.EVAL_FORM.IMPROVEMENTS|translate"
|
||||
[model]="metricForDay.improvements"
|
||||
[suggestions]="(improvementService.improvements$|async)"
|
||||
[label]="T.F.METRIC.EVAL_FORM.IMPROVEMENTS|translate"></chip-list-input>
|
||||
[suggestions]="(improvementService.improvements$|async)"></chip-list-input>
|
||||
|
||||
<chip-list-input (addItem)="addObstruction($event)"
|
||||
(addNewItem)="addNewObstruction($event)"
|
||||
(removeItem)="removeObstruction($event)"
|
||||
[label]="T.F.METRIC.EVAL_FORM.OBSTRUCTIONS|translate"
|
||||
[model]="metricForDay.obstructions"
|
||||
[suggestions]="(obstructionService.obstructions$|async)"
|
||||
[label]="T.F.METRIC.EVAL_FORM.OBSTRUCTIONS|translate"></chip-list-input>
|
||||
[suggestions]="(obstructionService.obstructions$|async)"></chip-list-input>
|
||||
|
||||
<chip-list-input (addItem)="addImprovementTomorrow($event)"
|
||||
(addNewItem)="addNewImprovementTomorrow($event)"
|
||||
(removeItem)="removeImprovementTomorrow($event)"
|
||||
(additionalAction)="toggleImprovementRepeat($event)"
|
||||
additionalActionIcon="repeat"
|
||||
[toggledItems]="improvementService.repeatedImprovementIds$|async"
|
||||
[additionalActionTooltip]="T.F.METRIC.EVAL_FORM.ENABLE_REPEAT_EVERY_DAY|translate"
|
||||
(removeItem)="removeImprovementTomorrow($event)"
|
||||
[additionalActionTooltipUnToggle]="T.F.METRIC.EVAL_FORM.DISABLE_REPEAT_EVERY_DAY|translate"
|
||||
[additionalActionTooltip]="T.F.METRIC.EVAL_FORM.ENABLE_REPEAT_EVERY_DAY|translate"
|
||||
[label]="T.F.METRIC.EVAL_FORM.IMPROVEMENTS_TOMORROW|translate"
|
||||
[model]="metricForDay.improvementsTomorrow"
|
||||
[suggestions]="(improvementService.improvements$|async)"
|
||||
[label]="T.F.METRIC.EVAL_FORM.IMPROVEMENTS_TOMORROW|translate"></chip-list-input>
|
||||
[toggledItems]="improvementService.repeatedImprovementIds$|async"
|
||||
additionalActionIcon="repeat"></chip-list-input>
|
||||
</form>
|
||||
</div>
|
||||
<div style="text-align: center; margin-top: 8px;margin-bottom: 16px;">
|
||||
<button mat-button
|
||||
(click)="addNote()">
|
||||
<button (click)="addNote()"
|
||||
mat-button>
|
||||
<mat-icon>add</mat-icon>
|
||||
{{T.F.METRIC.EVAL_FORM.ADD_NOTE_FOR_TOMORROW|translate}}
|
||||
{{T.F.METRIC.EVAL_FORM.ADD_NOTE_FOR_TOMORROW|translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,24 +29,16 @@ import {MatDialog} from '@angular/material/dialog';
|
|||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class EvaluationSheetComponent implements OnDestroy, OnInit {
|
||||
@Input() set day(val: string) {
|
||||
this.day$.next(val);
|
||||
}
|
||||
|
||||
@Output() save = new EventEmitter<any>();
|
||||
|
||||
T = T;
|
||||
metricForDay: MetricCopy;
|
||||
|
||||
day$ = new BehaviorSubject<string>(getWorklogStr());
|
||||
// isForToday$: Observable<boolean> = this.day$.pipe(map(day => day === getWorklogStr()));
|
||||
|
||||
private _metricForDay$ = this.day$.pipe(
|
||||
switchMap((day) => this._metricService.getMetricForDayOrDefaultWithCheckedImprovements$(day)),
|
||||
);
|
||||
// isForToday$: Observable<boolean> = this.day$.pipe(map(day => day === getWorklogStr()));
|
||||
private _subs = new Subscription();
|
||||
|
||||
|
||||
constructor(
|
||||
public obstructionService: ObstructionService,
|
||||
public improvementService: ImprovementService,
|
||||
|
|
@ -58,6 +50,10 @@ export class EvaluationSheetComponent implements OnDestroy, OnInit {
|
|||
) {
|
||||
}
|
||||
|
||||
@Input() set day(val: string) {
|
||||
this.day$.next(val);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._subs.add(this._metricForDay$.subscribe(metric => {
|
||||
this.metricForDay = metric;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
<div class="improvements"
|
||||
[@improvementBanner]="improvements.length">
|
||||
<div class="improvement"
|
||||
mat-ripple
|
||||
*ngFor="let improvement of improvements;">
|
||||
<button mat-icon-button
|
||||
disableRipple
|
||||
class="dismiss"
|
||||
<div [@improvementBanner]="improvements.length"
|
||||
class="improvements">
|
||||
<div *ngFor="let improvement of improvements;"
|
||||
class="improvement"
|
||||
mat-ripple>
|
||||
<button (click)="dismiss(improvement)"
|
||||
[matTooltip]="T.G.DISMISS|translate"
|
||||
(click)="dismiss(improvement)">
|
||||
class="dismiss"
|
||||
disableRipple
|
||||
mat-icon-button>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
|
||||
<div class="title">{{improvement?.title}}</div>
|
||||
|
||||
<button mat-icon-button
|
||||
disableRipple
|
||||
class="check"
|
||||
<button (click)="check(improvement)"
|
||||
[matTooltip]="T.F.METRIC.BANNER.CHECK|translate"
|
||||
(click)="check(improvement)">
|
||||
class="check"
|
||||
disableRipple
|
||||
mat-icon-button>
|
||||
<mat-icon>check</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
<!--[matDatepicker]="myDatepicker"-->
|
||||
<input
|
||||
[(ngModel)]="title"
|
||||
[placeholder]="T.F.NOTE.D_ADD_REMINDER.L_TITLE|translate"
|
||||
matInput
|
||||
name="title"
|
||||
[placeholder]="T.F.NOTE.D_ADD_REMINDER.L_TITLE|translate"
|
||||
required
|
||||
type="text">
|
||||
<mat-error>{{T.F.NOTE.D_ADD_REMINDER.E_ENTER_TITLE|translate}}</mat-error>
|
||||
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
<datetime-input (modelChange)="dateTime=$event"
|
||||
[model]="dateTime"
|
||||
[placeholder]="T.F.NOTE.D_ADD_REMINDER.L_DATETIME|translate"
|
||||
[required]="true"
|
||||
name="reminderDate"
|
||||
[placeholder]="T.F.NOTE.D_ADD_REMINDER.L_DATETIME|translate"></datetime-input>
|
||||
name="reminderDate"></datetime-input>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
<textarea (keydown)="keydownHandler($event)"
|
||||
(ngModelChange)="saveTmp()"
|
||||
[(ngModel)]="noteContent"
|
||||
[placeholder]="T.F.NOTE.D_ADD.NOTE_LABEL|translate"
|
||||
matTextareaAutosize
|
||||
name="noteContent"
|
||||
[placeholder]="T.F.NOTE.D_ADD.NOTE_LABEL|translate"
|
||||
rows="6"></textarea>
|
||||
</div>
|
||||
<datetime-input (modelChange)="reminderDate=$event"
|
||||
[model]="reminderDate"
|
||||
name="reminderData"
|
||||
[placeholder]="T.F.NOTE.D_ADD.DATETIME_LABEL|translate"
|
||||
name="reminderData"
|
||||
style="width: 100%;"></datetime-input>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
:host-context([dir=rtl]) {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
|
@ -12,7 +13,7 @@ textarea {
|
|||
padding: $s;
|
||||
min-height: $s*5;
|
||||
|
||||
@include mq (xs) {
|
||||
@include mq(xs) {
|
||||
min-height: 220px;
|
||||
width: 500px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
<mat-dialog-actions align="end">
|
||||
<div class="wrap-buttons">
|
||||
<button [matMenuTriggerFor]="snoozeMenu"
|
||||
(dblclick)="snooze(10)"
|
||||
<button (dblclick)="snooze(10)"
|
||||
[matMenuTriggerFor]="snoozeMenu"
|
||||
color="primary"
|
||||
mat-button
|
||||
type="button">
|
||||
|
|
@ -41,18 +41,18 @@
|
|||
|
||||
|
||||
<mat-menu #snoozeMenu="matMenu">
|
||||
<button mat-menu-item
|
||||
(click)="snooze(10)">
|
||||
<button (click)="snooze(10)"
|
||||
mat-menu-item>
|
||||
<mat-icon>snooze</mat-icon>
|
||||
{{T.G.MINUTES|translate:{m: 10} }}
|
||||
</button>
|
||||
<button mat-menu-item
|
||||
(click)="snooze(30)">
|
||||
<button (click)="snooze(30)"
|
||||
mat-menu-item>
|
||||
<mat-icon>snooze</mat-icon>
|
||||
{{T.G.MINUTES|translate:{m: 30} }}
|
||||
</button>
|
||||
<button mat-menu-item
|
||||
(click)="snooze(60)">
|
||||
<button (click)="snooze(60)"
|
||||
mat-menu-item>
|
||||
<mat-icon>snooze</mat-icon>
|
||||
{{T.G.MINUTES|translate:{m: 60} }}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ $noteFontSize: 12px;
|
|||
:host {
|
||||
display: block;
|
||||
margin-right: $s;
|
||||
|
||||
:host-context([dir=rtl]) {
|
||||
margin-left: $s;
|
||||
margin-right: unset;
|
||||
|
|
@ -25,11 +26,12 @@ $noteFontSize: 12px;
|
|||
box-shadow: $whiteframe-shadow-3dp;
|
||||
//outline: 2px dashed $c-accent;
|
||||
}
|
||||
|
||||
&.isFocused {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
&.isImg {
|
||||
&.isImg {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +50,7 @@ $noteFontSize: 12px;
|
|||
right: unset;
|
||||
left: -$s*0.75;
|
||||
}
|
||||
|
||||
// fixes drag and drop on mobile
|
||||
touch-action: none;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
<mat-icon>add</mat-icon>
|
||||
{{T.F.NOTE.NOTES_CMP.ADD_BTN|translate}}
|
||||
</button>
|
||||
<button mat-icon-button
|
||||
(click)="onScrollToSidenavClick()">
|
||||
<button (click)="onScrollToSidenavClick()"
|
||||
mat-icon-button>
|
||||
<mat-icon>arrow_upwards</mat-icon>
|
||||
</button>
|
||||
<!-- <button mat-icon-button>-->
|
||||
|
|
|
|||
|
|
@ -66,16 +66,16 @@
|
|||
flaws.</p>
|
||||
|
||||
<mat-form-field>
|
||||
<textarea matInput
|
||||
[placeholder]="T.F.PROCRASTINATION.REFRAME.TL1|translate"></textarea>
|
||||
<textarea [placeholder]="T.F.PROCRASTINATION.REFRAME.TL1|translate"
|
||||
matInput></textarea>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<textarea matInput
|
||||
[placeholder]="T.F.PROCRASTINATION.REFRAME.TL2|translate"></textarea>
|
||||
<textarea [placeholder]="T.F.PROCRASTINATION.REFRAME.TL2|translate"
|
||||
matInput></textarea>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<textarea matInput
|
||||
[placeholder]="T.F.PROCRASTINATION.REFRAME.TL3|translate"></textarea>
|
||||
<textarea [placeholder]="T.F.PROCRASTINATION.REFRAME.TL3|translate"
|
||||
matInput></textarea>
|
||||
</mat-form-field>
|
||||
</section>
|
||||
</mat-tab>
|
||||
|
|
@ -88,7 +88,8 @@
|
|||
|
||||
<task [task]="currentTask"></task>
|
||||
|
||||
<p style="margin-top: 16px;" [innerHTML]="T.F.PROCRASTINATION.SPLIT_UP.OUTRO|translate"></p>
|
||||
<p [innerHTML]="T.F.PROCRASTINATION.SPLIT_UP.OUTRO|translate"
|
||||
style="margin-top: 16px;"></p>
|
||||
</section>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {Subscription} from 'rxjs';
|
|||
import {loadFromSessionStorage, saveToSessionStorage} from '../../../../core/persistence/local-storage';
|
||||
import {GithubCfg} from '../../../issue/github/github';
|
||||
import {DialogGithubInitialSetupComponent} from '../../../issue/github/dialog-github-initial-setup/dialog-github-initial-setup.component';
|
||||
import {DEFAULT_ISSUE_PROVIDER_CFGS, GITHUB_TYPE, JIRA_TYPE} from '../../../issue/issue.const';
|
||||
import {GITHUB_TYPE} from '../../../issue/issue.const';
|
||||
import {T} from '../../../../t.const';
|
||||
import {DEFAULT_JIRA_CFG} from '../../../issue/jira/jira.const';
|
||||
import {DEFAULT_GITHUB_CFG} from '../../../issue/github/github.const';
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ h1, mat-dialog-actions, help-section {
|
|||
direction: rtl;
|
||||
}
|
||||
}
|
||||
|
||||
.form-wrapper {
|
||||
min-width: 280px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<form (submit)="addTask()"
|
||||
[class.mat-elevation-z2]="isElevated"
|
||||
[class.mat-elevation-z1]="!isElevated"
|
||||
[class.mat-elevation-z2]="isElevated"
|
||||
class="add-task-form">
|
||||
<div *ngIf="(isLoading$|async)"
|
||||
class="spinner">
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
<div class="form-wrapper">
|
||||
<datetime-input (modelChange)="dateTime=$event"
|
||||
[model]="dateTime"
|
||||
[placeholder]="T.F.TASK.D_REMINDER_ADD.DATETIME_FOR|translate"
|
||||
[required]="true"
|
||||
name="reminderDate"
|
||||
[placeholder]="T.F.TASK.D_REMINDER_ADD.DATETIME_FOR|translate"></datetime-input>
|
||||
name="reminderDate"></datetime-input>
|
||||
|
||||
<div *ngIf="isMoveToBacklogPossible"
|
||||
style="margin: 16px 0;">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import {ChangeDetectionStrategy, Component, Inject} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
|
||||
import {Task} from '../task.model';
|
||||
import {TaskService} from '../task.service';
|
||||
import {ReminderCopy} from '../../reminder/reminder.model';
|
||||
import {ReminderService} from '../../reminder/reminder.service';
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
<mat-dialog-content>
|
||||
<help-section>
|
||||
<div class="side-info"
|
||||
[innerHTML]="T.F.TASK.D_TIME_FOR_DAY.HELP|translate">
|
||||
<div [innerHTML]="T.F.TASK.D_TIME_FOR_DAY.HELP|translate"
|
||||
class="side-info">
|
||||
</div>
|
||||
</help-section>
|
||||
|
||||
|
|
@ -16,9 +16,9 @@
|
|||
<mat-form-field>
|
||||
<input [(ngModel)]="newEntry.date"
|
||||
[matDatepicker]="myDatepicker"
|
||||
[placeholder]="T.F.TASK.D_TIME_FOR_DAY.DATE|translate"
|
||||
matInput
|
||||
name="date"
|
||||
[placeholder]="T.F.TASK.D_TIME_FOR_DAY.DATE|translate"
|
||||
required
|
||||
type="text">
|
||||
<mat-error>{{T.V.E_DATETIME|translate}}</mat-error>
|
||||
|
|
@ -39,14 +39,14 @@
|
|||
<mat-dialog-actions align="end">
|
||||
<button [mat-dialog-close]="true"
|
||||
color="primary"
|
||||
type="button"
|
||||
mat-button>
|
||||
mat-button
|
||||
type="button">
|
||||
{{T.G.CANCEL|translate}}
|
||||
</button>
|
||||
|
||||
<button type="submit"
|
||||
color="primary"
|
||||
mat-stroked-button>
|
||||
<button color="primary"
|
||||
mat-stroked-button
|
||||
type="submit">
|
||||
<mat-icon>save</mat-icon>
|
||||
{{T.G.SAVE|translate}}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
<mat-dialog-content>
|
||||
<help-section>
|
||||
<div class="side-info"
|
||||
[innerHTML]="T.F.TASK.D_TIME_FOR_DAY.HELP|translate">
|
||||
<div [innerHTML]="T.F.TASK.D_TIME_FOR_DAY.HELP|translate"
|
||||
class="side-info">
|
||||
</div>
|
||||
</help-section>
|
||||
|
||||
|
|
@ -27,18 +27,18 @@
|
|||
class="other-day">
|
||||
<mat-form-field class="mat-icon-float mat-block">
|
||||
<input [(ngModel)]="timeSpentOnDayCopy[strDate]"
|
||||
[placeholder]="T.F.TASK.D_TIME.TIME_SPENT_ON|translate:{date:strDate}"
|
||||
inputDuration="optional"
|
||||
matInput
|
||||
name="timeSpentOnDay{{strDate}}"
|
||||
[placeholder]="T.F.TASK.D_TIME.TIME_SPENT_ON|translate:{date:strDate}"
|
||||
type="text">
|
||||
|
||||
<mat-icon matPrefix>timer</mat-icon>
|
||||
</mat-form-field>
|
||||
<button (click)="deleteValue(strDate)"
|
||||
[title]="T.F.TASK.D_TIME.DELETE_FOR|translate"
|
||||
color=""
|
||||
mat-icon-button
|
||||
[title]="T.F.TASK.D_TIME.DELETE_FOR|translate"
|
||||
type="button">
|
||||
<mat-icon>delete_forever</mat-icon>
|
||||
</button>
|
||||
|
|
@ -59,15 +59,15 @@
|
|||
<mat-dialog-actions align="end">
|
||||
<button [mat-dialog-close]="true"
|
||||
color="primary"
|
||||
type="button"
|
||||
mat-button>
|
||||
mat-button
|
||||
type="button">
|
||||
{{T.G.CANCEL|translate}}
|
||||
</button>
|
||||
|
||||
<button (click)="submit()"
|
||||
type="submit"
|
||||
color="primary"
|
||||
mat-stroked-button>
|
||||
mat-stroked-button
|
||||
type="submit">
|
||||
<mat-icon>save</mat-icon>
|
||||
{{T.G.SAVE|translate}}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@
|
|||
</button>
|
||||
|
||||
<ng-container *ngIf="!isError">
|
||||
<button [matMenuTriggerFor]="snoozeMenu"
|
||||
(dblclick)="snooze(10)"
|
||||
<button (dblclick)="snooze(10)"
|
||||
[disabled]="isDisableControls"
|
||||
[matMenuTriggerFor]="snoozeMenu"
|
||||
color="primary"
|
||||
mat-button
|
||||
type="button">
|
||||
|
|
@ -64,28 +64,32 @@
|
|||
type="button">
|
||||
<mat-icon>play_arrow</mat-icon>
|
||||
{{!isForCurrentProject
|
||||
? (T.F.TASK.D_REMINDER_VIEW.SWITCH_PROJECT_START|translate)
|
||||
: (T.F.TASK.D_REMINDER_VIEW.START|translate)}}
|
||||
? (T.F.TASK.D_REMINDER_VIEW.SWITCH_PROJECT_START|translate)
|
||||
: (T.F.TASK.D_REMINDER_VIEW.START|translate)}}
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
|
||||
<mat-menu #snoozeMenu="matMenu">
|
||||
<button mat-menu-item
|
||||
(click)="snooze(10)">
|
||||
<mat-icon>snooze</mat-icon>{{T.G.MINUTES|translate:{m: 10} }}
|
||||
<button (click)="snooze(10)"
|
||||
mat-menu-item>
|
||||
<mat-icon>snooze</mat-icon>
|
||||
{{T.G.MINUTES|translate:{m: 10} }}
|
||||
</button>
|
||||
<button mat-menu-item
|
||||
(click)="snooze(30)">
|
||||
<mat-icon>snooze</mat-icon>{{T.G.MINUTES|translate:{m: 30} }}
|
||||
<button (click)="snooze(30)"
|
||||
mat-menu-item>
|
||||
<mat-icon>snooze</mat-icon>
|
||||
{{T.G.MINUTES|translate:{m: 30} }}
|
||||
</button>
|
||||
<button mat-menu-item
|
||||
(click)="snooze(60)">
|
||||
<mat-icon>snooze</mat-icon>{{T.G.MINUTES|translate:{m: 60} }}
|
||||
<button (click)="snooze(60)"
|
||||
mat-menu-item>
|
||||
<mat-icon>snooze</mat-icon>
|
||||
{{T.G.MINUTES|translate:{m: 60} }}
|
||||
</button>
|
||||
<button mat-menu-item
|
||||
(click)="editReminder()">
|
||||
<mat-icon>edit</mat-icon>{{T.G.EDIT|translate}}
|
||||
<button (click)="editReminder()"
|
||||
mat-menu-item>
|
||||
<mat-icon>edit</mat-icon>
|
||||
{{T.G.EDIT|translate}}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {ChangeDetectionStrategy, Component, Inject, OnDestroy} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog';
|
||||
import {Reminder} from '../../reminder/reminder.model';
|
||||
import {Task, TaskWithReminderData} from '../task.model';
|
||||
import {Task} from '../task.model';
|
||||
import {TaskService} from '../task.service';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {ReminderService} from '../../reminder/reminder.service';
|
||||
|
|
@ -47,15 +47,15 @@ export class DialogViewTaskReminderComponent implements OnDestroy {
|
|||
}));
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._subs.unsubscribe();
|
||||
}
|
||||
|
||||
get isError() {
|
||||
// just for this dialog we make an exception about using getters
|
||||
return !this.task && this.isForCurrentProject;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._subs.unsubscribe();
|
||||
}
|
||||
|
||||
play() {
|
||||
this.isDisableControls = true;
|
||||
if (this.isForCurrentProject) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<mat-form-field>
|
||||
<input [formControl]="taskSelectCtrl"
|
||||
[matAutocomplete]="auto"
|
||||
matInput
|
||||
[placeholder]="T.F.TASK.SELECT_OR_CREATE|translate"
|
||||
matInput
|
||||
required>
|
||||
<mat-icon *ngIf="!isCreate"
|
||||
matSuffix="">track_changes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<ng-container *ngIf="type==='input'">
|
||||
<section class="input-item"
|
||||
(click)="onInputItemClick()">
|
||||
<section (click)="onInputItemClick()"
|
||||
class="input-item">
|
||||
<div class="input-item__title">
|
||||
<ng-content select="[input-title]"></ng-content>
|
||||
</div>
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
<ng-content select="[input-value]"></ng-content>
|
||||
</div>
|
||||
|
||||
<button mat-icon-button
|
||||
<button class="input-item__edit-btn"
|
||||
color="accent"
|
||||
class="input-item__edit-btn">
|
||||
mat-icon-button>
|
||||
<mat-icon>{{inputIcon}}</mat-icon>
|
||||
</button>
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ export class TaskAdditionalInfoItemComponent {
|
|||
|
||||
@HostBinding('tabindex') tabindex = 3;
|
||||
|
||||
constructor(
|
||||
public elementRef: ElementRef
|
||||
) {
|
||||
}
|
||||
|
||||
@HostListener('keydown', ['$event']) onKeyDown(ev: KeyboardEvent) {
|
||||
const tagName = (ev.target as HTMLElement).tagName.toLowerCase();
|
||||
|
|
@ -55,11 +59,6 @@ export class TaskAdditionalInfoItemComponent {
|
|||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
public elementRef: ElementRef
|
||||
) {
|
||||
}
|
||||
|
||||
focusEl() {
|
||||
this.elementRef.nativeElement.focus();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<better-drawer-container [isOver]="isAlwaysOver || (layoutService.isNotesOver$|async)"
|
||||
<better-drawer-container (wasClosed)="taskService.setSelectedId(null)"
|
||||
[isOpen]="taskService.selectedTask$|async"
|
||||
(wasClosed)="taskService.setSelectedId(null)"
|
||||
[isOver]="isAlwaysOver || (layoutService.isNotesOver$|async)"
|
||||
[sideWidth]="40">
|
||||
<ng-container better-drawer-content>
|
||||
<ng-content></ng-content>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {animate, style, transition, trigger} from '@angular/animations';
|
||||
import {ANI_ENTER_TIMING, ANI_LEAVE_TIMING} from '../../../ui/animations/animation.const';
|
||||
import {ANI_ENTER_TIMING} from '../../../ui/animations/animation.const';
|
||||
|
||||
const ANI = [
|
||||
style({opacity: 0, transform: 'scale(0.9)'}),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<div class="wrapper"
|
||||
*ngIf="task.id"
|
||||
[@taskAdditionalInfoTaskChange]="task.id">
|
||||
<div *ngIf="task.id"
|
||||
[@taskAdditionalInfoTaskChange]="task.id"
|
||||
class="wrapper">
|
||||
|
||||
<task-additional-info-item
|
||||
class="input-item --estimate"
|
||||
*ngIf="task.parentId"
|
||||
[inputIcon]="'arrow_forward'"
|
||||
(collapseParent)="collapseParent()"
|
||||
(editActionTriggered)="taskService.setSelectedId(task.parentId)"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
(collapseParent)="collapseParent()">
|
||||
*ngIf="task.parentId"
|
||||
[inputIcon]="'arrow_forward'"
|
||||
class="input-item --estimate">
|
||||
<ng-container input-title>
|
||||
<mat-icon>subdirectory_arrow_right</mat-icon>
|
||||
<span>{{T.F.TASK.ADDITIONAL_INFO.PARENT|translate}}</span>
|
||||
|
|
@ -22,12 +22,12 @@
|
|||
</task-additional-info-item>
|
||||
|
||||
<task-additional-info-item
|
||||
class="input-item --estimate"
|
||||
*ngIf="!task.subTasks?.length"
|
||||
[inputIcon]="'edit'"
|
||||
(collapseParent)="collapseParent()"
|
||||
(editActionTriggered)="estimateTime()"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
(collapseParent)="collapseParent()">
|
||||
*ngIf="!task.subTasks?.length"
|
||||
[inputIcon]="'edit'"
|
||||
class="input-item --estimate">
|
||||
<ng-container input-title>
|
||||
<mat-icon>timer</mat-icon>
|
||||
<span>{{T.F.TASK.ADDITIONAL_INFO.TIME|translate}}</span>
|
||||
|
|
@ -47,33 +47,33 @@
|
|||
</task-additional-info-item>
|
||||
|
||||
<task-additional-info-item
|
||||
class="input-item"
|
||||
*ngIf="!task.repeatCfgId"
|
||||
[inputIcon]="task.reminderId ? 'edit': 'add'"
|
||||
(collapseParent)="collapseParent()"
|
||||
(editActionTriggered)="editReminder()"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
(collapseParent)="collapseParent()">
|
||||
*ngIf="!task.repeatCfgId"
|
||||
[inputIcon]="task.reminderId ? 'edit': 'add'"
|
||||
class="input-item">
|
||||
<ng-container input-title>
|
||||
<mat-icon>alarm</mat-icon>
|
||||
<span *ngIf="task.reminderId">{{T.F.TASK.ADDITIONAL_INFO.REMINDER|translate}}</span>
|
||||
<span *ngIf="!task.reminderId">{{T.F.TASK.ADDITIONAL_INFO.SCHEDULE_TASK|translate}}</span>
|
||||
</ng-container>
|
||||
<ng-container input-value>
|
||||
<div class="reminder-value"
|
||||
matTooltipPosition="left"
|
||||
*ngIf="reminderData$|async as reminderData"
|
||||
[matTooltip]="reminderData.remindAt|date:'short'">
|
||||
<div *ngIf="reminderData$|async as reminderData"
|
||||
[matTooltip]="reminderData.remindAt|date:'short'"
|
||||
class="reminder-value"
|
||||
matTooltipPosition="left">
|
||||
{{reminderData.remindAt|humanizeTimestamp}}
|
||||
</div>
|
||||
</ng-container>
|
||||
</task-additional-info-item>
|
||||
|
||||
<task-additional-info-item
|
||||
[type]="'panel'"
|
||||
(collapseParent)="collapseParent()"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
*ngIf="task.issueData"
|
||||
[expanded]="true"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
(collapseParent)="collapseParent()">
|
||||
[type]="'panel'">
|
||||
<ng-container panel-header>
|
||||
<issue-header [task]="task"></issue-header>
|
||||
</ng-container>
|
||||
|
|
@ -87,11 +87,11 @@
|
|||
|
||||
<task-additional-info-item
|
||||
#noteWrapperElRef
|
||||
[type]="'panel'"
|
||||
[expanded]="task.notes || (!(task.issueId) && !task.attachmentIds?.length)"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
(collapseParent)="collapseParent()"
|
||||
(editActionTriggered)="isFocusNotes=true">
|
||||
(editActionTriggered)="isFocusNotes=true"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
[expanded]="task.notes || (!(task.issueId) && !task.attachmentIds?.length)"
|
||||
[type]="'panel'">
|
||||
<ng-container panel-header>
|
||||
<mat-icon *ngIf="task.notes">chat</mat-icon>
|
||||
<mat-icon *ngIf="!task.notes">chat_bubble_outline</mat-icon>
|
||||
|
|
@ -99,24 +99,24 @@
|
|||
</ng-container>
|
||||
|
||||
<ng-container panel-content>
|
||||
<inline-markdown (changed)="changeTaskNotes($event); isFocusNotes=false"
|
||||
(blur)="isFocusNotes=false"
|
||||
<inline-markdown (blur)="isFocusNotes=false"
|
||||
(blurred)="isFocusNotes=false"
|
||||
(changed)="changeTaskNotes($event); isFocusNotes=false"
|
||||
(keyboardUnToggle)="focusItem(noteWrapperElRef)"
|
||||
[isFocus]="isFocusNotes"
|
||||
[model]="task.notes"
|
||||
[isShowControls]="true"></inline-markdown>
|
||||
[isShowControls]="true"
|
||||
[model]="task.notes"></inline-markdown>
|
||||
</ng-container>
|
||||
</task-additional-info-item>
|
||||
|
||||
|
||||
<task-additional-info-item
|
||||
#attachmentPanelElRef
|
||||
[type]="'panel'"
|
||||
[expanded]="true"
|
||||
*ngIf="issueAttachments?.length || (localAttachments$|async)?.length"
|
||||
(collapseParent)="collapseParent()"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
(collapseParent)="collapseParent()">
|
||||
*ngIf="issueAttachments?.length || (localAttachments$|async)?.length"
|
||||
[expanded]="true"
|
||||
[type]="'panel'">
|
||||
<ng-container panel-header>
|
||||
<mat-icon>attachment</mat-icon>
|
||||
<span>{{T.F.TASK.ADDITIONAL_INFO.ATTACHMENTS|translate:{nr: issueAttachments?.length + (localAttachments$|async)?.length} }}</span>
|
||||
|
|
@ -128,8 +128,8 @@
|
|||
<attachment-list [attachments]="issueAttachments"
|
||||
[isDisableControls]="true"></attachment-list>
|
||||
</ng-container>
|
||||
<h3 class="mat-h3"
|
||||
*ngIf="issueAttachments?.length"
|
||||
<h3 *ngIf="issueAttachments?.length"
|
||||
class="mat-h3"
|
||||
style="margin-bottom: 0;">{{T.F.TASK.ADDITIONAL_INFO.LOCAL_ATTACHMENTS|translate}}</h3>
|
||||
<attachment-list [attachments]="localAttachments$|async"></attachment-list>
|
||||
</div>
|
||||
|
|
@ -137,12 +137,12 @@
|
|||
</task-additional-info-item>
|
||||
|
||||
<task-additional-info-item
|
||||
class="input-item"
|
||||
*ngIf="!issueAttachments?.length && !(localAttachments$|async)?.length"
|
||||
[inputIcon]="'add'"
|
||||
(collapseParent)="collapseParent()"
|
||||
(editActionTriggered)="addAttachment()"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
(collapseParent)="collapseParent()">
|
||||
*ngIf="!issueAttachments?.length && !(localAttachments$|async)?.length"
|
||||
[inputIcon]="'add'"
|
||||
class="input-item">
|
||||
<ng-container input-title>
|
||||
<mat-icon>attachment</mat-icon>
|
||||
<span>{{T.F.TASK.ADDITIONAL_INFO.ADD_ATTACHMENT|translate}}</span>
|
||||
|
|
@ -153,12 +153,12 @@
|
|||
|
||||
|
||||
<task-additional-info-item
|
||||
class="input-item"
|
||||
*ngIf="!task.parentId && !task.issueId"
|
||||
[inputIcon]="task.repeatCfgId ? 'edit': 'add'"
|
||||
(collapseParent)="collapseParent()"
|
||||
(editActionTriggered)="editTaskRepeatCfg()"
|
||||
(keyPress)="onItemKeyPress($event)"
|
||||
(collapseParent)="collapseParent()">
|
||||
*ngIf="!task.parentId && !task.issueId"
|
||||
[inputIcon]="task.repeatCfgId ? 'edit': 'add'"
|
||||
class="input-item">
|
||||
<ng-container input-title>
|
||||
<mat-icon
|
||||
style="transform: rotate(45deg);">repeat
|
||||
|
|
@ -166,9 +166,9 @@
|
|||
<span>{{T.F.TASK.ADDITIONAL_INFO.REPEAT|translate}}</span>
|
||||
</ng-container>
|
||||
<ng-container input-value>
|
||||
<div class="reminder-value"
|
||||
matTooltipPosition="left"
|
||||
*ngIf="repeatCfgDays$|async as repeatCfgDays">
|
||||
<div *ngIf="repeatCfgDays$|async as repeatCfgDays"
|
||||
class="reminder-value"
|
||||
matTooltipPosition="left">
|
||||
{{repeatCfgDays}}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
|
|
|||
|
|
@ -83,17 +83,14 @@ export class TaskAdditionalInfoComponent implements AfterViewInit, OnDestroy {
|
|||
: of(null)
|
||||
),
|
||||
);
|
||||
|
||||
private _attachmentIds$ = new BehaviorSubject([]);
|
||||
localAttachments$: Observable<Attachment[]> = this._attachmentIds$.pipe(
|
||||
switchMap((ids) => this.attachmentService.getByIds$(ids))
|
||||
);
|
||||
|
||||
parentId$ = new BehaviorSubject<string>(null);
|
||||
parentTaskData$: Observable<TaskWithIssueData> = this.parentId$.pipe(
|
||||
switchMap((id) => id ? this.taskService.getByIdWithIssueData$(id) : of(null))
|
||||
);
|
||||
|
||||
private _attachmentIds$ = new BehaviorSubject([]);
|
||||
localAttachments$: Observable<Attachment[]> = this._attachmentIds$.pipe(
|
||||
switchMap((ids) => this.attachmentService.getByIds$(ids))
|
||||
);
|
||||
private _taskData: TaskWithSubTasks;
|
||||
private _focusTimeout: number;
|
||||
private _subs = new Subscription();
|
||||
|
|
@ -109,6 +106,10 @@ export class TaskAdditionalInfoComponent implements AfterViewInit, OnDestroy {
|
|||
) {
|
||||
}
|
||||
|
||||
get task(): TaskWithSubTasks {
|
||||
return this._taskData;
|
||||
}
|
||||
|
||||
@Input() set task(newVal: TaskWithSubTasks) {
|
||||
const prev = this._taskData;
|
||||
this._taskData = newVal;
|
||||
|
|
@ -123,10 +124,6 @@ export class TaskAdditionalInfoComponent implements AfterViewInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
get task(): TaskWithSubTasks {
|
||||
return this._taskData;
|
||||
}
|
||||
|
||||
get progress() {
|
||||
return this._taskData && this._taskData.timeEstimate && (this._taskData.timeSpent / this._taskData.timeEstimate) * 100;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@
|
|||
class="repeat-task-icon">repeat
|
||||
</mat-icon>
|
||||
<span *ngIf="task.parentId">–</span>
|
||||
<inline-input style="display: inline-block"
|
||||
(changed)="updateTaskTitle(task, $event)"
|
||||
[value]="task.title">
|
||||
<inline-input (changed)="updateTaskTitle(task, $event)"
|
||||
[value]="task.title"
|
||||
style="display: inline-block">
|
||||
</inline-input>
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -67,10 +67,10 @@
|
|||
mat-cell>
|
||||
<button (click)="toggleTaskDone(task)"
|
||||
[class.isDone]="task.isDone"
|
||||
[title]="T.F.TASK.SUMMARY_TABLE.TOGGLE_DONE|translate"
|
||||
class="check-done"
|
||||
mat-icon-button
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.SUMMARY_TABLE.TOGGLE_DONE|translate">
|
||||
tabindex="2">
|
||||
<mat-icon aria-label="Mark as undone"
|
||||
class="undo">undo
|
||||
</mat-icon>
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ import {
|
|||
selectStartableTaskIds,
|
||||
selectStartableTasks,
|
||||
selectTaskAdditionalInfoTargetPanel,
|
||||
selectTaskById, selectTaskByIdWithIssueData,
|
||||
selectTaskById,
|
||||
selectTaskByIdWithIssueData,
|
||||
selectTaskByIssueId,
|
||||
selectTaskEntities,
|
||||
selectTasksByRepeatConfigId,
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
<div class="inner-wrapper"
|
||||
[class.isSelected]="isSelected">
|
||||
<div [class.isSelected]="isSelected"
|
||||
class="inner-wrapper">
|
||||
|
||||
<div style="visibility: hidden; position: fixed"
|
||||
<div [matMenuTriggerFor]="menu"
|
||||
[style.left]="contextMenuPosition.x"
|
||||
[style.top]="contextMenuPosition.y"
|
||||
[matMenuTriggerFor]="menu">
|
||||
style="visibility: hidden; position: fixed">
|
||||
</div>
|
||||
|
||||
<div class="box"></div>
|
||||
|
||||
<!-- TODO only bind pan etc when touch is available-->
|
||||
<div (panend)="onPanEnd()"
|
||||
<div (contextmenu)="openContextMenu($event)"
|
||||
(panend)="onPanEnd()"
|
||||
(panleft)="onPanLeft($event)"
|
||||
(panright)="onPanRight($event)"
|
||||
(panstart)="onPanStart($event)"
|
||||
[class.isPreventPointerEventsWhilePanning]="isPreventPointerEventsWhilePanning"
|
||||
class="first-line"
|
||||
(contextmenu)="openContextMenu($event)">
|
||||
class="first-line">
|
||||
|
||||
|
||||
<ng-container *ngIf="isTouch">
|
||||
|
|
@ -35,10 +35,10 @@
|
|||
|
||||
<button (click)="toggleTaskDone()"
|
||||
*ngIf="task.isDone"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_DONE|translate"
|
||||
class="check-done"
|
||||
mat-icon-button
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_DONE|translate">
|
||||
tabindex="2">
|
||||
<mat-icon @swirl
|
||||
class="undo">undo
|
||||
</mat-icon>
|
||||
|
|
@ -49,9 +49,9 @@
|
|||
|
||||
<div class="title-and-left-btns-wrapper">
|
||||
|
||||
<div class="drag-handle"
|
||||
[class.handle-par]="!task.parentId"
|
||||
[class.handle-sub]="task.parentId">
|
||||
<div [class.handle-par]="!task.parentId"
|
||||
[class.handle-sub]="task.parentId"
|
||||
class="drag-handle">
|
||||
<mat-icon
|
||||
class="drag-handle-ico"
|
||||
svgIcon="drag_handle">
|
||||
|
|
@ -62,20 +62,20 @@
|
|||
|
||||
|
||||
<div class="type-ico-wrapper">
|
||||
<mat-icon class="task-type-ico"
|
||||
<mat-icon (click)="editTaskRepeatCfg()"
|
||||
*ngIf="task.repeatCfgId"
|
||||
(click)="editTaskRepeatCfg()"
|
||||
[style.transform]="isCurrent ? 'scale(1.4) rotate(45deg)': 'rotate(45deg)'">repeat
|
||||
[style.transform]="isCurrent ? 'scale(1.4) rotate(45deg)': 'rotate(45deg)'"
|
||||
class="task-type-ico">repeat
|
||||
</mat-icon>
|
||||
|
||||
<mat-icon class="task-type-ico"
|
||||
(click)="handleUpdateBtnClick()"
|
||||
<mat-icon (click)="handleUpdateBtnClick()"
|
||||
*ngIf="task.issueData?.wasUpdated"
|
||||
class="task-type-ico"
|
||||
color="accent">update
|
||||
</mat-icon>
|
||||
|
||||
<div class="task-type-ico"
|
||||
*ngIf="!task.issueData?.wasUpdated && task.issueData">
|
||||
<div *ngIf="!task.issueData?.wasUpdated && task.issueData"
|
||||
class="task-type-ico">
|
||||
<div *ngIf="task.issueData?.storyPoints"
|
||||
class="mini-badge bgc-primary">{{task.issueData?.storyPoints}}</div>
|
||||
<mat-icon [svgIcon]="task.issueType|issueIcon"></mat-icon>
|
||||
|
|
@ -85,18 +85,18 @@
|
|||
|
||||
<div #contentEditableOnClickEl
|
||||
(editFinished)="updateTaskTitleIfChanged($event.isChanged, $event.newVal)"
|
||||
[value]="task.title"
|
||||
[isResetAfterEdit]="true"
|
||||
[value]="task.title"
|
||||
class="task-title"
|
||||
contenteditable="true"
|
||||
contentEditableOnClick
|
||||
contenteditable="true"
|
||||
tabindex="2"></div>
|
||||
</div>
|
||||
|
||||
<div class="all-controls-wrapper">
|
||||
<div (click)="estimateTime()"
|
||||
[class.isEditable]="!task.subTasks?.length"
|
||||
[class.hasNoTimeSpentOrEstimate]="!task.timeSpent && !task.timeEstimate"
|
||||
[class.isEditable]="!task.subTasks?.length"
|
||||
class="time-wrapper">
|
||||
<div *ngIf="!task.subTasks?.length"
|
||||
class="time">
|
||||
|
|
@ -122,84 +122,84 @@
|
|||
<div class="hover-controls">
|
||||
<button (click)="startTask()"
|
||||
*ngIf="!task.isDone && !isCurrent && !task.subTasks"
|
||||
[title]="T.F.TASK.CMP.TRACK_TIME|translate"
|
||||
class="ico-btn start-task-btn"
|
||||
color=""
|
||||
mat-icon-button
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.CMP.TRACK_TIME|translate">
|
||||
tabindex="2">
|
||||
<mat-icon class="play-icon"
|
||||
svgIcon="play"></mat-icon>
|
||||
</button>
|
||||
|
||||
<button (click)="pauseTask()"
|
||||
*ngIf="!task.isDone && isCurrent && !task.subTasks"
|
||||
[title]="T.F.TASK.CMP.TRACK_TIME_STOP|translate"
|
||||
class="ico-btn"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.CMP.TRACK_TIME_STOP|translate">
|
||||
tabindex="2">
|
||||
<mat-icon>pause</mat-icon>
|
||||
</button>
|
||||
<button (click)="toggleTaskDone()"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_DONE|translate"
|
||||
class="ico-btn task-done-btn"
|
||||
color=""
|
||||
mat-icon-button
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_DONE|translate">
|
||||
tabindex="2">
|
||||
<mat-icon *ngIf="task.isDone">undo</mat-icon>
|
||||
<mat-icon *ngIf="!task.isDone">check</mat-icon>
|
||||
</button>
|
||||
|
||||
<button (click)="toggleShowAdditionalInfoOpen()"
|
||||
*ngIf="!task.notes&&!task.issueData&&!isSelected"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_ADDITIONAL|translate"
|
||||
class="ico-btn show-additional-info-btn"
|
||||
color=""
|
||||
mat-icon-button
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_ADDITIONAL|translate">
|
||||
tabindex="2">
|
||||
<mat-icon>chat_bubble_outline</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button (click)="editReminder()"
|
||||
*ngIf="task.reminderId"
|
||||
[title]="T.F.TASK.CMP.EDIT_REMINDER|translate"
|
||||
class="ico-btn"
|
||||
color=""
|
||||
mat-icon-button
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.CMP.EDIT_REMINDER|translate">
|
||||
tabindex="2">
|
||||
<mat-icon>alarm</mat-icon>
|
||||
</button>
|
||||
|
||||
<button (click)="toggleShowAttachments()"
|
||||
*ngIf="task.attachmentIds?.length || (task.issueType==='JIRA' && task.issueData && task.issueData['attachments']?.length)"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_ATTACHMENTS|translate"
|
||||
class="ico-btn attachment-btn"
|
||||
color=""
|
||||
mat-icon-button
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_ATTACHMENTS|translate">
|
||||
tabindex="2">
|
||||
<mat-icon>attach_file</mat-icon>
|
||||
</button>
|
||||
|
||||
<button (click)="toggleShowAdditionalInfoOpen()"
|
||||
*ngIf="task.notes||task.issueData||isSelected"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_ADDITIONAL|translate"
|
||||
class="ico-btn show-additional-info-btn"
|
||||
color=""
|
||||
mat-icon-button
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_ADDITIONAL|translate">
|
||||
tabindex="2">
|
||||
<mat-icon *ngIf="!isSelected">chat</mat-icon>
|
||||
<mat-icon *ngIf="isSelected">close</mat-icon>
|
||||
</button>
|
||||
|
||||
<button [matMenuTriggerFor]="menu"
|
||||
[style.pointer-events]="isContextMenuDisabled ? 'none' : 'all'"
|
||||
<button (menuClosed)="isContextMenuDisabled = false"
|
||||
(menuOpened)="isContextMenuDisabled = true"
|
||||
(menuClosed)="isContextMenuDisabled = false"
|
||||
[matMenuTriggerFor]="menu"
|
||||
[style.pointer-events]="isContextMenuDisabled ? 'none' : 'all'"
|
||||
[title]="T.F.TASK.CMP.OPEN_TASK_MENU|translate"
|
||||
class="ico-btn menu-trigger show-touch-only"
|
||||
color=""
|
||||
mat-icon-button
|
||||
[title]="T.F.TASK.CMP.OPEN_TASK_MENU|translate">
|
||||
mat-icon-button>
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
|
||||
|
|
@ -285,8 +285,8 @@
|
|||
<a *ngIf="task.issueData?.url"
|
||||
[href]="task.issueData.url"
|
||||
mat-menu-item
|
||||
target="_blank"
|
||||
tabindex="2">
|
||||
tabindex="2"
|
||||
target="_blank">
|
||||
<mat-icon [svgIcon]="task.issueType|issueIcon"></mat-icon>
|
||||
{{T.F.TASK.CMP.OPEN_ISSUE|translate}}
|
||||
</a>
|
||||
|
|
@ -325,18 +325,18 @@
|
|||
{{T.F.TASK.CMP.DELETE|translate}}
|
||||
</button>
|
||||
|
||||
<button mat-menu-item
|
||||
tabindex="2"
|
||||
*ngIf="!task.parentId && !task.issueId"
|
||||
[matMenuTriggerFor]="advMenu">
|
||||
<button *ngIf="!task.parentId && !task.issueId"
|
||||
[matMenuTriggerFor]="advMenu"
|
||||
mat-menu-item
|
||||
tabindex="2">
|
||||
{{T.F.TASK.CMP.ADVANCED|translate}}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<mat-menu #advMenu="matMenu">
|
||||
<button mat-menu-item
|
||||
<button *ngIf="!task.parentId && !task.issueId && !task.repeatCfgId"
|
||||
[matMenuTriggerFor]="projectMenu"
|
||||
*ngIf="!task.parentId && !task.issueId && !task.repeatCfgId">
|
||||
mat-menu-item>
|
||||
<mat-icon>forward</mat-icon>
|
||||
{{T.F.TASK.CMP.MOVE_TO_OTHER_PROJECT|translate}}
|
||||
</button>
|
||||
|
|
@ -375,11 +375,11 @@
|
|||
class="sub-tasks">
|
||||
<button (click)="toggleSubTaskMode()"
|
||||
*ngIf="task.subTasks && task.subTasks.length"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_SUB_TASK_VISIBILITY|translate"
|
||||
class="toggle-sub-tasks-btn ico-btn mat-elevation-z2"
|
||||
color=""
|
||||
mat-mini-fab
|
||||
tabindex="2"
|
||||
[title]="T.F.TASK.CMP.TOGGLE_SUB_TASK_VISIBILITY|translate">
|
||||
tabindex="2">
|
||||
<mat-icon *ngIf="(task._showSubTasksMode===ShowSubTasksMode.HideAll)">add
|
||||
</mat-icon>
|
||||
<mat-icon *ngIf="(task._showSubTasksMode!==ShowSubTasksMode.HideAll)"
|
||||
|
|
|
|||
|
|
@ -66,6 +66,26 @@ export class TaskComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
@ViewChild(MatMenuTrigger, {static: true}) contextMenu: MatMenuTrigger;
|
||||
|
||||
@HostBinding('tabindex') tabIndex = 1;
|
||||
private _dragEnterTarget: HTMLElement;
|
||||
private _destroy$: Subject<boolean> = new Subject<boolean>();
|
||||
private _currentPanTimeout: number;
|
||||
|
||||
constructor(
|
||||
private readonly _taskService: TaskService,
|
||||
private readonly _matDialog: MatDialog,
|
||||
private readonly _configService: GlobalConfigService,
|
||||
private readonly _issueService: IssueService,
|
||||
private readonly _attachmentService: AttachmentService,
|
||||
private readonly _elementRef: ElementRef,
|
||||
private readonly _renderer: Renderer2,
|
||||
private readonly _cd: ChangeDetectorRef,
|
||||
public readonly projectService: ProjectService,
|
||||
) {
|
||||
}
|
||||
|
||||
public get progress() {
|
||||
return this.task && this.task.timeEstimate && (this.task.timeSpent / this.task.timeEstimate) * 100;
|
||||
}
|
||||
|
||||
// TODO do via observable
|
||||
@HostBinding('class.isCurrent')
|
||||
|
|
@ -89,27 +109,6 @@ export class TaskComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
return 't-' + this.task.id;
|
||||
}
|
||||
|
||||
private _dragEnterTarget: HTMLElement;
|
||||
private _destroy$: Subject<boolean> = new Subject<boolean>();
|
||||
private _currentPanTimeout: number;
|
||||
|
||||
constructor(
|
||||
private readonly _taskService: TaskService,
|
||||
private readonly _matDialog: MatDialog,
|
||||
private readonly _configService: GlobalConfigService,
|
||||
private readonly _issueService: IssueService,
|
||||
private readonly _attachmentService: AttachmentService,
|
||||
private readonly _elementRef: ElementRef,
|
||||
private readonly _renderer: Renderer2,
|
||||
private readonly _cd: ChangeDetectorRef,
|
||||
public readonly projectService: ProjectService,
|
||||
) {
|
||||
}
|
||||
|
||||
public get progress() {
|
||||
return this.task && this.task.timeEstimate && (this.task.timeSpent / this.task.timeEstimate) * 100;
|
||||
}
|
||||
|
||||
// methods come last
|
||||
@HostListener('keydown', ['$event']) onKeyDown(ev: KeyboardEvent) {
|
||||
this._handleKeyboardShortcuts(ev);
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import {TaskUiEffects} from './store/task-ui.effects';
|
|||
import {TaskElectronEffects} from './store/task-electron.effects';
|
||||
import {SubTaskTotalTimeSpentPipe} from './pipes/sub-task-total-time-spent.pipe';
|
||||
import {SubTaskTotalTimeEstimatePipe} from './pipes/sub-task-total-time-estimate.pipe';
|
||||
import { TaskAdditionalInfoItemComponent } from './task-additional-info/task-additional-info-item/task-additional-info-item.component';
|
||||
import { TaskAdditionalInfoWrapperComponent } from './task-additional-info/task-additional-info-wrapper/task-additional-info-wrapper.component';
|
||||
import {TaskAdditionalInfoItemComponent} from './task-additional-info/task-additional-info-item/task-additional-info-item.component';
|
||||
import {TaskAdditionalInfoWrapperComponent} from './task-additional-info/task-additional-info-wrapper/task-additional-info-wrapper.component';
|
||||
import {BetterDrawerModule} from '../../ui/better-drawer/better-drawer.module';
|
||||
|
||||
@NgModule({
|
||||
|
|
|
|||
|
|
@ -11,21 +11,21 @@
|
|||
|
||||
|
||||
<div *ngIf="!isShowAsText"
|
||||
class="wrapper-wrapper"
|
||||
[class.isNoCols]="!options.cols?.length"
|
||||
class="wrapper-wrapper"
|
||||
>
|
||||
<button [matMenuTriggerFor]="addColMenu"
|
||||
[matTooltip]="T.F.WORKLOG.EXPORT.ADD_COL|translate"
|
||||
class="add-col"
|
||||
color=""
|
||||
[matTooltip]="T.F.WORKLOG.EXPORT.ADD_COL|translate"
|
||||
matTooltipPosition="left"
|
||||
mat-mini-fab>
|
||||
mat-mini-fab
|
||||
matTooltipPosition="left">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
<mat-menu #addColMenu="matMenu">
|
||||
<button mat-menu-item
|
||||
<button (click)="addCol(colOpt.id)"
|
||||
*ngFor="let colOpt of colOpts"
|
||||
(click)="addCol(colOpt.id)">
|
||||
mat-menu-item>
|
||||
{{colOpt.title|translate}}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
|
@ -61,9 +61,9 @@
|
|||
</div>
|
||||
|
||||
|
||||
<collapsible class="options-collapsible"
|
||||
[isInline]="true"
|
||||
[title]="T.F.WORKLOG.EXPORT.OPTIONS|translate">
|
||||
<collapsible [isInline]="true"
|
||||
[title]="T.F.WORKLOG.EXPORT.OPTIONS|translate"
|
||||
class="options-collapsible">
|
||||
<section class="options">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@
|
|||
|
||||
<button (click)="exportData(month.value,year.key, month.key)"
|
||||
aria-label="export data"
|
||||
class="mat-elevation-z1"
|
||||
color="primary"
|
||||
mat-mini-fab
|
||||
class="mat-elevation-z1">
|
||||
mat-mini-fab>
|
||||
<mat-icon>call_made</mat-icon>
|
||||
</button>
|
||||
</h2>
|
||||
|
|
@ -53,9 +53,9 @@
|
|||
(click)="exportData(month.value,year.key, month.key, week.weekNr)"
|
||||
[matTooltip]="week.start+'.-'+week.end+'. Days: '+ week.daysWorked+', Time: '+(week.timeSpent|msToString)"
|
||||
aria-label="export data"
|
||||
class="mat-elevation-z1"
|
||||
color=""
|
||||
mat-mini-fab
|
||||
class="mat-elevation-z1">
|
||||
mat-mini-fab>
|
||||
<mat-icon>call_made</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -94,8 +94,8 @@
|
|||
<td [class.isSubTask]="logEntry.task.parentId"
|
||||
class="title">
|
||||
<mat-icon *ngIf="logEntry.task.repeatCfgId"
|
||||
class="repeat-task-icon"
|
||||
[title]="T.F.WORKLOG.CMP.REPEATING_TASK|translate">repeat
|
||||
[title]="T.F.WORKLOG.CMP.REPEATING_TASK|translate"
|
||||
class="repeat-task-icon">repeat
|
||||
</mat-icon>
|
||||
<span class="task-title">{{logEntry.task.title}}</span>
|
||||
</td>
|
||||
|
|
@ -113,10 +113,10 @@
|
|||
<td class="actions">
|
||||
<button (click)="restoreTask(logEntry.task);"
|
||||
*ngIf="!logEntry.task?.parentId && !logEntry.isNoRestore"
|
||||
[title]="T.F.WORKLOG.CMP.RESTORE_TASK_FROM_ARCHIVE|translate"
|
||||
aria-label="restore"
|
||||
color=""
|
||||
mat-icon-button
|
||||
[title]="T.F.WORKLOG.CMP.RESTORE_TASK_FROM_ARCHIVE|translate">
|
||||
mat-icon-button>
|
||||
<mat-icon>settings_backup_restore</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import {BehaviorSubject, Observable} from 'rxjs';
|
|||
providedIn: 'root'
|
||||
})
|
||||
export class ImexMetaService {
|
||||
isDataImportInProgress = false;
|
||||
private _isDataImportInProgress$ = new BehaviorSubject<boolean>(false);
|
||||
isDataImportInProgress$: Observable<boolean> = this._isDataImportInProgress$.asObservable();
|
||||
isDataImportInProgress = false;
|
||||
|
||||
constructor() {
|
||||
this.isDataImportInProgress$.subscribe((val) => this.isDataImportInProgress = val);
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
<div class="dark-mode-toggle">
|
||||
<mat-slide-toggle
|
||||
[checked]="(configService.misc$|async)?.isDarkMode"
|
||||
(change)="toggleDarkMode($event)">
|
||||
(change)="toggleDarkMode($event)"
|
||||
[checked]="(configService.misc$|async)?.isDarkMode">
|
||||
{{T.PS.TOGGLE_DARK_MODE|translate}}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<section class="config-section "
|
||||
*ngFor="let section of globalConfigFormCfg; trackBy:trackBySectionKey;">
|
||||
<section *ngFor="let section of globalConfigFormCfg; trackBy:trackBySectionKey;"
|
||||
class="config-section ">
|
||||
<config-section (save)="saveGlobalCfg($event)"
|
||||
[cfg]="globalCfg[section.key]"
|
||||
[section]="section"></config-section>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
<div *ngIf="isForToday"
|
||||
style="text-align: center;">
|
||||
<a class="back-btn"
|
||||
mat-button
|
||||
color="primary"
|
||||
mat-button
|
||||
routerLink="/work-view">
|
||||
<mat-icon>chevron_left</mat-icon>
|
||||
{{T.PDS.BACK|translate}}
|
||||
|
|
@ -48,8 +48,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-point"
|
||||
[title]="T.PDS.TIME_SPENT_ESTIMATE_TITLE|translate">
|
||||
<div [title]="T.PDS.TIME_SPENT_ESTIMATE_TITLE|translate"
|
||||
class="summary-point">
|
||||
<mat-icon>timer</mat-icon>
|
||||
<div class="summary-text">{{T.PDS.TIME_SPENT_AND_ESTIMATE_LABEL|translate}}
|
||||
<div class="summary-val">{{timeWorked$|async|msToClockString}}
|
||||
|
|
@ -80,13 +80,13 @@
|
|||
</ng-template>
|
||||
|
||||
<ng-container *ngIf="(hasTasksForToday$|async); else noTasks">
|
||||
<task-summary-table [day]="(dayStr$|async)"
|
||||
[flatTasks]="(tasksWorkedOnOrDoneOrRepeatableFlat$|async)"
|
||||
(updated)="onTaskSummaryEdit()"></task-summary-table>
|
||||
<task-summary-table (updated)="onTaskSummaryEdit()"
|
||||
[day]="(dayStr$|async)"
|
||||
[flatTasks]="(tasksWorkedOnOrDoneOrRepeatableFlat$|async)"></task-summary-table>
|
||||
<div style="text-align: center; margin-top: 8px; margin-bottom: 16px;">
|
||||
<button [matMenuTriggerFor]="roundTimeActions"
|
||||
mat-button
|
||||
[title]="T.PDS.ROUND_TIME_SPENT_TITLE|translate">
|
||||
[title]="T.PDS.ROUND_TIME_SPENT_TITLE|translate"
|
||||
mat-button>
|
||||
<mat-icon>restore</mat-icon>
|
||||
{{T.PDS.ROUND_TIME_SPENT|translate}}
|
||||
</button>
|
||||
|
|
@ -133,8 +133,8 @@
|
|||
</mat-tab>
|
||||
|
||||
|
||||
<mat-tab [label]="T.PDS.EVALUATION|translate"
|
||||
*ngIf="!(configService.misc$|async)?.isHideEvaluationSheet">
|
||||
<mat-tab *ngIf="!(configService.misc$|async)?.isHideEvaluationSheet"
|
||||
[label]="T.PDS.EVALUATION|translate">
|
||||
<section class="tab-inner"
|
||||
style="max-width: 600px">
|
||||
<evaluation-sheet (save)="onEvaluationSave()"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {RouterModule} from '@angular/router';
|
|||
import {WorklogModule} from '../../features/worklog/worklog.module';
|
||||
import {MetricModule} from '../../features/metric/metric.module';
|
||||
import {TasksModule} from '../../features/tasks/tasks.module';
|
||||
import { PlanTasksTomorrowComponent } from './plan-tasks-tomorrow/plan-tasks-tomorrow.component';
|
||||
import {PlanTasksTomorrowComponent} from './plan-tasks-tomorrow/plan-tasks-tomorrow.component';
|
||||
import {BetterDrawerModule} from '../../ui/better-drawer/better-drawer.module';
|
||||
|
||||
@NgModule({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div class="task-list-wrapper">
|
||||
<section class="add-task-bar-wrapper">
|
||||
<section class="add-task-bar-wrapper">
|
||||
<add-task-bar [isAddToBottom]="true"
|
||||
[isDisableAutoFocus]="true"></add-task-bar>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
max-width: 628px;
|
||||
}
|
||||
|
||||
:host::ng-deep .hover-controls{
|
||||
:host::ng-deep .hover-controls {
|
||||
.task-done-btn,
|
||||
.start-task-btn {
|
||||
display: none !important;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {T} from '../../t.const';
|
|||
})
|
||||
export class MetricPageComponent {
|
||||
T = T;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,39 +21,39 @@
|
|||
|
||||
<div class="issue-providers">
|
||||
<mat-icon *ngIf="project.issueIntegrationCfgs?.JIRA?.isEnabled"
|
||||
svgIcon="jira"
|
||||
[title]="T.PP.JIRA_CONFIGURED|translate"></mat-icon>
|
||||
[title]="T.PP.JIRA_CONFIGURED|translate"
|
||||
svgIcon="jira"></mat-icon>
|
||||
<mat-icon *ngIf="project.issueIntegrationCfgs?.GITHUB?.repo"
|
||||
svgIcon="github"
|
||||
[title]="T.PP.GITHUB_CONFIGURED|translate"></mat-icon>
|
||||
[title]="T.PP.GITHUB_CONFIGURED|translate"
|
||||
svgIcon="github"></mat-icon>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button (click)="edit(project)"
|
||||
[title]="T.PP.EDIT_PROJECT|translate"
|
||||
color=""
|
||||
mat-icon-button
|
||||
[title]="T.PP.EDIT_PROJECT|translate">
|
||||
mat-icon-button>
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button (click)="archive(project.id)"
|
||||
[disabled]="project.id === (projectService.currentId$|async)"
|
||||
[title]="T.PP.ARCHIVE_PROJECT|translate"
|
||||
color=""
|
||||
mat-icon-button
|
||||
[title]="T.PP.ARCHIVE_PROJECT|translate">
|
||||
mat-icon-button>
|
||||
<mat-icon>archive</mat-icon>
|
||||
</button>
|
||||
<button (click)="export(project.id, project.title)"
|
||||
[title]="T.PP.EXPORT_PROJECT|translate"
|
||||
class="hide-xs"
|
||||
color=""
|
||||
mat-icon-button
|
||||
[title]="T.PP.EXPORT_PROJECT|translate">
|
||||
mat-icon-button>
|
||||
<mat-icon>flight</mat-icon>
|
||||
</button>
|
||||
<button (click)="remove(project.id)"
|
||||
[disabled]="project.id === (projectService.currentId$|async)"
|
||||
[title]="T.PP.DELETE_PROJECT|translate"
|
||||
color="warn"
|
||||
mat-icon-button
|
||||
[title]="T.PP.DELETE_PROJECT|translate">
|
||||
mat-icon-button>
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -97,25 +97,25 @@
|
|||
|
||||
<div class="issue-providers">
|
||||
<mat-icon *ngIf="project.issueIntegrationCfgs?.JIRA?.isEnabled"
|
||||
svgIcon="jira"
|
||||
[title]="T.PP.JIRA_CONFIGURED|translate"></mat-icon>
|
||||
[title]="T.PP.JIRA_CONFIGURED|translate"
|
||||
svgIcon="jira"></mat-icon>
|
||||
<mat-icon *ngIf="project.issueIntegrationCfgs?.GITHUB?.repo"
|
||||
svgIcon="github"
|
||||
[title]="T.PP.GITHUB_CONFIGURED|translate"></mat-icon>
|
||||
[title]="T.PP.GITHUB_CONFIGURED|translate"
|
||||
svgIcon="github"></mat-icon>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button (click)="unarchive(project.id)"
|
||||
[title]="T.PP.UN_ARCHIVE_PROJECT|translate"
|
||||
color=""
|
||||
mat-icon-button
|
||||
[title]="T.PP.UN_ARCHIVE_PROJECT|translate">
|
||||
mat-icon-button>
|
||||
<mat-icon>unarchive</mat-icon>
|
||||
</button>
|
||||
<button (click)="remove(project.id)"
|
||||
*ngIf="project.id !== (projectService.currentId$|async)"
|
||||
[title]="T.PP.DELETE_PROJECT|translate"
|
||||
color="warn"
|
||||
mat-icon-button
|
||||
[title]="T.PP.DELETE_PROJECT|translate">
|
||||
mat-icon-button>
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import {CommonModule} from '@angular/common';
|
|||
import {ProjectSettingsComponent} from './project-settings.component';
|
||||
import {UiModule} from '../../ui/ui.module';
|
||||
import {ConfigModule} from '../../features/config/config.module';
|
||||
import {GoogleModule} from '../../features/google/google.module';
|
||||
import {JiraModule} from '../../features/issue/jira/jira.module';
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,20 +18,20 @@
|
|||
<mat-card-content>
|
||||
<div class="content">
|
||||
<button (click)="startTask(task)"
|
||||
color=""
|
||||
class="mat-elevation-z1 start-task-btn"
|
||||
matTooltipPosition="right"
|
||||
[matTooltip]="T.SCHEDULE.START_TASK|translate"
|
||||
mat-mini-fab>
|
||||
class="mat-elevation-z1 start-task-btn"
|
||||
color=""
|
||||
mat-mini-fab
|
||||
matTooltipPosition="right">
|
||||
<mat-icon>play_arrow</mat-icon>
|
||||
</button>
|
||||
|
||||
<div class="task-info">
|
||||
<div class="title">
|
||||
<div class="task-title"
|
||||
(editFinished)="updateTaskTitleIfChanged($event.isChanged, $event.newVal, task)"
|
||||
contenteditable="true"
|
||||
contentEditableOnClick>{{task.title}}</div>
|
||||
<div (editFinished)="updateTaskTitleIfChanged($event.isChanged, $event.newVal, task)"
|
||||
class="task-title"
|
||||
contentEditableOnClick
|
||||
contenteditable="true">{{task.title}}</div>
|
||||
<div *ngIf="(projectService.getById$(task.reminderData.projectId)|async) as project"
|
||||
class="project">
|
||||
<div [ngStyle]="getThemeColor(project.theme?.primary || project.themeColor)"
|
||||
|
|
@ -39,11 +39,11 @@
|
|||
<div class="project-title">{{project.title}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="due-date"
|
||||
<div (click)="editReminder(task)"
|
||||
[matTooltip]="task.reminderData.remindAt|date:'short'"
|
||||
class="due-date"
|
||||
mat-ripple
|
||||
(click)="editReminder(task)"
|
||||
matTooltipPosition="left"
|
||||
[matTooltip]="task.reminderData.remindAt|date:'short'">
|
||||
matTooltipPosition="left">
|
||||
<div class="date">
|
||||
{{task.reminderData.remindAt|humanizeTimestamp}}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<div class="wrapper">
|
||||
<div class="task-list-wrapper">
|
||||
<task-list [noTasksMsg]="T.BL.NO_TASKS|translate"
|
||||
<task-list [isBacklog]="true"
|
||||
[noTasksMsg]="T.BL.NO_TASKS|translate"
|
||||
[tasks]="backlogTasks$|async"
|
||||
[isBacklog]="true"
|
||||
class="isHidePlayBtn"
|
||||
listId="PARENT"
|
||||
listModelId="BACKLOG"></task-list>
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@
|
|||
@expandFade
|
||||
class="planning-mode-content">
|
||||
<add-task-bar (done)="startWork()"
|
||||
[isDoubleEnterMode]="true"
|
||||
[isAddToBottom]="true"></add-task-bar>
|
||||
[isAddToBottom]="true"
|
||||
[isDoubleEnterMode]="true"></add-task-bar>
|
||||
<button (click)="startWork()"
|
||||
class="read-to-work-btn"
|
||||
color="primary"
|
||||
|
|
|
|||
|
|
@ -53,23 +53,13 @@ export class WorkViewPageComponent implements OnInit, OnDestroy, AfterContentIni
|
|||
),
|
||||
map(v => v[0]),
|
||||
);
|
||||
|
||||
splitTopEl$ = new ReplaySubject<HTMLElement>(1);
|
||||
upperContainerScroll$ = this.projectService.isProjectChanging$.pipe(
|
||||
filter(isChanging => !isChanging),
|
||||
delay(50),
|
||||
switchMap(() => this.splitTopEl$),
|
||||
switchMap((el) => fromEvent(el, 'scroll')),
|
||||
);
|
||||
|
||||
|
||||
@ViewChild('splitTopEl', {static: false, read: ElementRef}) set splitTopElRef(ref: ElementRef) {
|
||||
if (ref) {
|
||||
this.splitTopEl$.next(ref.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
splitTopEl$ = new ReplaySubject<HTMLElement>(1);
|
||||
|
||||
private _subs = new Subscription();
|
||||
private _switchListAnimationTimeout: number;
|
||||
|
||||
|
|
@ -86,6 +76,11 @@ export class WorkViewPageComponent implements OnInit, OnDestroy, AfterContentIni
|
|||
) {
|
||||
}
|
||||
|
||||
@ViewChild('splitTopEl', {static: false, read: ElementRef}) set splitTopElRef(ref: ElementRef) {
|
||||
if (ref) {
|
||||
this.splitTopEl$.next(ref.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const sub = this._dragulaService.find(SUB);
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
<div class="content"
|
||||
#contentElRef>
|
||||
<div #contentElRef
|
||||
class="content">
|
||||
<ng-content select="[better-drawer-content]"></ng-content>
|
||||
</div>
|
||||
|
||||
<div class="backdrop"
|
||||
(click)="close()"
|
||||
<div (click)="close()"
|
||||
*ngIf="isOpenGet && isOverGet"
|
||||
@fade></div>
|
||||
@fade
|
||||
class="backdrop"></div>
|
||||
|
||||
|
||||
<div class="side"
|
||||
[style]="sideStyle">
|
||||
<button class="close-btn"
|
||||
(click)="close()">
|
||||
<div [style]="sideStyle"
|
||||
class="side">
|
||||
<button (click)="close()"
|
||||
class="close-btn">
|
||||
×
|
||||
</button>
|
||||
<div class="side-inner">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import { BetterDrawerContainerComponent } from './better-drawer-container.component';
|
||||
import {BetterDrawerContainerComponent} from './better-drawer-container.component';
|
||||
|
||||
describe('BetterDrawerContainerComponent', () => {
|
||||
let component: BetterDrawerContainerComponent;
|
||||
|
|
@ -8,9 +8,9 @@ describe('BetterDrawerContainerComponent', () => {
|
|||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ BetterDrawerContainerComponent ]
|
||||
declarations: [BetterDrawerContainerComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
import {fadeAnimation} from '../../animations/fade.ani';
|
||||
import {DomSanitizer, SafeStyle} from '@angular/platform-browser';
|
||||
import {Observable, ReplaySubject, Subscription} from 'rxjs';
|
||||
import {distinctUntilChanged, filter, map, share, switchMap, tap} from 'rxjs/operators';
|
||||
import {distinctUntilChanged, filter, map, share, switchMap} from 'rxjs/operators';
|
||||
import {observeWidth} from '../../../util/resize-observer-obs';
|
||||
import {MainContainerClass} from '../../../app.constants';
|
||||
|
||||
|
|
@ -31,35 +31,8 @@ const VERY_SMALL_CONTAINER_WIDTH = 450;
|
|||
})
|
||||
export class BetterDrawerContainerComponent implements OnInit, AfterContentInit, OnDestroy {
|
||||
@Input() sideWidth: number;
|
||||
|
||||
@Input() set isOpen(v: boolean) {
|
||||
this._isOpen = v;
|
||||
this._updateStyle();
|
||||
}
|
||||
|
||||
@HostBinding('class.isOpen') get isOpenGet() {
|
||||
return this._isOpen;
|
||||
}
|
||||
|
||||
@Input() set isOver(v: boolean) {
|
||||
this._isOver = v;
|
||||
this._updateStyle();
|
||||
}
|
||||
|
||||
@HostBinding('class.isOver') get isOverGet() {
|
||||
return this._isOver;
|
||||
}
|
||||
|
||||
@Output() wasClosed = new EventEmitter<void>();
|
||||
|
||||
@ViewChild('contentElRef', {static: false, read: ElementRef}) set setContentElRef(ref: ElementRef) {
|
||||
if (ref) {
|
||||
this.contentEl$.next(ref.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
contentEl$ = new ReplaySubject<HTMLElement>(1);
|
||||
|
||||
containerWidth$: Observable<number> = this.contentEl$.pipe(
|
||||
filter(el => !!el),
|
||||
switchMap((el) => observeWidth(el)),
|
||||
|
|
@ -74,20 +47,43 @@ export class BetterDrawerContainerComponent implements OnInit, AfterContentInit,
|
|||
map(v => v < VERY_SMALL_CONTAINER_WIDTH),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
sideStyle: SafeStyle;
|
||||
|
||||
private _isOpen: boolean;
|
||||
private _isOver: boolean;
|
||||
private _subs = new Subscription();
|
||||
|
||||
|
||||
constructor(
|
||||
private _elementRef: ElementRef,
|
||||
private _domSanitizer: DomSanitizer,
|
||||
) {
|
||||
}
|
||||
|
||||
@HostBinding('class.isOpen') get isOpenGet() {
|
||||
return this._isOpen;
|
||||
}
|
||||
|
||||
@HostBinding('class.isOver') get isOverGet() {
|
||||
return this._isOver;
|
||||
}
|
||||
|
||||
@ViewChild('contentElRef', {static: false, read: ElementRef}) set setContentElRef(ref: ElementRef) {
|
||||
if (ref) {
|
||||
this.contentEl$.next(ref.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
private _isOpen: boolean;
|
||||
|
||||
@Input() set isOpen(v: boolean) {
|
||||
this._isOpen = v;
|
||||
this._updateStyle();
|
||||
}
|
||||
|
||||
private _isOver: boolean;
|
||||
|
||||
@Input() set isOver(v: boolean) {
|
||||
this._isOver = v;
|
||||
this._updateStyle();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._updateStyle();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { BetterDrawerContainerComponent } from './better-drawer-container/better-drawer-container.component';
|
||||
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {BetterDrawerContainerComponent} from './better-drawer-container/better-drawer-container.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
|
|
@ -15,4 +14,5 @@ import { BetterDrawerContainerComponent } from './better-drawer-container/better
|
|||
CommonModule
|
||||
],
|
||||
})
|
||||
export class BetterDrawerModule { }
|
||||
export class BetterDrawerModule {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@
|
|||
|
||||
{{ modelItem.title}}
|
||||
|
||||
<mat-icon matChipRemove
|
||||
[matTooltip]="T.G.REMOVE|translate">cancel
|
||||
<mat-icon [matTooltip]="T.G.REMOVE|translate"
|
||||
matChipRemove>cancel
|
||||
</mat-icon>
|
||||
|
||||
<button mat-icon-button
|
||||
<button (click)="additionalAction.emit(modelItem.id)"
|
||||
*ngIf="additionalActionIcon"
|
||||
class="additional"
|
||||
[class.isToggled]="isToggled(modelItem.id)"
|
||||
(click)="additionalAction.emit(modelItem.id)"
|
||||
[matTooltip]="isToggled(modelItem.id) ? additionalActionTooltipUnToggle: additionalActionTooltip">
|
||||
[matTooltip]="isToggled(modelItem.id) ? additionalActionTooltipUnToggle: additionalActionTooltip"
|
||||
class="additional"
|
||||
mat-icon-button>
|
||||
<mat-icon style="transform: rotate(45deg)">{{additionalActionIcon}}
|
||||
</mat-icon>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -30,29 +30,17 @@ export class ChipListInputComponent {
|
|||
@Input() additionalActionTooltip: string;
|
||||
@Input() additionalActionTooltipUnToggle: string;
|
||||
@Input() toggledItems: string[];
|
||||
|
||||
@Input() set suggestions(val) {
|
||||
this.suggestionsIn = val.sort((a, b) => a.title.localeCompare(b.title));
|
||||
this._updateModelItems(this._modelIds);
|
||||
}
|
||||
|
||||
suggestionsIn: Suggestion[];
|
||||
|
||||
@Input() set model(v: string[]) {
|
||||
this._modelIds = v;
|
||||
this._updateModelItems(v);
|
||||
}
|
||||
|
||||
@Output() addItem = new EventEmitter<string>();
|
||||
@Output() addNewItem = new EventEmitter<string>();
|
||||
@Output() removeItem = new EventEmitter<string>();
|
||||
@Output() additionalAction = new EventEmitter<string>();
|
||||
|
||||
modelItems: Suggestion[];
|
||||
|
||||
inputCtrl = new FormControl();
|
||||
separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||
|
||||
@ViewChild('inputElRef', {static: true}) inputEl: ElementRef<HTMLInputElement>;
|
||||
@ViewChild('autoElRef', {static: true}) matAutocomplete: MatAutocomplete;
|
||||
private _modelIds: string[] = [];
|
||||
filteredSuggestions: Observable<Suggestion[]> = this.inputCtrl.valueChanges.pipe(
|
||||
startWith(''),
|
||||
map((val: string | null) => val
|
||||
|
|
@ -60,14 +48,19 @@ export class ChipListInputComponent {
|
|||
: this.suggestionsIn.filter(suggestion => !this._modelIds || !this._modelIds.includes(suggestion.id)))
|
||||
);
|
||||
|
||||
@ViewChild('inputElRef', {static: true}) inputEl: ElementRef<HTMLInputElement>;
|
||||
@ViewChild('autoElRef', {static: true}) matAutocomplete: MatAutocomplete;
|
||||
|
||||
private _modelIds: string[] = [];
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
@Input() set suggestions(val) {
|
||||
this.suggestionsIn = val.sort((a, b) => a.title.localeCompare(b.title));
|
||||
this._updateModelItems(this._modelIds);
|
||||
}
|
||||
|
||||
@Input() set model(v: string[]) {
|
||||
this._modelIds = v;
|
||||
this._updateModelItems(v);
|
||||
}
|
||||
|
||||
add(event: MatChipInputEvent): void {
|
||||
if (!this.matAutocomplete.isOpen) {
|
||||
const input = event.input;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
<mat-chip (click)="setCommonVal('2H')">{{T.DATETIME_INPUT.IN|translate:{time: '2h'} }}</mat-chip>
|
||||
<mat-chip (click)="setCommonVal('5H')">{{T.DATETIME_INPUT.IN|translate:{time: '5h'} }}</mat-chip>
|
||||
<mat-chip (click)="setCommonVal('TOMORROW_11')">{{T.DATETIME_INPUT.TOMORROW|translate:{time: '11:00'} }}</mat-chip>
|
||||
<mat-chip *ngIf="lastVal"
|
||||
(click)="setLastVal()">{{lastVal|date:'short'}}</mat-chip>
|
||||
<mat-chip (click)="setLastVal()"
|
||||
*ngIf="lastVal">{{lastVal|date:'short'}}</mat-chip>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,15 +26,15 @@ export class DatetimeInputComponent {
|
|||
}
|
||||
}
|
||||
|
||||
get model() {
|
||||
return this.nrValue;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set model(v: number) {
|
||||
this._updateValues(v);
|
||||
}
|
||||
|
||||
get model() {
|
||||
return this.nrValue;
|
||||
}
|
||||
|
||||
updateFromInput(v: number) {
|
||||
this._updateValues(v, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
<input (ngModelChange)="onInputChange($event)"
|
||||
[id]="uid"
|
||||
[ngModel]="_model"
|
||||
[placeholder]="T.G.CLICK_TO_EDIT|translate"
|
||||
class="value"
|
||||
inputDuration="optional"
|
||||
[placeholder]="T.G.CLICK_TO_EDIT|translate"
|
||||
spellcheck="false"
|
||||
type="text">
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
(blur)="untoggleShowEdit(); setBlur($event)"
|
||||
(focus)="setFocus($event)"
|
||||
(input)="resizeTextareaToFit()"
|
||||
(keypress)="keypressHandler($event)"
|
||||
(keydown)="keypressHandler($event)"
|
||||
(keypress)="keypressHandler($event)"
|
||||
*ngIf="isShowEdit || (isTurnOffMarkdownParsing$|async)"
|
||||
[@fade]
|
||||
[ngModel]="modelCopy"
|
||||
|
|
@ -17,18 +17,18 @@
|
|||
<div #previewEl
|
||||
(click)="toggleShowEdit($event)"
|
||||
(focus)="toggleShowEdit($event)"
|
||||
*ngIf="!(isTurnOffMarkdownParsing$|async)"
|
||||
[data]="model"
|
||||
[hidden]="isShowEdit"
|
||||
*ngIf="!(isTurnOffMarkdownParsing$|async)"
|
||||
class="mat-body-1 markdown-parsed"
|
||||
markdown
|
||||
tabindex="1"></div>
|
||||
</div>
|
||||
|
||||
<div class="controls"
|
||||
*ngIf="isShowControls">
|
||||
<button mat-icon-button
|
||||
(click)="openFullScreen()">
|
||||
<div *ngIf="isShowControls"
|
||||
class="controls">
|
||||
<button (click)="openFullScreen()"
|
||||
mat-icon-button>
|
||||
<mat-icon>fullscreen</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -50,14 +50,27 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
|
|||
map(cfg => cfg && cfg.isTurnOffMarkdown),
|
||||
startWith(false),
|
||||
);
|
||||
private _hideOverFlowTimeout: number;
|
||||
|
||||
constructor(
|
||||
private _electronService: ElectronService,
|
||||
private _cd: ChangeDetectorRef,
|
||||
private _globalConfigService: GlobalConfigService,
|
||||
private _matDialog: MatDialog,
|
||||
) {
|
||||
this.resizeParsedToFit();
|
||||
}
|
||||
|
||||
@HostBinding('class.isFocused') get isFocused() {
|
||||
return this.isShowEdit;
|
||||
}
|
||||
|
||||
private _hideOverFlowTimeout: number;
|
||||
|
||||
private _model: string;
|
||||
|
||||
get model() {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
@Input() set model(v: string) {
|
||||
this._model = v;
|
||||
this.modelCopy = v;
|
||||
|
|
@ -69,20 +82,6 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
get model() {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
|
||||
constructor(
|
||||
private _electronService: ElectronService,
|
||||
private _cd: ChangeDetectorRef,
|
||||
private _globalConfigService: GlobalConfigService,
|
||||
private _matDialog: MatDialog,
|
||||
) {
|
||||
this.resizeParsedToFit();
|
||||
}
|
||||
|
||||
@Input() set isFocus(val: boolean) {
|
||||
if (!this.isShowEdit && val) {
|
||||
this.toggleShowEdit();
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 287 KiB After Width: | Height: | Size: 301 KiB |
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#2b5797</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#2b5797</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,30 @@
|
|||
<svg version="1.1" viewBox="0 0 30 30" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg"
|
||||
focusable="false">
|
||||
<circle cx="12" cy="9" r="1.5" class="clr-i-outline clr-i-outline-path-1"></circle>
|
||||
<circle cx="12" cy="21" r="1.5" class="clr-i-outline clr-i-outline-path-2"></circle>
|
||||
<circle cx="18" cy="9" r="1.5" class="clr-i-outline clr-i-outline-path-3"></circle>
|
||||
<circle cx="18" cy="21" r="1.5" class="clr-i-outline clr-i-outline-path-4"></circle>
|
||||
<circle cx="18" cy="15" r="1.5" class="clr-i-outline clr-i-outline-path-5"></circle>
|
||||
<circle cx="12" cy="15" r="1.5" class="clr-i-outline clr-i-outline-path-6"></circle>
|
||||
<svg focusable="false"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
version="1.1"
|
||||
viewBox="0 0 30 30"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<circle class="clr-i-outline clr-i-outline-path-1"
|
||||
cx="12"
|
||||
cy="9"
|
||||
r="1.5"></circle>
|
||||
<circle class="clr-i-outline clr-i-outline-path-2"
|
||||
cx="12"
|
||||
cy="21"
|
||||
r="1.5"></circle>
|
||||
<circle class="clr-i-outline clr-i-outline-path-3"
|
||||
cx="18"
|
||||
cy="9"
|
||||
r="1.5"></circle>
|
||||
<circle class="clr-i-outline clr-i-outline-path-4"
|
||||
cx="18"
|
||||
cy="21"
|
||||
r="1.5"></circle>
|
||||
<circle class="clr-i-outline clr-i-outline-path-5"
|
||||
cx="18"
|
||||
cy="15"
|
||||
r="1.5"></circle>
|
||||
<circle class="clr-i-outline clr-i-outline-path-6"
|
||||
cx="12"
|
||||
cy="15"
|
||||
r="1.5"></circle>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 661 B After Width: | Height: | Size: 856 B |
|
|
@ -1,5 +1,11 @@
|
|||
<svg width="1024" height="1000" viewBox="0 0 1024 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
<svg fill="none"
|
||||
height="1000"
|
||||
viewBox="0 0 1024 1000"
|
||||
width="1024"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path clip-rule="evenodd"
|
||||
d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z"
|
||||
transform="scale(64)" fill="currentColor"/>
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
transform="scale(64)" />
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 991 B After Width: | Height: | Size: 1 KiB |
|
|
@ -1,20 +1,27 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" id="JIRA_-_Blue" viewBox="61 -48.7 256 256">
|
||||
<style id="style3">.st0{fill:currentColor}</style>
|
||||
<g id="g3051" transform="translate(-25.053 -71.833) scale(1.54217)">
|
||||
<svg id="JIRA_-_Blue"
|
||||
viewBox="61 -48.7 256 256"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<style id="style3">.st0 {
|
||||
fill: currentColor
|
||||
}</style>
|
||||
<g id="g3051"
|
||||
transform="translate(-25.053 -71.833) scale(1.54217)">
|
||||
<g id="g17">
|
||||
<g id="g19">
|
||||
<g id="g21">
|
||||
<g id="g23">
|
||||
<path class="st0"
|
||||
d="M103.1 24.9c-6 0-10.9 4.9-10.9 10.9s4.9 10.9 10.9 10.9S114 41.8 114 35.8c-.1-6-4.9-10.9-10.9-10.9z"
|
||||
id="path25" fill="currentColor"/>
|
||||
fill="currentColor"
|
||||
id="path25" />
|
||||
</g>
|
||||
</g>
|
||||
<g id="g27">
|
||||
<g id="g29">
|
||||
<path class="st0"
|
||||
d="M174.5 24.9c-6 0-10.9 4.9-10.9 10.9s4.9 10.9 10.9 10.9 10.9-4.9 10.9-10.9-4.9-10.9-10.9-10.9z"
|
||||
id="path31" fill="currentColor"/>
|
||||
fill="currentColor"
|
||||
id="path31" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
|
@ -22,22 +29,26 @@
|
|||
<g id="g35">
|
||||
<path class="st0"
|
||||
d="M138.8 15c-6 0-10.9 4.9-10.9 10.9s4.9 10.9 10.9 10.9 10.9-4.9 10.9-10.9c0-6.1-4.9-10.9-10.9-10.9z"
|
||||
id="path37" fill="currentColor"/>
|
||||
fill="currentColor"
|
||||
id="path37" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g39">
|
||||
<path class="st0"
|
||||
d="M200.3 55.3c-.6-.5-1.3-.7-2.1-.7-.4 0-.8.1-1.2.2-7.4 2.8-14.8 5-21.7 6.5-1.4.3-2.5 1.4-3 2.6-5 13.6-17.7 26.6-31 40.2-.5.5-1.3 1.2-2.5 1.2s-2-.6-2.5-1.2c-13.3-13.6-26-26.5-31-40.2-.5-1.2-1.6-2.3-3-2.6-6.8-1.6-14.3-3.8-21.7-6.6-.4-.2-.8-.2-1.2-.2-.7 0-1.5.2-2.1.7-.9.7-1.4 1.9-1.3 3 1.1 11.3 5.6 22.4 13.7 34 7.6 10.9 17.5 21 27.1 30.8 17.6 18 34.3 35 35.8 54.7a3.4 3.4 0 0 0 3.4 3.2h20.7c.9 0 1.8-.4 2.5-1.1.6-.7 1-1.6.9-2.5-.7-13.2-5.6-26.4-15.2-40.4-2-2.9-4.1-5.8-6.4-8.6-.9-1.1-.5-2.5.2-3.3l2.1-2.1c9.6-9.8 19.5-19.9 27.1-30.8 8.1-11.6 12.6-22.6 13.7-34 .1-.9-.4-2.1-1.3-2.8z"
|
||||
id="path41" fill="currentColor"/>
|
||||
fill="currentColor"
|
||||
id="path41" />
|
||||
<g id="g43">
|
||||
<path class="st0"
|
||||
d="M116.5 138.4c-.5-.5-1.2-1-2.4-1-1.5 0-2.5 1.1-2.8 1.6-8.7 13.3-13.2 25.8-13.8 38.4 0 .9.3 1.9.9 2.5.6.7 1.5 1.1 2.5 1.1h20.7c1.8 0 3.3-1.4 3.4-3.2.5-6.1 2.3-12.1 5.8-18.5 1.2-2.2.3-4.3-.4-5.2-3.5-4.5-7.6-9.2-13.9-15.7z"
|
||||
id="path45" fill="currentColor"/>
|
||||
fill="currentColor"
|
||||
id="path45" />
|
||||
</g>
|
||||
<path class="st0"
|
||||
d="M138.8 65.4c6.5 0 10.3-.4 11.6-.4 1.6 0 3.1 1.3 3.1 3.2 0 .5-.1.9-.3 1.3-.9 2.3-4.8 10.6-12.1 17.7-.8.8-1.8.9-2.4.9-.6 0-1.6-.1-2.4-.9-7.3-7.1-11.2-15.4-12.1-17.7-.1-.3-.3-.7-.3-1.3 0-1.8 1.5-3.2 3.1-3.2 1.5 0 5.3.4 11.8.4z"
|
||||
id="path47" fill="currentColor"/>
|
||||
fill="currentColor"
|
||||
id="path47" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.5 KiB |
|
|
@ -1,8 +1,11 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
<svg fill="currentColor"
|
||||
id="svg3360"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<polygon points="3,3 21,12 3,21" style="stroke:currentColor;stroke-width:1.5;stroke-opacity:1" fill-opacity="0"
|
||||
stroke-opacity="1" stroke-linejoin="round"/>
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<polygon fill-opacity="0"
|
||||
points="3,3 21,12 3,21"
|
||||
stroke-linejoin="round"
|
||||
stroke-opacity="1"
|
||||
style="stroke:currentColor;stroke-width:1.5;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 305 B After Width: | Height: | Size: 339 B |
|
|
@ -1,21 +1,25 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="1024.000000pt" height="1024.000000pt" viewBox="0 0 1024.000000 1024.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M4785 10230 c-1318 -97 -2500 -656 -3398 -1608 -1037 -1099 -1534
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg height="1024.000000pt"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
version="1.0"
|
||||
viewBox="0 0 1024.000000 1024.000000"
|
||||
width="1024.000000pt"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g fill="#000000"
|
||||
stroke="none"
|
||||
transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)">
|
||||
<path d="M4785 10230 c-1318 -97 -2500 -656 -3398 -1608 -1037 -1099 -1534
|
||||
-2623 -1347 -4133 231 -1861 1460 -3445 3208 -4133 436 -172 866 -275 1382
|
||||
-333 218 -24 761 -24 985 0 787 86 1486 317 2134 705 1352 809 2259 2203 2451
|
||||
3766 159 1297 -183 2606 -956 3659 l-72 98 -174 -318 c-95 -175 -914 -1672
|
||||
-1818 -3326 -905 -1654 -1649 -3006 -1655 -3005 -13 4 -4136 3461 -4131 3465
|
||||
1 1 864 -346 1917 -772 1053 -427 1919 -774 1926 -773 8 2 2540 3210 3805
|
||||
4820 l29 37 -73 83 c-559 642 -1236 1121 -2008 1423 -442 173 -882 279 -1370
|
||||
330 -172 19 -666 27 -835 15z"/>
|
||||
</g>
|
||||
330 -172 19 -666 27 -835 15z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1.1 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 64 64">
|
||||
<svg viewBox="0 0 64 64"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<path id="path3395"
|
||||
style="fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.46761858;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:13.80000019;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 0.65537046,34.582787 32.452267,63.855301 63.791548,0.94356422 30.222644,47.722023 Z"/>
|
||||
<path d="M 0.65537046,34.582787 32.452267,63.855301 63.791548,0.94356422 30.222644,47.722023 Z"
|
||||
id="path3395"
|
||||
style="fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.46761858;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:13.80000019;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 392 B After Width: | Height: | Size: 393 B |