Auth Email Rework (#1567)
* Hooked up Send to for Series and volumes and fixed a bug where Email Service errors weren't propagating to the UI layer. When performing actions on series detail, don't disable the button anymore. * Added send to action to volumes * Fixed a bug where .kavitaignore wasn't being applied at library root level * Added a notification for when a device is being sent a file. * Added a check in forgot password for users that do not have an email set or aren't confirmed. * Added a new api for change email and moved change password directly into new Account tab (styling and logic needs testing) * Save approx scroll position like with jump key, but on normal click of card. * Implemented the ability to change your email address or set one. This requires a 2 step process using a confirmation token. This needs polishing and css. * Removed an unused directive from codebase * Fixed up some typos on publicly * Updated query for Pending Invites to also check if the user account has not logged in at least once. * Cleaned up the css for validate email change * Hooked in an indicator to tell user that a user has an unconfirmed email * Cleaned up code smells
This commit is contained in:
parent
3792ac3421
commit
5f17c2fb73
49 changed files with 816 additions and 274 deletions
|
|
@ -11,6 +11,7 @@ import { ThemeService } from './theme.service';
|
|||
import { InviteUserResponse } from '../_models/invite-user-response';
|
||||
import { UserUpdateEvent } from '../_models/events/user-update-event';
|
||||
import { DeviceService } from './device.service';
|
||||
import { UpdateEmailResponse } from '../_models/email/update-email-response';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
|
@ -132,6 +133,10 @@ export class AccountService implements OnDestroy {
|
|||
);
|
||||
}
|
||||
|
||||
isEmailConfirmed() {
|
||||
return this.httpClient.get<boolean>(this.baseUrl + 'account/email-confirmed');
|
||||
}
|
||||
|
||||
migrateUser(model: {email: string, username: string, password: string, sendEmail: boolean}) {
|
||||
return this.httpClient.post<string>(this.baseUrl + 'account/migrate-email', model, {responseType: 'text' as 'json'});
|
||||
}
|
||||
|
|
@ -152,6 +157,10 @@ export class AccountService implements OnDestroy {
|
|||
return this.httpClient.post<User>(this.baseUrl + 'account/confirm-email', model);
|
||||
}
|
||||
|
||||
confirmEmailUpdate(model: {email: string, token: string}) {
|
||||
return this.httpClient.post<User>(this.baseUrl + 'account/confirm-email-update', model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a user id, returns a full url for setting up the user account
|
||||
* @param userId
|
||||
|
|
@ -181,6 +190,10 @@ export class AccountService implements OnDestroy {
|
|||
return this.httpClient.post(this.baseUrl + 'account/update', model);
|
||||
}
|
||||
|
||||
updateEmail(email: string) {
|
||||
return this.httpClient.post<UpdateEmailResponse>(this.baseUrl + 'account/update/email', {email});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will get latest preferences for a user and cache them into user store
|
||||
* @returns
|
||||
|
|
|
|||
|
|
@ -383,6 +383,24 @@ export class ActionFactoryService {
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'Send To',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [
|
||||
{
|
||||
action: Action.SendTo,
|
||||
title: '',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
dynamicList: this.deviceService.devices$.pipe(map((devices: Array<Device>) => devices.map(d => {
|
||||
return {'title': d.name, 'data': d};
|
||||
}), shareReplay())),
|
||||
children: []
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
action: Action.Download,
|
||||
title: 'Download',
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ import { AddToListModalComponent, ADD_FLOW } from '../reading-list/_modals/add-t
|
|||
import { EditReadingListModalComponent } from '../reading-list/_modals/edit-reading-list-modal/edit-reading-list-modal.component';
|
||||
import { ConfirmService } from '../shared/confirm.service';
|
||||
import { Chapter } from '../_models/chapter';
|
||||
import { Device } from '../_models/device/device';
|
||||
import { Library } from '../_models/library';
|
||||
import { ReadingList } from '../_models/reading-list';
|
||||
import { Series } from '../_models/series';
|
||||
import { Volume } from '../_models/volume';
|
||||
import { DeviceService } from './device.service';
|
||||
import { LibraryService } from './library.service';
|
||||
import { MemberService } from './member.service';
|
||||
import { ReaderService } from './reader.service';
|
||||
|
|
@ -39,7 +41,7 @@ export class ActionService implements OnDestroy {
|
|||
|
||||
constructor(private libraryService: LibraryService, private seriesService: SeriesService,
|
||||
private readerService: ReaderService, private toastr: ToastrService, private modalService: NgbModal,
|
||||
private confirmService: ConfirmService, private memberService: MemberService) { }
|
||||
private confirmService: ConfirmService, private memberService: MemberService, private deviceSerivce: DeviceService) { }
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy.next();
|
||||
|
|
@ -552,6 +554,15 @@ export class ActionService implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
sendToDevice(chapterIds: Array<number>, device: Device, callback?: VoidActionCallback) {
|
||||
this.deviceSerivce.sendTo(chapterIds, device.id).subscribe(() => {
|
||||
this.toastr.success('File emailed to ' + device.name);
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async promptIfForce(extraContent: string = '') {
|
||||
// Prompt user if we should do a force or not
|
||||
const config = this.confirmService.defaultConfirm;
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ export class DeviceService {
|
|||
}));
|
||||
}
|
||||
|
||||
sendTo(chapterId: number, deviceId: number) {
|
||||
return this.httpClient.post(this.baseUrl + 'device/send-to', {deviceId, chapterId}, {responseType: 'text' as 'json'});
|
||||
sendTo(chapterIds: Array<number>, deviceId: number) {
|
||||
return this.httpClient.post(this.baseUrl + 'device/send-to', {deviceId, chapterIds}, {responseType: 'text' as 'json'});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -51,31 +51,35 @@ export enum EVENTS {
|
|||
/**
|
||||
* A subtype of NotificationProgress that represents a file being processed for cover image extraction
|
||||
*/
|
||||
CoverUpdateProgress = 'CoverUpdateProgress',
|
||||
CoverUpdateProgress = 'CoverUpdateProgress',
|
||||
/**
|
||||
* A library is created or removed from the instance
|
||||
*/
|
||||
LibraryModified = 'LibraryModified',
|
||||
LibraryModified = 'LibraryModified',
|
||||
/**
|
||||
* A user updates an entities read progress
|
||||
*/
|
||||
UserProgressUpdate = 'UserProgressUpdate',
|
||||
UserProgressUpdate = 'UserProgressUpdate',
|
||||
/**
|
||||
* A user updates account or preferences
|
||||
*/
|
||||
UserUpdate = 'UserUpdate',
|
||||
UserUpdate = 'UserUpdate',
|
||||
/**
|
||||
* When bulk bookmarks are being converted
|
||||
*/
|
||||
ConvertBookmarksProgress = 'ConvertBookmarksProgress',
|
||||
ConvertBookmarksProgress = 'ConvertBookmarksProgress',
|
||||
/**
|
||||
* When files are being scanned to calculate word count
|
||||
*/
|
||||
WordCountAnalyzerProgress = 'WordCountAnalyzerProgress',
|
||||
WordCountAnalyzerProgress = 'WordCountAnalyzerProgress',
|
||||
/**
|
||||
* When the user needs to be informed, but it's not a big deal
|
||||
*/
|
||||
Info = 'Info',
|
||||
Info = 'Info',
|
||||
/**
|
||||
* A user is sending files to their device
|
||||
*/
|
||||
SendingToDevice = 'SendingToDevice',
|
||||
}
|
||||
|
||||
export interface Message<T> {
|
||||
|
|
@ -261,6 +265,13 @@ export class MessageHubService {
|
|||
payload: resp.body
|
||||
});
|
||||
});
|
||||
|
||||
this.hubConnection.on(EVENTS.SendingToDevice, resp => {
|
||||
this.messagesSource.next({
|
||||
event: EVENTS.SendingToDevice,
|
||||
payload: resp.body
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stopHubConnection() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue