Archived
Private
Public Access
1
0

added mails page + reworked button placement

This commit is contained in:
2023-04-22 14:12:20 +02:00
parent a2149d24ec
commit c15df303f0
19 changed files with 560 additions and 50 deletions

View 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"});
}
}

View File

@@ -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' },

View File

@@ -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)
},
];

View 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[];
}

View File

@@ -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>

View File

@@ -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();
}
}

View 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>

View 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%;
}

View 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();
}
}

View File

@@ -28,3 +28,7 @@
ion-menu-button {
color: var(--ion-color-primary);
}
.pointer {
cursor: pointer;
}