Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add toasts where sensible #294

Merged
Merged
30 changes: 27 additions & 3 deletions teammapper-frontend/src/app/core/services/mmp/mmp.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { SettingsService } from '../settings/settings.service';
import { ToastrService } from 'ngx-toastr';
import { UtilsService } from '../utils/utils.service';
import { jsPDF } from 'jspdf';
import { first } from 'rxjs/operators';
Expand Down Expand Up @@ -31,7 +32,11 @@ export class MmpService implements OnDestroy {
private additionalOptions: CachedMapOptions;
private settingsSubscription: Subscription;

constructor(public settingsService: SettingsService) {
constructor(
public settingsService: SettingsService,
public utilsService: UtilsService,
public toastrService: ToastrService
) {
this.additionalOptions = null;
this.branchColors = COLORS;

Expand Down Expand Up @@ -314,8 +319,27 @@ export class MmpService implements OnDestroy {
* Copy a node with his children in the mmp clipboard.
* If id is not specified, copy the selected node.
*/
public copyNode(nodeId?: string) {
this.currentMap.instance.copyNode(nodeId);
public async copyNode(nodeId?: string) {
try {
this.currentMap.instance.copyNode(nodeId);

const successMessage = await this.utilsService.translate(
'TOASTS.NODE_COPIED'
);
this.toastrService.success(successMessage);
} catch (e) {
if (e.message == 'The root node can not be copied') {
JannikStreek marked this conversation as resolved.
Show resolved Hide resolved
const rootNodeFailureMessage = await this.utilsService.translate(
'TOASTS.ERRORS.ROOT_NODE_COPIED'
);
this.toastrService.error(rootNodeFailureMessage);
} else {
const genericErrorMessage = await this.utilsService.translate(
'TOASTS.ERRORS.GENERIC_NODE_COPY_ERROR'
);
this.toastrService.error(genericErrorMessage);
}
}
}

/**
Expand Down
36 changes: 36 additions & 0 deletions teammapper-frontend/src/app/guards/toast.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Injectable } from '@angular/core';
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot,
Router,
} from '@angular/router';
import { ToastrService } from 'ngx-toastr';

@Injectable({
providedIn: 'root',
})
export class ToastGuard implements CanActivate {
constructor(private router: Router, private toastrService: ToastrService) {}

canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Promise<boolean> | boolean {
JannikStreek marked this conversation as resolved.
Show resolved Hide resolved
const showToast = next.queryParamMap.get('showToast');
const toastMessage = next.queryParamMap.get('toastMessage');

if (showToast === 'true' && toastMessage) {
JannikStreek marked this conversation as resolved.
Show resolved Hide resolved
this.toastrService.success(toastMessage);

// This preserves both map UUID and the all important fragment whilst deleting anything toast-related.
const urlTree = this.router.parseUrl(state.url);
delete urlTree.queryParams['showToast'];
delete urlTree.queryParams['toastMessage'];
this.router.navigateByUrl(urlTree);

return true;
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,18 @@ export class DialogAboutComponent {
if (confirm(this.translateService.instant('MODALS.INFO.CONFIRM_DELETE'))) {
await this.mapSyncService.deleteMap(await this.mapAdminId);
await this.storageService.remove(this.map.uuid);
window.location.reload();

const url = new URL(window.location.href);
JannikStreek marked this conversation as resolved.
Show resolved Hide resolved

// Set the query parameters
url.searchParams.set('showToast', 'true');
url.searchParams.set(
'toastMessage',
this.translateService.instant('TOASTS.DELETE_MAP_SUCCESS')
);

// Reload the page with the new URL
window.location.href = url.toString();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { qrcodeStyling } from './qrcode-settings';
import { API_URL, HttpService } from 'src/app/core/http/http.service';
import { UtilsService } from 'src/app/core/services/utils/utils.service';
import { ToastrService } from 'ngx-toastr';
import { StorageService } from 'src/app/core/services/storage/storage.service';

@Component({
selector: 'teammapper-dialog-share',
Expand Down Expand Up @@ -31,7 +32,8 @@ export class DialogShareComponent implements OnInit {
constructor(
private httpService: HttpService,
private toastrService: ToastrService,
private utilsService: UtilsService
private utilsService: UtilsService,
private storageService: StorageService
) {}

ngOnInit() {
Expand All @@ -55,10 +57,14 @@ export class DialogShareComponent implements OnInit {
this.qrCode.append(this.qrCodeCanvas.nativeElement);
}

copy() {
async copy() {
this.inputLink.nativeElement.select();
// requires a secure origin (https) to work
navigator.clipboard.writeText(this.getLink());
const successMessage = await this.utilsService.translate(
'TOASTS.URL_COPIED'
);
this.toastrService.success(successMessage);
}

async duplicateMindMap() {
Expand All @@ -72,20 +78,28 @@ export class DialogShareComponent implements OnInit {

const newMap = await response.json();
if (newMap && newMap.map.uuid) {
const sucessMessage = await this.utilsService.translate(
const successMessage = await this.utilsService.translate(
'TOASTS.SUCCESSFULLY_DUPLICATED'
);
this.toastrService.success(sucessMessage);

// Built in delay to allow users to read the toast (if we redirect immediately the toast gets swallowed up)
// The reason we're doing a client-side replace and not server-side redirect is to make sure all client-side data is refreshed
setTimeout(
() =>
window.location.replace(
`/map/${newMap.map.uuid}#${newMap.modificationSecret}`
),
750
);

await this.storageService.set(newMap.map.uuid, {
adminId: newMap.adminId,
modificationSecret: newMap.modificationSecret,
ttl: newMap.map.deletedAt,
rootName: newMap.map.data[0].name,
});

const baseUrl = `/map/${newMap.map.uuid}`;
const fragment = newMap.modificationSecret;

const url = new URL(baseUrl, window.location.origin);

url.searchParams.set('showToast', 'true');
url.searchParams.set('toastMessage', successMessage);

url.hash = fragment;

window.location.replace(url.toString());
JannikStreek marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
4 changes: 4 additions & 0 deletions teammapper-frontend/src/app/root-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ToastGuard } from './guards/toast.guard';

const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./modules/about/about.module').then(m => m.AboutModule),
canActivate: [ToastGuard],
},
{
path: 'map',
loadChildren: () =>
import('./modules/application/application.module').then(
m => m.ApplicationModule
),
canActivate: [ToastGuard],
},
{
path: 'map/:id',
loadChildren: () =>
import('./modules/application/application.module').then(
m => m.ApplicationModule
),
canActivate: [ToastGuard],
},
{
path: 'app',
Expand Down
9 changes: 8 additions & 1 deletion teammapper-frontend/src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,14 @@
"JSON_FILE_SIZE_TOO_LARGE": "Die Datei ist größer als 1 MB und kann möglicherweise später nicht importiert werden. Bitte Datei verkleinern (z.B. Bilder entfernen)."
},
"TOASTS": {
"SUCCESSFULLY_DUPLICATED": "Mindmap erfolgreich dupliziert! Leite weiter..."
"SUCCESSFULLY_DUPLICATED": "Mindmap erfolgreich dupliziert!",
"URL_COPIED": "URL erfolgreich kopiert!",
"NODE_COPIED": "Knoten erfolgreich kopiert!",
"ERRORS": {
"ROOT_NODE_COPIED": "Es ist nicht möglich, den Hauptknoten zu kopieren!",
"GENERIC_NODE_COPY_ERROR": "Beim kopieren des Knoten ist ein Fehler aufgetreten."
JannikStreek marked this conversation as resolved.
Show resolved Hide resolved
},
"DELETE_MAP_SUCCESS": "Mindmap erfolgreich gelöscht!"
},
"GENERAL": {
"CREATE": "Mindmap erstellen",
Expand Down
9 changes: 8 additions & 1 deletion teammapper-frontend/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,14 @@
"JSON_FILE_SIZE_TOO_LARGE": "The file is exceeding 1 MB in file size and could cause problems during import. Please reduce the file size e.g. by removing images."
},
"TOASTS": {
"SUCCESSFULLY_DUPLICATED": "Mindmap successfully duplicated! Redirecting..."
"SUCCESSFULLY_DUPLICATED": "Mindmap successfully duplicated!",
"URL_COPIED": "URL successfully copied!",
"NODE_COPIED": "Node successfully copied!",
"ERRORS": {
"ROOT_NODE_COPIED": "It's not possible to copy the root node!",
"GENERIC_NODE_COPY_ERROR": "An error occurred whilst trying to copy the root node."
},
"DELETE_MAP_SUCCESS": "Mindmap successfully deleted!"
},
"GENERAL": {
"REPOSITORY": "Source Code",
Expand Down
9 changes: 8 additions & 1 deletion teammapper-frontend/src/assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,14 @@
"JSON_FILE_SIZE_TOO_LARGE": "The file is exceeding 1 MB in file size and could cause problems during import. Please reduce the file size e.g. by removing images."
},
"TOASTS": {
"SUCCESSFULLY_DUPLICATED": "¡Mapa mental duplicado con éxito! Redirigiendo..."
"SUCCESSFULLY_DUPLICATED": "¡Mapa mental duplicado con éxito!",
"URL_COPIED": "URL copiada correctamente",
"NODE_COPIED": "¡Nodo copiado con éxito!",
"ERRORS": {
"ROOT_NODE_COPIED": "No es posible copiar el nodo raíz.",
"GENERIC_NODE_COPY_ERROR": "Se ha producido un error al intentar copiar el nodo raíz."
},
"DELETE_MAP_SUCCESS": "¡Mapa mental borrado con éxito!"
},
"GENERAL": {
"REPOSITORY": "Source Code",
Expand Down
9 changes: 8 additions & 1 deletion teammapper-frontend/src/assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,14 @@
"JSON_FILE_SIZE_TOO_LARGE": "The file is exceeding 1 MB in file size and could cause problems during import. Please reduce the file size e.g. by removing images."
},
"TOASTS": {
"SUCCESSFULLY_DUPLICATED": "Mindmap dupliquée avec succès ! Redirection..."
"SUCCESSFULLY_DUPLICATED": "Mindmap dupliquée avec succès !",
"URL_COPIED": "URL copié avec succès",
"NODE_COPIED": "Nœud copié avec succès !",
"ERRORS": {
"ROOT_NODE_COPIED": "Il n'est pas possible de copier le noeud racine !",
"GENERIC_NODE_COPY_ERROR": "Une erreur s'est produite lors de la copie du nœud racine."
},
"DELETE_MAP_SUCCESS": "Mindmap supprimée avec succès !"
},
"GENERAL": {
"REPOSITORY": "Source Code",
Expand Down
9 changes: 8 additions & 1 deletion teammapper-frontend/src/assets/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,14 @@
"JSON_FILE_SIZE_TOO_LARGE": "The file is exceeding 1 MB in file size and could cause problems during import. Please reduce the file size e.g. by removing images."
},
"TOASTS": {
"SUCCESSFULLY_DUPLICATED": "Mappa mentale duplicata con successo! Reindirizzamento..."
"SUCCESSFULLY_DUPLICATED": "Mappa mentale duplicata con successo!",
"URL_COPIED": "URL copiato con successo",
"NODE_COPIED": "Nodo copiato con successo!",
"ERRORS": {
"ROOT_NODE_COPIED": "Non è possibile copiare il nodo radice!",
"GENERIC_NODE_COPY_ERROR": "Si è verificato un errore durante il tentativo di copiare il nodo radice."
},
"DELETE_MAP_SUCCESS": "Mappa mentale cancellata con successo!"
},
"GENERAL": {
"REPOSITORY": "Source Code",
Expand Down
9 changes: 8 additions & 1 deletion teammapper-frontend/src/assets/i18n/pt-br.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,14 @@
"JSON_FILE_SIZE_TOO_LARGE": "The file is exceeding 1 MB in file size and could cause problems during import. Please reduce the file size e.g. by removing images."
},
"TOASTS": {
"SUCCESSFULLY_DUPLICATED": "Mindmap duplicado com sucesso! Redirecionando..."
"SUCCESSFULLY_DUPLICATED": "Mindmap duplicado com sucesso!",
"URL_COPIED": "URL copiado com sucesso",
"NODE_COPIED": "Nó copiado com sucesso!",
"ERRORS": {
"ROOT_NODE_COPIED": "Não é possível copiar o nó raiz!",
"GENERIC_NODE_COPY_ERROR": "Ocorreu um erro ao tentar copiar o nó raiz."
},
"DELETE_MAP_SUCCESS": "Mapa mental excluído com sucesso!"
},
"GENERAL": {
"REPOSITORY": "Source Code",
Expand Down
9 changes: 8 additions & 1 deletion teammapper-frontend/src/assets/i18n/zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,14 @@
"JSON_FILE_SIZE_TOO_LARGE": "The file is exceeding 1 MB in file size and could cause problems during import. Please reduce the file size e.g. by removing images."
},
"TOASTS": {
"SUCCESSFULLY_DUPLICATED": "思维导图复制成功!重定向..."
"SUCCESSFULLY_DUPLICATED": "思维导图复制成功!",
"URL_COPIED": "成功复制 URL",
"NODE_COPIED": "节点复制成功!",
"ERRORS": {
"ROOT_NODE_COPIED": "无法复制根节点!",
"GENERIC_NODE_COPY_ERROR": "在尝试复制根节点时发生错误"
},
"DELETE_MAP_SUCCESS": "思维导图已成功删除!"
},
"GENERAL": {
"REPOSITORY": "Source Code",
Expand Down
9 changes: 8 additions & 1 deletion teammapper-frontend/src/assets/i18n/zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,14 @@
"JSON_FILE_SIZE_TOO_LARGE": "The file is exceeding 1 MB in file size and could cause problems during import. Please reduce the file size e.g. by removing images."
},
"TOASTS": {
"SUCCESSFULLY_DUPLICATED": "Mindmap successfully duplicated! Redirecting..."
"SUCCESSFULLY_DUPLICATED": "Mindmap successfully duplicated!",
"URL_COPIED": "URL successfully copied!",
"NODE_COPIED": "Node successfully copied!",
"ERRORS": {
"ROOT_NODE_COPIED": "It's not possible to copy the root node!",
"GENERIC_NODE_COPY_ERROR": "An error occurred whilst trying to copy the root node."
},
"DELETE_MAP_SUCCESS": "Mindmap successfully deleted!"
},
"GENERAL": {
"REPOSITORY": "Source Code",
Expand Down