added mails page + reworked button placement
This commit is contained in:
50
BetterIServ.Mobile/src/app/api/mail.service.ts
Normal file
50
BetterIServ.Mobile/src/app/api/mail.service.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {IServService} from "./iserv.service";
|
||||
import {HttpClient, HttpEvent} from "@angular/common/http";
|
||||
import {MailContent, MailData, MailFolder} from "../entities/mail";
|
||||
import {firstValueFrom, Observable} from "rxjs";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MailService {
|
||||
|
||||
constructor(private iserv: IServService, private client: HttpClient) { }
|
||||
|
||||
public async getMails(folder: string, page: number): Promise<MailContent[]> {
|
||||
const mails = await firstValueFrom(this.client.post<MailContent[]>(this.iserv.backend + `/mail/list/${page}?folder=${folder}`, this.iserv.userdata));
|
||||
|
||||
for (let mail of mails) {
|
||||
mail.time = new Date(mail.time);
|
||||
}
|
||||
|
||||
return mails;
|
||||
}
|
||||
|
||||
public async getMail(id: number): Promise<MailContent> {
|
||||
const mail = await firstValueFrom(this.client.post<MailContent>(this.iserv.backend + "/mail/content/" + id, this.iserv.userdata));
|
||||
|
||||
mail.time = new Date(mail.time);
|
||||
return mail;
|
||||
}
|
||||
|
||||
public async getFolders(): Promise<MailFolder[]> {
|
||||
return (await firstValueFrom(this.client.post<{value: MailFolder[]}>(this.iserv.backend + "/mail/folder", this.iserv.userdata))).value;
|
||||
}
|
||||
|
||||
public async sendMail(subject: string, receiver: string, mailBody: string) {
|
||||
const data: MailData = {
|
||||
domain: this.iserv.userdata.domain,
|
||||
username: this.iserv.userdata.username,
|
||||
password: this.iserv.userdata.password,
|
||||
token: this.iserv.userdata.token,
|
||||
subject, receiver, mailBody
|
||||
};
|
||||
await firstValueFrom(this.client.post(this.iserv.backend + "/mail/send", data));
|
||||
}
|
||||
|
||||
public downloadAttachment(mailId: number, attachment: string): Observable<HttpEvent<Blob>> {
|
||||
return this.client.post(this.iserv.backend + `/mail/download/${mailId}/${attachment}`, this.iserv.userdata, {responseType: "blob", reportProgress: true, observe: "events"});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export class AppComponent {
|
||||
|
||||
public appPages = [
|
||||
{ title: 'Übersicht', url: '/home', icon: 'home' },
|
||||
{ title: 'E-Mail', url: '/email', icon: 'mail' },
|
||||
{ title: 'E-Mail', url: '/mails', icon: 'mail' },
|
||||
{ title: 'Dateien', url: '/files', icon: 'folder' },
|
||||
{ title: 'Aufgaben', url: '/tasks', icon: 'clipboard' },
|
||||
{ title: 'Stundenplan', url: '/schedule', icon: 'grid' },
|
||||
|
||||
@@ -18,4 +18,8 @@ export const routes: Routes = [
|
||||
path: 'files',
|
||||
loadComponent: () => import('./pages/files/files.page').then( m => m.FilesPage)
|
||||
},
|
||||
{
|
||||
path: 'mails',
|
||||
loadComponent: () => import('./pages/mails/mails.page').then( m => m.MailsPage)
|
||||
},
|
||||
];
|
||||
|
||||
28
BetterIServ.Mobile/src/app/entities/mail.ts
Normal file
28
BetterIServ.Mobile/src/app/entities/mail.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {Userdata} from "./userdata";
|
||||
|
||||
export interface MailFolder {
|
||||
name: string;
|
||||
newMessageCount: number;
|
||||
}
|
||||
|
||||
export interface MailData extends Userdata {
|
||||
mailBody: string;
|
||||
receiver: string;
|
||||
subject: string;
|
||||
}
|
||||
|
||||
export interface MailAddress {
|
||||
displayName: string;
|
||||
user: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
export interface MailContent {
|
||||
id: number;
|
||||
sender: MailAddress;
|
||||
subject: string;
|
||||
message: string;
|
||||
time: Date;
|
||||
read: boolean;
|
||||
attachments: string[];
|
||||
}
|
||||
@@ -4,6 +4,36 @@
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Dateien</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="onMove()" *ngIf="clipboard != undefined"><ion-icon ios="checkmark-circle-outline" md="checkmark-circle-sharp" ></ion-icon></ion-button>
|
||||
<ion-button (click)="upload.click()"><ion-icon ios="arrow-up-circle-outline" md="arrow-up-circle-sharp" /></ion-button>
|
||||
<ion-button id="create-folder"><ion-icon ios="add-circle-outline" md="add-circle-sharp" ></ion-icon></ion-button>
|
||||
<form #uploadForm>
|
||||
<input type="file" hidden="hidden" multiple (change)="onUpload(upload.files, uploadForm)" #upload>
|
||||
</form>
|
||||
|
||||
<ion-modal trigger="create-folder" (willDismiss)="createFolder($event)" #modal>
|
||||
<ng-template>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-button (click)="modal.dismiss(null, 'cancel')">Abbrechen</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Neuer Ordner</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="modal.dismiss(newFolder.value, 'confirm')" [strong]="true">Erstellen</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Name</ion-label>
|
||||
<ion-input aria-label="Name" type="text" #newFolder/>
|
||||
</ion-item>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
</ion-buttons>
|
||||
<ion-progress-bar type="indeterminate" *ngIf="loading" />
|
||||
<ion-progress-bar [value]="progress" *ngIf="progress != -1" />
|
||||
</ion-toolbar>
|
||||
@@ -16,43 +46,6 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<ion-button (click)="upload.click()"><ion-icon ios="arrow-up-circle-outline" md="arrow-up-circle-sharp" /></ion-button>
|
||||
<ion-button id="create-folder"><ion-icon ios="add-circle-outline" md="add-circle-sharp" ></ion-icon></ion-button>
|
||||
<form #uploadForm>
|
||||
<input type="file" hidden="hidden" multiple (change)="onUpload(upload.files, uploadForm)" #upload>
|
||||
</form>
|
||||
|
||||
<ion-modal trigger="create-folder" (willDismiss)="createFolder($event)" #modal>
|
||||
<ng-template>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-button (click)="modal.dismiss(null, 'cancel')">Abbrechen</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Neuer Ordner</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="modal.dismiss(newFolder.value, 'confirm')" [strong]="true">Erstellen</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Name</ion-label>
|
||||
<ion-input aria-label="Name" type="text" #newFolder/>
|
||||
</ion-item>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
</ion-col>
|
||||
<ion-col *ngIf="clipboard">
|
||||
<ion-button (click)="onMove()">Verschieben</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
<section class="container">
|
||||
<ion-list>
|
||||
<ion-list-header>{{currentDirectory}}</ion-list-header>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {ActionSheetController, AlertController, IonicModule, Platform} from '@ionic/angular';
|
||||
import {ActionSheetController, AlertController, IonicModule, Platform, ToastController} from '@ionic/angular';
|
||||
import {WebdavService} from "../../api/webdav.service";
|
||||
import {DirectoryContent} from "../../entities/directoryContent";
|
||||
import {File} from "@awesome-cordova-plugins/file/ngx";
|
||||
@@ -24,7 +24,7 @@ export class FilesPage implements OnInit {
|
||||
|
||||
public progress: number = -1;
|
||||
|
||||
constructor(private webdav: WebdavService, private platform: Platform, private menus: ActionSheetController, private alerts: AlertController) { }
|
||||
constructor(private webdav: WebdavService, private platform: Platform, private menus: ActionSheetController, private alerts: AlertController, private toasts: ToastController) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.directoryContent = await this.webdav.getDirectory(this.currentDirectory);
|
||||
@@ -122,9 +122,10 @@ export class FilesPage implements OnInit {
|
||||
await this.switchDirectory(this.currentDirectory);
|
||||
this.loading = false;
|
||||
|
||||
await (await this.alerts.create({
|
||||
header: "Element gelöscht!",
|
||||
buttons: ["Ok"]
|
||||
await (await this.toasts.create({
|
||||
message: "Element gelöscht!",
|
||||
position: "bottom",
|
||||
duration: 2000
|
||||
})).present();
|
||||
}
|
||||
}
|
||||
@@ -152,6 +153,12 @@ export class FilesPage implements OnInit {
|
||||
await this.switchDirectory(this.currentDirectory);
|
||||
this.loading = false;
|
||||
form.reset();
|
||||
|
||||
await (await this.toasts.create({
|
||||
message: "Element hochgeladen!",
|
||||
position: "bottom",
|
||||
duration: 2000
|
||||
})).present();
|
||||
}
|
||||
|
||||
public async createFolder(event: any) {
|
||||
@@ -160,6 +167,12 @@ export class FilesPage implements OnInit {
|
||||
await this.webdav.createFolder(this.currentDirectory + event.detail.data);
|
||||
await this.switchDirectory(this.currentDirectory);
|
||||
this.loading = false;
|
||||
|
||||
await (await this.toasts.create({
|
||||
message: "Ordner erstellt!",
|
||||
position: "bottom",
|
||||
duration: 2000
|
||||
})).present();
|
||||
}
|
||||
|
||||
public async onMove() {
|
||||
@@ -168,6 +181,12 @@ export class FilesPage implements OnInit {
|
||||
await this.switchDirectory(this.currentDirectory);
|
||||
this.loading = false;
|
||||
delete this.clipboard;
|
||||
|
||||
await (await this.toasts.create({
|
||||
message: "Element verschoben!",
|
||||
position: "bottom",
|
||||
duration: 2000
|
||||
})).present();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
96
BetterIServ.Mobile/src/app/pages/mails/mails.page.html
Normal file
96
BetterIServ.Mobile/src/app/pages/mails/mails.page.html
Normal file
@@ -0,0 +1,96 @@
|
||||
<ion-header [translucent]="true">
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
<ion-title>E-Mails</ion-title>
|
||||
|
||||
<ion-buttons slot="end">
|
||||
<ion-button id="new-email"><ion-icon ios="add-circle-outline" md="add-circle-sharp" /></ion-button>
|
||||
|
||||
<ion-modal trigger="new-email" #newEmail>
|
||||
<ng-template>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-button (click)="newEmail.dismiss()">Zurück</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Neue E-Mail</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="sendMail(receiver.value.toString(), subject.value.toString(), message.value, newEmail)">Senden</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-progress-bar type="indeterminate" *ngIf="showLoading" />
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding current-mail">
|
||||
<form class="new-email">
|
||||
<ion-input type="email" label="An" labelPlacement="floating" fill="outline" #receiver />
|
||||
<ion-input type="text" label="Betreff" labelPlacement="floating" fill="outline" #subject />
|
||||
<ion-textarea label="Nachricht" labelPlacement="floating" fill="outline" auto-grow="true" #message />
|
||||
</form>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
</ion-buttons>
|
||||
|
||||
<ion-progress-bar type="indeterminate" *ngIf="showLoading" />
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content [fullscreen]="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title size="large">E-Mails</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-select label="Ordner" [value]="folders[0]" interface="action-sheet" (ionChange)="changeFolder(select.value)" #select>
|
||||
<ion-select-option *ngFor="let folder of folders" [value]="folder">
|
||||
{{folder.name}}
|
||||
</ion-select-option>
|
||||
</ion-select>
|
||||
|
||||
<ion-list>
|
||||
<ion-item *ngFor="let message of mails" class="mail pointer" (click)="selectMail(message, mailModal)">
|
||||
<div>
|
||||
<div>
|
||||
<ion-icon ios="mail-unread-outline" md="mail-unread-sharp" *ngIf="!message.read" />
|
||||
<ion-label>{{message.sender.displayName}}</ion-label>
|
||||
<ion-label class="date">{{message.time.toLocaleDateString()}}</ion-label>
|
||||
</div>
|
||||
<ion-label class="subject">{{message.subject}}</ion-label>
|
||||
</div>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<ion-infinite-scroll (ionInfinite)="loadMore($event)">
|
||||
<ion-infinite-scroll-content></ion-infinite-scroll-content>
|
||||
</ion-infinite-scroll>
|
||||
|
||||
<ion-modal #mailModal>
|
||||
<ng-template>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-button (click)="mailModal.dismiss()">Zurück</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{currentMail?.sender.displayName}}</ion-title>
|
||||
<ion-progress-bar type="indeterminate" *ngIf="showLoading" />
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding current-mail">
|
||||
<div class="header">
|
||||
<ion-label class="subject">{{currentMail?.subject}}</ion-label>
|
||||
<ion-label class="time">{{currentMail?.time.toLocaleDateString()}}</ion-label>
|
||||
<ion-list *ngIf="currentMail?.attachments?.length > 0">
|
||||
<ion-item *ngFor="let attachment of currentMail?.attachments" class="pointer" (click)="downloadAttachment(attachment, currentMail?.id)">
|
||||
<ion-label>{{attachment}}</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</div>
|
||||
<ion-item-divider />
|
||||
<div class="message" [innerHtml]="currentMail?.message"></div>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
|
||||
</ion-content>
|
||||
51
BetterIServ.Mobile/src/app/pages/mails/mails.page.scss
Normal file
51
BetterIServ.Mobile/src/app/pages/mails/mails.page.scss
Normal file
@@ -0,0 +1,51 @@
|
||||
ion-select {
|
||||
padding-inline: 15px;
|
||||
}
|
||||
|
||||
.mail > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
margin-block: 15px;
|
||||
width: 100%;
|
||||
|
||||
.subject, .date {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
|
||||
ion-icon {
|
||||
margin-right: 5px;
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
.date {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.current-mail {
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
.subject {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-email {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
height: 100%;
|
||||
}
|
||||
109
BetterIServ.Mobile/src/app/pages/mails/mails.page.ts
Normal file
109
BetterIServ.Mobile/src/app/pages/mails/mails.page.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {InfiniteScrollCustomEvent, IonicModule, IonModal, Platform, ToastController} from '@ionic/angular';
|
||||
import {MailService} from "../../api/mail.service";
|
||||
import {MailContent, MailFolder} from "../../entities/mail";
|
||||
import {marked} from "marked";
|
||||
import {HttpDownloadProgressEvent, HttpEventType} from "@angular/common/http";
|
||||
import {File} from "@awesome-cordova-plugins/file/ngx";
|
||||
import {saveAs} from "file-saver";
|
||||
|
||||
@Component({
|
||||
selector: 'app-mails',
|
||||
templateUrl: './mails.page.html',
|
||||
styleUrls: ['./mails.page.scss'],
|
||||
standalone: true,
|
||||
imports: [IonicModule, CommonModule, FormsModule]
|
||||
})
|
||||
export class MailsPage implements OnInit {
|
||||
|
||||
public showLoading = false;
|
||||
public mails: MailContent[] = [];
|
||||
public folders: MailFolder[] = [];
|
||||
public currentMail: MailContent;
|
||||
private currentPage = 0;
|
||||
private currentFolder: MailFolder;
|
||||
|
||||
constructor(private mail: MailService, private platform: Platform, private toasts: ToastController) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.showLoading = true;
|
||||
const folderResponse = this.mail.getFolders();
|
||||
const mailResponse = this.mail.getMails("INBOX", this.currentPage);
|
||||
await Promise.all([mailResponse, folderResponse]);
|
||||
|
||||
this.folders = await folderResponse;
|
||||
this.mails = await mailResponse;
|
||||
|
||||
this.currentFolder = this.folders.filter(folder => folder.name == "INBOX")[0];
|
||||
this.showLoading = false;
|
||||
}
|
||||
|
||||
public async changeFolder(folder: MailFolder) {
|
||||
this.showLoading = true;
|
||||
this.currentFolder = folder;
|
||||
this.currentPage = 0;
|
||||
this.mails = await this.mail.getMails(this.currentFolder.name, this.currentPage);
|
||||
this.showLoading = false;
|
||||
}
|
||||
|
||||
public async loadMore(event: any) {
|
||||
this.showLoading = true;
|
||||
this.currentPage++;
|
||||
const newMails = await this.mail.getMails(this.currentFolder.name, this.currentPage);
|
||||
this.mails.push(...newMails);
|
||||
this.showLoading = false;
|
||||
await event.target.complete();
|
||||
}
|
||||
|
||||
public async selectMail(message: MailContent, modal: IonModal) {
|
||||
this.showLoading = true;
|
||||
message.read = true;
|
||||
this.currentMail = message;
|
||||
|
||||
const body = await this.mail.getMail(message.id);
|
||||
this.currentMail.message = marked(body.message);
|
||||
this.currentMail.attachments = body.attachments;
|
||||
|
||||
this.showLoading = false;
|
||||
await modal.present();
|
||||
}
|
||||
|
||||
public async downloadAttachment(attachment: string, mailId: number) {
|
||||
this.showLoading = true;
|
||||
this.mail.downloadAttachment(mailId, attachment).subscribe(async event => {
|
||||
if (event.type == HttpEventType.Response) {
|
||||
const blob = event.body;
|
||||
const file = new File();
|
||||
|
||||
if (this.platform.is('desktop')) {
|
||||
saveAs(blob, attachment);
|
||||
this.showLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const downloadPath = (
|
||||
this.platform.is('android')
|
||||
) ? file.externalDataDirectory : file.documentsDirectory;
|
||||
await file.writeFile(downloadPath, attachment, blob, {replace: true});
|
||||
this.showLoading = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public async sendMail(receiver: string, subject: string, message: string, modal: IonModal) {
|
||||
this.showLoading = true;
|
||||
await this.mail.sendMail(subject, receiver, message);
|
||||
await modal.dismiss();
|
||||
this.showLoading = false;
|
||||
|
||||
const toast = await this.toasts.create({
|
||||
message: "E-Mail gesendet!",
|
||||
duration: 2000,
|
||||
position: "bottom"
|
||||
});
|
||||
await toast.present();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user