Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions apps/web-mzima-client/src/app/core/helpers/media-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { SafeUrl } from '@angular/platform-browser';
import { ɵunwrapSafeValue as unwrapSafeValue } from '@angular/core';
import { MediaFile } from '../interfaces/media';

export function getDocumentThumbnail(mediaFile: MediaFile) {
const path = '/assets/images/logos/';
let thumbnail = 'unknown_document.png';
switch (mediaFile.mimeType) {
case 'application/pdf':
thumbnail = 'pdf_document.png';
break;
case 'application/msword':
thumbnail = 'word_document.png';
break;
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
thumbnail = 'word_document.png';
break;
}
return path + thumbnail;
}

export function getFileSize(mediaFile: MediaFile): string {
let filesize = 0;
if (mediaFile.status === 'ready') filesize = mediaFile.size!;
else filesize = mediaFile.file ? mediaFile.file.size : 0;

// Megabytes
if (filesize > 1000000) {
return (filesize / 1000000).toFixed(2).toString() + 'MB';
}
// Kilobytes
else if (filesize > 1000) {
return (filesize / 1000).toFixed(2).toString() + 'kB';
}
// Bytes
else {
return filesize + 'bytes';
}
}

// Our media api returns a relative url with a filename that has an id prepended, instead of the original filename.
// This function attempts to take that url, and return the original filename.
export function getFileNameFromUrl(url: string | SafeUrl): string {
const urlToCheck = unwrapSafeValue(url);

// Try to use a regex to strip out what we add to the filename and the path
const regex = /.*\/[0-9a-fA-F]{13}-(.*)/;
const match = urlToCheck.match(regex);
if (match && match[1]) return match[1];

// The url doesnt have the expected filename, so return everything after the final slash
const lastSlashIndex = urlToCheck.lastIndexOf('/');
const newFilename = lastSlashIndex !== -1 ? urlToCheck.substring(lastSlashIndex + 1) : urlToCheck;
const firstHyphenIndex = newFilename.indexOf('-') + 1;
return newFilename.substring(firstHyphenIndex);
}
23 changes: 22 additions & 1 deletion apps/web-mzima-client/src/app/core/helpers/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,29 @@ export const surveyFields = [
{
label: 'survey.upload_image',
type: 'media',
input: 'upload',
input: 'image',
instructions: 'survey.upload_desc',
config: {
hasCaption: true,
maxUploadSize: 2,
},
},
{
label: 'survey.upload_audio',
type: 'media',
input: 'audio',
instructions: 'survey.audio_desc',
config: {
maxUploadSize: 2,
},
},
{
label: 'survey.upload_document',
type: 'media',
input: 'document',
instructions: 'survey.document_desc',
config: {
maxUploadSize: 2,
},
},
{
Expand All @@ -116,6 +135,8 @@ export const surveyFields = [
},
];

export const maxUploadSizes = [2, 5, 10];

export const fieldHasTranslations = (field: any, lang: any) => {
if (!field.translations) return false;
return field.translations[lang]?.options
Expand Down
67 changes: 67 additions & 0 deletions apps/web-mzima-client/src/app/core/interfaces/media.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { SafeUrl } from '@angular/platform-browser';

enum ErrorEnum {
NONE = 'none',
MAX_SIZE = 'post.media.messages.max_size',
REQUIRED = 'post.media.messages.required',
MAX_FILES = 'post.media.messages.max_files',
}

type MediaType = {
icon: string;
buttonText: string;
fileTypes: string;
};

type MediaFileStatus =
| 'ready'
| 'upload'
| 'uploading'
| 'uploaded'
| 'error'
| 'too_big'
| 'delete';

const mediaTypes = new Map<string, MediaType>([
[
'image',
{
icon: 'add_a_photo',
buttonText: 'post.media.add_photo',
fileTypes: 'image/jpeg, image/png',
},
],
[
'audio',
{
icon: 'speaker',
buttonText: 'post.media.add_audio',
fileTypes: 'audio/mp3, audio/ogg, audio/aac',
},
],
[
'document',
{
icon: 'note_add',
buttonText: 'post.media.add_document',
fileTypes:
'application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document',
},
],
]);

type MediaFile = {
id?: number;
generatedId: number;
file?: File;
filename: string;
fileExtension?: string;
url: string | SafeUrl | null;
caption?: string;
status: MediaFileStatus;
size?: number;
mimeType?: string;
value?: number;
};

export { MediaFile, MediaFileStatus, MediaType, ErrorEnum, mediaTypes };
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<div class="media-loader">
<div class="media-loader__btn">
<div class="media-loader__preview" *ngIf="mediaFiles.length > 0">
<div class="media-loader__preview-{{ media }}">
<div
[class]="'media-preview ' + media + ' ' + mediaFile.status"
*ngFor="let mediaFile of mediaFiles; let i = index"
>
<ng-container
*ngIf="
mediaFile.status === 'ready' || mediaFile.status === 'uploaded';
then itemReady;
else inProgress
"
>
</ng-container>

<ng-template #itemReady>
<img
*ngIf="media === 'document'"
class="thumbnail"
[src]="getDocumentThumbnail(mediaFile)"
/>
<img *ngIf="media === 'image'" class="thumbnail" [src]="mediaFile.url" />
<audio *ngIf="media === 'audio'" controls [src]="mediaFile.url"></audio>
<div class="media-details">
<div class="filename">{{ mediaFile.filename }}</div>
<div class="filesize">{{ getFileSize(mediaFile) }}</div>
</div>
</ng-template>

<ng-template #inProgress>
<div class="media-blank">
<mat-icon
*ngIf="mediaFile.status === 'error' || mediaFile.status === 'too_big'"
class="xicon"
icon
svgIcon="warning"
></mat-icon>
<div class="media-error" *ngIf="mediaFile.status === 'too_big'">
{{ ErrorEnum.MAX_SIZE | translate }}
</div>
<app-spinner
class="loading-spinner"
*ngIf="mediaFile.status === 'uploading'"
></app-spinner>
<div class="media-status" *ngIf="mediaFile.status === 'uploading'">Uploading...</div>
</div>
</ng-template>

<div
class="status-button"
(click)="clickDeleteButton(mediaFile.value, mediaFile.generatedId)"
>
<ng-container *ngIf="mediaFile.status === 'uploaded'; then check; else cross">
</ng-container>
<ng-template #check>
<mat-icon class="xicon" fontIcon="check"></mat-icon>
</ng-template>
<ng-template #cross>
<mat-icon class="xicon" fontIcon="close"></mat-icon>
</ng-template>
</div>
</div>
</div>
</div>
<div class="media-loader__controls">
<mzima-client-button
fill="outline"
color="secondary"
[disabled]="maxFiles !== -1 && this.mediaFiles.length < maxFiles"
class="media-loader__control"
(buttonClick)="fileInput.click()"
[data-qa]="'btn-post-item-upload'"
[ngSwitch]="mediaType"
>
<mat-icon iconPrefix>{{ mediaType.icon }}</mat-icon>
{{ mediaType.buttonText | translate }}
</mzima-client-button>
<input
hidden
#fileInput
multiple
type="file"
accept="{{ mediaType.fileTypes }}"
(change)="onFileSelected($event)"
/>
</div>

<mat-error class="error-msg" *ngIf="error !== 'none'">
{{ error | translate }}
</mat-error>
</div>
</div>
Loading