update file upload & download
This commit is contained in:
parent
1a3f4b8989
commit
cc9377290f
12 changed files with 97 additions and 176 deletions
|
|
@ -13,7 +13,7 @@ import { UserService } from '../../../service/user.service';
|
|||
import { SingleChatComponent } from '../single-chat/single-chat.component';
|
||||
import { ChatMsgBoxComponent } from '../chat-msg-box/chat-msg-box.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DownloadFilesService } from '../../../service/download-files.service';
|
||||
import { DownloadFilesService } from '../../../service/files.service';
|
||||
import { ChannelService } from '../../../service/channel.service';
|
||||
import { InfoComponent } from '../info/info.component';
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ import { InfoComponent } from '../info/info.component';
|
|||
templateUrl: './chat-content.component.html',
|
||||
styleUrl: './chat-content.component.scss',
|
||||
})
|
||||
export class ChatContentComponent implements AfterViewInit, AfterViewChecked {
|
||||
export class ChatContentComponent implements AfterViewInit {
|
||||
@Input() currentChannel: string = '';
|
||||
@Input() isPrivatChannel: boolean = false;
|
||||
@Input() isSearchChannel: boolean = false;
|
||||
|
|
@ -46,20 +46,11 @@ export class ChatContentComponent implements AfterViewInit, AfterViewChecked {
|
|||
private chatService: ChatService,
|
||||
private userService: UserService,
|
||||
public channelService: ChannelService,
|
||||
private downloadFilesService: DownloadFilesService,
|
||||
private renderer: Renderer2
|
||||
) {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.scrollToBottom();
|
||||
this.checkIfLoadedFirebaseFiles();
|
||||
}
|
||||
|
||||
ngAfterViewChecked() {
|
||||
if (this.filesLoaded) {
|
||||
this.scrollToBottom();
|
||||
this.filesLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -73,17 +64,6 @@ export class ChatContentComponent implements AfterViewInit, AfterViewChecked {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether Firebase files have been loaded from the server
|
||||
*/
|
||||
checkIfLoadedFirebaseFiles() {
|
||||
this.downloadFilesService.downloadedFiles.subscribe((files) => {
|
||||
if (files.length > 0) {
|
||||
this.filesLoaded = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls to the bottom of the message body element.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@
|
|||
(click)="sendMessage()"
|
||||
/>
|
||||
}
|
||||
<!--- show chnnels or users by pressing @ or #-->
|
||||
<!--- show channels or users by pressing @ or #-->
|
||||
@if (openSmallWindow) {
|
||||
<div class="filteredElementWindow2">
|
||||
@if (showChannels) { @for (i of checkIfUserHasAccessToChannel(); track
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
import { FormsModule } from '@angular/forms';
|
||||
import { PickerComponent } from '@ctrl/ngx-emoji-mart';
|
||||
import { Firestore, addDoc, collection } from '@angular/fire/firestore';
|
||||
import { DownloadFilesService } from '../../../service/download-files.service';
|
||||
import { DownloadFilesService } from '../../../service/files.service';
|
||||
import { UserService } from '../../../service/user.service';
|
||||
import { EmojiPickerComponent } from '../../../shared/components/emoji-picker/emoji-picker.component';
|
||||
import { SmallBtnComponent } from '../../../shared/components/small-btn/small-btn.component';
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
@if(imageUrl) {
|
||||
<div class="files">
|
||||
<div class="filesView">
|
||||
@if (['png', 'jpg', 'jpeg', 'gif'].includes(getFileType(imageUrl))) {
|
||||
@if (['png', 'jpg', 'jpeg', 'gif'].includes(getFileType(filePath))) {
|
||||
<img
|
||||
src="{{ imageUrl }}"
|
||||
src="{{ filePath }}"
|
||||
alt="image"
|
||||
[ngStyle]="{
|
||||
height:
|
||||
|
|
@ -11,13 +10,13 @@
|
|||
? '5vw'
|
||||
: '7.5vw'
|
||||
}"
|
||||
(click)="showOverlay(imageUrl)"
|
||||
(click)="showOverlay(filePath)"
|
||||
/>
|
||||
} @if (['pdf', 'doc', 'txt'].includes(getFileType(imageUrl))) {
|
||||
<a href="{{ imageUrl }}" target="_blank">
|
||||
} @if (['pdf', 'doc', 'txt'].includes(getFileType(filePath))) {
|
||||
<a href="{{ filePath }}" target="_blank">
|
||||
<img
|
||||
class="otherFiles"
|
||||
src="./assets/img/attachments/{{ getFileType(imageUrl) }}.png"
|
||||
src="./assets/img/attachments/{{ getFileType(filePath) }}.png"
|
||||
alt="image"
|
||||
[ngStyle]="{
|
||||
height:
|
||||
|
|
@ -26,11 +25,11 @@
|
|||
: '5vw'
|
||||
}"
|
||||
/></a>
|
||||
} @if (['mp3', 'wav', 'wma'].includes(getFileType(imageUrl))) {
|
||||
} @if (['mp3', 'wav', 'wma'].includes(getFileType(filePath))) {
|
||||
<audio
|
||||
controls
|
||||
preload="auto"
|
||||
src="{{ imageUrl }}"
|
||||
src="{{ filePath }}"
|
||||
[ngStyle]="{
|
||||
width:
|
||||
openOnSecondaryChat && viewWidth >= RESPONSIVE_THRESHOLD
|
||||
|
|
@ -38,10 +37,10 @@
|
|||
: ''
|
||||
}"
|
||||
></audio>
|
||||
}@if (getFileType(imageUrl) === 'mp4') {
|
||||
<div (click)="showOverlay(imageUrl)">
|
||||
}@if (getFileType(filePath) === 'mp4') {
|
||||
<div (click)="showOverlay(filePath)">
|
||||
<video
|
||||
src="{{ imageUrl }}"
|
||||
src="{{ filePath }}"
|
||||
alt="video"
|
||||
[ngStyle]="{
|
||||
height:
|
||||
|
|
@ -54,4 +53,3 @@
|
|||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { CommonModule } from '@angular/common';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { DownloadFilesService } from '../../../../service/download-files.service';
|
||||
import { DownloadFilesService } from '../../../../service/files.service';
|
||||
import { OverlayService } from '../../../../service/overlay.service';
|
||||
import { OverlayComponent } from '../../../../shared/components/overlay/overlay.component';
|
||||
import { SharedService } from '../../../../service/shared.service';
|
||||
|
|
@ -18,7 +18,6 @@ export class AttachmentsComponent {
|
|||
@Input() openOnSecondaryChat: boolean = false;
|
||||
@Input() viewWidth: number = 0;
|
||||
loadingUrl: string = './../../../assets/img/loading.svg';
|
||||
imageUrl: string | null = '';
|
||||
|
||||
constructor(
|
||||
public downloadFilesService: DownloadFilesService,
|
||||
|
|
@ -28,14 +27,6 @@ export class AttachmentsComponent {
|
|||
|
||||
RESPONSIVE_THRESHOLD = this.sharedService.RESPONSIVE_THRESHOLD;
|
||||
|
||||
async ngOnInit() {
|
||||
if (this.filePath) {
|
||||
this.imageUrl = await this.downloadFilesService.downloadFiles(
|
||||
this.filePath
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Type of files:
|
||||
// Img: PNG, GIF, JPG, JPEG
|
||||
// Files: PDF, DOC, TXT
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { FormsModule, NgForm } from '@angular/forms';
|
|||
import { ChatService } from '../../../../service/chat.service';
|
||||
import { PickerComponent } from '@ctrl/ngx-emoji-mart';
|
||||
import { SmallBtnComponent } from '../../../../shared/components/small-btn/small-btn.component';
|
||||
import { DownloadFilesService } from '../../../../service/download-files.service';
|
||||
import { DownloadFilesService } from '../../../../service/files.service';
|
||||
import { EmojiPickerComponent } from '../../../../shared/components/emoji-picker/emoji-picker.component';
|
||||
import { UserService } from '../../../../service/user.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
|
|
|||
|
|
@ -89,12 +89,12 @@
|
|||
<span>{{ "single-chat.edit" | translate }}</span>
|
||||
}
|
||||
</div>
|
||||
@if(filePath) {
|
||||
@if(chat.attachments) {
|
||||
<app-attachments
|
||||
[chatId]="chat.id"
|
||||
[openOnSecondaryChat]="openOnSecondaryChat"
|
||||
[viewWidth]="viewWidth"
|
||||
[filePath]="filePath"
|
||||
[filePath]="chat.attachments"
|
||||
></app-attachments>
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Chat, ChatAnswers } from '../../../interface/chat.interface';
|
|||
import { ChatContentComponent } from '../chat-content/chat-content.component';
|
||||
import { CommonModule, NgSwitchCase } from '@angular/common';
|
||||
import { ChatService } from '../../../service/chat.service';
|
||||
import { DownloadFilesService } from '../../../service/download-files.service';
|
||||
import { DownloadFilesService } from '../../../service/files.service';
|
||||
import { NgxExtendedPdfViewerModule } from 'ngx-extended-pdf-viewer';
|
||||
import { OptionsMenuComponent } from './options-menu/options-menu.component';
|
||||
import { AttachmentsComponent } from './attachments/attachments.component';
|
||||
|
|
@ -63,16 +63,6 @@ export class SingleChatComponent {
|
|||
public downloadFilesService: DownloadFilesService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.checkChatHasFiles();
|
||||
}
|
||||
|
||||
async checkChatHasFiles() {
|
||||
this.filePath = await this.downloadFilesService.checkChatHasFiles(
|
||||
this.chat.id
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal to edit a message.
|
||||
* @param {boolean} variable - The value indicating whether the message edit form should be open or not.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
export interface Chat {
|
||||
id: string;
|
||||
userId: string;
|
||||
chatId: string;
|
||||
channelId: string;
|
||||
message: string;
|
||||
edited: boolean;
|
||||
publishedTimestamp: number;
|
||||
attachments: string;
|
||||
}
|
||||
|
||||
export interface ChatAnswers {
|
||||
|
|
@ -14,6 +16,7 @@ export interface ChatAnswers {
|
|||
edited: boolean;
|
||||
publishedTimestamp: number;
|
||||
userId: string;
|
||||
attachments: string;
|
||||
}
|
||||
|
||||
export interface ChatReactions {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,12 @@ import {
|
|||
updateDoc,
|
||||
} from '@angular/fire/firestore';
|
||||
import { Chat, ChatAnswers, ChatReactions } from '../interface/chat.interface';
|
||||
import {
|
||||
getDownloadURL,
|
||||
getStorage,
|
||||
ref,
|
||||
uploadBytes,
|
||||
} from '@angular/fire/storage';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
|
@ -26,6 +32,7 @@ export class ChatService implements OnDestroy {
|
|||
getChannelId: string = '';
|
||||
getPrvChatId: string = '';
|
||||
inputValue: string = '';
|
||||
fileSrc: string = '';
|
||||
|
||||
unsubChat;
|
||||
unsubChatAnswers;
|
||||
|
|
@ -101,6 +108,14 @@ export class ChatService implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
async updateChatFile(chatId: string, chatFile: string) {
|
||||
await updateDoc(doc(collection(this.firestore, 'chats'), chatId), {
|
||||
attachments: chatFile,
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the reaction document with the specified ID with the provided array of user IDs.
|
||||
* @param reactionId The ID of the reaction document to update.
|
||||
|
|
@ -171,6 +186,24 @@ export class ChatService implements OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
uploadFile(file: File) {
|
||||
const storage = getStorage();
|
||||
const storageRef = ref(storage, 'chatFiles/' + file.name);
|
||||
uploadBytes(storageRef, file)
|
||||
.then((snapshot) => {
|
||||
getDownloadURL(storageRef)
|
||||
.then((url) => {
|
||||
this.fileSrc = url;
|
||||
})
|
||||
.catch((error) =>
|
||||
console.error('Error retrieving the download URL:', error)
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Upload error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from all Firestore subscriptions when the service is destroyed.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,115 +0,0 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
getDownloadURL,
|
||||
getStorage,
|
||||
listAll,
|
||||
ref,
|
||||
uploadBytes,
|
||||
} from '@angular/fire/storage';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DownloadFilesService {
|
||||
uploadFiles: File[] = [];
|
||||
downloadedFiles: BehaviorSubject<{ id: string; files: string[] }[]> =
|
||||
new BehaviorSubject<{ id: string; files: string[] }[]>([]);
|
||||
|
||||
fileNames: any[] = [];
|
||||
|
||||
constructor() {
|
||||
this.listAllFiles();
|
||||
}
|
||||
|
||||
async checkChatHasFiles(chatId: string): Promise<string | undefined> {
|
||||
try {
|
||||
const storage = getStorage();
|
||||
const listRef = ref(storage, `chatFiles/${chatId}`);
|
||||
|
||||
const result = await listAll(listRef);
|
||||
if (result.items.length > 0) {
|
||||
return result.items[0].fullPath;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error listing files:', error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads all files and updates the list.
|
||||
* @param {string} docID - The documentId under which the files are to be saved.
|
||||
*/
|
||||
uploadAllFiles(docID: string) {
|
||||
const storage = getStorage();
|
||||
for (const file of this.uploadFiles) {
|
||||
const storageRef = ref(storage, `chatFiles/${docID}/${file.name}`);
|
||||
uploadBytes(storageRef, file).then((snapshot) => {
|
||||
this.listAllFiles();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all files in 'chatFiles' and updates the downloaded files.
|
||||
*/
|
||||
listAllFiles() {
|
||||
const storage = getStorage();
|
||||
const listRef2 = ref(storage, 'chatFiles');
|
||||
|
||||
listAll(listRef2)
|
||||
.then(async (res) => {
|
||||
const downloadedFilesData: { id: string; files: string[] }[] = [];
|
||||
|
||||
for (const folderRef of res.prefixes) {
|
||||
const folderID = folderRef.name;
|
||||
const folderFiles = [];
|
||||
|
||||
const folderRes = await listAll(folderRef);
|
||||
|
||||
for (const fileRef of folderRes.items) {
|
||||
const url = await getDownloadURL(fileRef);
|
||||
folderFiles.push(url);
|
||||
}
|
||||
|
||||
downloadedFilesData.push({ id: folderID, files: folderFiles });
|
||||
}
|
||||
// Emit the updated value
|
||||
this.downloadedFiles.next(downloadedFilesData);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error when retrieving the files:', error);
|
||||
});
|
||||
}
|
||||
|
||||
checkFiles(chatId: string) {
|
||||
const storage = getStorage();
|
||||
const listRef = ref(storage, `chatFiles/${chatId}`);
|
||||
return listAll(listRef)
|
||||
.then((result) => {
|
||||
if (result.items.length > 0) {
|
||||
return result.items[0].fullPath;
|
||||
}
|
||||
return;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error listing files:', error);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
async downloadFiles(path: string): Promise<string | null> {
|
||||
try {
|
||||
const storage = getStorage();
|
||||
const forestRef = ref(storage, path);
|
||||
const downloadUrl = await getDownloadURL(forestRef);
|
||||
return downloadUrl;
|
||||
} catch (error) {
|
||||
console.error('Error downloading file:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/app/service/files.service.ts
Normal file
41
src/app/service/files.service.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
getDownloadURL,
|
||||
getStorage,
|
||||
ref,
|
||||
uploadBytes,
|
||||
} from '@angular/fire/storage';
|
||||
import { ChatService } from './chat.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DownloadFilesService {
|
||||
uploadFiles: File[] = [];
|
||||
|
||||
constructor(private chatService: ChatService) {}
|
||||
|
||||
/**
|
||||
* Uploads all files and updates the list.
|
||||
* @param {string} docID - The documentId under which the files are to be saved.
|
||||
*/
|
||||
uploadAllFiles(docID: string) {
|
||||
const storage = getStorage();
|
||||
for (const file of this.uploadFiles) {
|
||||
const storageRef = ref(storage, `chatFiles/${docID}/${file.name}`);
|
||||
uploadBytes(storageRef, file)
|
||||
.then((snapshot) => {
|
||||
getDownloadURL(storageRef)
|
||||
.then((url) => {
|
||||
this.chatService.updateChatFile(docID, url);
|
||||
})
|
||||
.catch((error) =>
|
||||
console.error('Error retrieving the download URL:', error)
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Upload error:', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue