Most of the application is already coded but I need one feature that I dont really understand. My app is basically a multimedia management app written in React JS and using an electron framework with...

Most of the application is already coded but I need one feature that I dont really understand. My app is basically a multimedia management app written in React JS and using an electron framework with an Nedb database. As of right now the app can handle uploads of the media, storage of the media based on time, location, title, and deivce taken from. The feature that I need implemented is one that is similar to google photos where the media is grouped into "events". For example, if the application notices that there are a lot of pictures taken around a specific location within a certain time period it will group the media into clusters called "events" it will then label this event with a name perhaps from a title from a media item? I'm not sure but this is the functionality that I need. I actually don't need it to be perfect I would just like sort of a setup on it so I can understand how to continue.


Multi management 1. Technology & libraries · Electron: combine Chromium and Node.js in to a single runtime for running cross-platform applications with Web Technology in Mac, Windows and Linux · React: javascript library for building user interfaces. · Ant Design: React UI framework for building UI easily. · NeDb: javascript library for managing app’s database. · Leaflet: javascript library for interactive maps 2. Basic architecture 3. Basic Folder structure · package.json: · containing common project information (name, description, author, version) · main (public/electron.js) – specify Application’s entry point · build: configure to build project to binary file, such as: .exe file for Windows · dependencies: all libraries for the application · devDependencies: all libraries for development · scripts: define some command for development and deployment, some important commands · yarn dev: start development enviroment · yarn package: deploy project to standalone application (check release/win-unpacked folder) · yarn.lock: auto-generated file when user runs command yarn to install all dependencies for the app and development. · src: containing all files for UI (entry point to start rendering UI - index.js) · public: containing Html file (define root element, then React will render UI to it), some files for electron (electron.js, fileDb.js, categoryDb.js, preload.js). · electron.js: Application’s entry point, all javascript files in public folder will be imported to this file. · index.html: contain root element for React to render UI to it · preload.js: expose ipcRenderer so React can interact with Electron from it. · fileDb.js: contain all functions for file’s database · categoryDb.js: contain all functions for category’s database 4. Detail 4.1. Application startup Application starts up from file public/electron.js Here, we create a function to create Window (createWindow()) for the application. Basically, this function is called when electron application is ready (app.whenReady().then). In createWindow(), we define window’s width, height and specify index.html file to be loaded for the UI. To be easier to understand, this window is just a Browser Window. So it loads a html file like a browser loads a website. Refer to public/electron.js for more information because I commented more detail in this file. 4.2. Explain some components with UI · App.js: root component for the application · AppSider.js: app’s sidebar which contains menu items · AppContent.js: contain searchbox, import button and main area where showing grid of files, grid of categories and map · ImportPanel.js: panel for importing files after user clicks on Import Button · FormUpdateInfo.js: component for update file info in ImportPanel · CustomGrid.js: define some components for AppContent and ImportPanel: · MainFileGrid: grid of files in AppContent when user selects File(Search, Photo, Video, Document) in Sidebar menu · CategoryGrid: grid of categories in AppContent when user selects Category(Calendar, Device, Location) · ImportFileGrid: grid of files in ImportPanel after user open native file dialog, then choose some files to import. · LocationMap.js: for showing map in AppContent when user selects Category(Map) · DocumentItem.js: defines some components to display some kind up documents · FilePdfOutlined: for PDF files · FileWordOutlined: for Word files · FileExcelOutlined: for Excel files · FilePptOutlined: for Powerpoint files · FileOutlined: for other files · Ant Design’s Image: for previewing/playing Image · Player: for previewing/playing video ·

tag: for normal image without previewing/playing feature · tag: for normal video without previewing/playing feature 4.3. Model for File   // prepare data for file info   const doc = {     timestamp,     year,     month,     title: fileInfo.title || "",     device: fileInfo.device || "",     country: fileInfo.country || "",     region: fileInfo.region || "", // state or provice      city: fileInfo.city || "",     countryCode: fileInfo.countryCode || "", // country’s isoCode     regionCode: fileInfo.regionCode || "", // region’s isoCode     lat: fileInfo.lat, // latitude     lon: fileInfo.lon, // longitude     type: fileInfo.type, // type of file with value: photo, video, document     path: fileInfo.path,     timeCreated: fileInfo.timeCreated || new Date().getTime(),     timeUpdated: new Date().getTime(),   }; 4.4. Model for Category 4.4.1. Model for Location Category   const doc = {     type: info.type, // type = "location"     sort1: info.country,     sort2: info.region,     sort3: info.city,     previewPath: info.previewPath, // path of file as thumbnail for each category     previewType: info.previewType, // photo, video, document     lat: info.lat, // latitude     lon: info.lon, // longitude     data: { // detail data for Location       country: info.country,       region: info.region,       city: info.city,     },   }; 4.4.2. Model for Calendar Category   const doc = {     type: info.type, // type = "calendar"     sort1: info.year,     sort2: info.month,     sort3: "",     previewPath: info.previewPath, // path of file as thumbnail for each category     previewType: info.previewType, // photo, video, document     data: { // detail data for Calendar       year: info.year,       month: info.month,     },   }; 4.4.3. Model for Device Category   const doc = {     type: info.type, // type = "device"     sort1: info.device,     sort2: "",     sort3: "",     previewPath: info.previewPath, // path of file as thumbnail for each category     previewType: info.previewType, // photo, video, document     data: { // detail data for Device       device: info.device,     },   }; 5. Concept for importing file and getting file info Before openning a dialog to choose files, use should select one of types: photo, video, document. Then when user clicks Open: ImportPanel.js   //handle import clicked -> send event to electron to open file dialog   const onImportClicked = async () => {     try {       const result = await window.electron.ipcRenderer.invoke(         "open-file-dialog",         contentType       ); //...     } catch (error) {       console.log("result of open file dialog error:", error);     }   }; It invokes “open-file-dialog” with param as contentType. Then in electron, init file filters by extension for each contentType to choose file: Electron.js // handle open file dialog with event from renderer process (React) ipcMain.handle("open-file-dialog", async (_, contentType) => {   console.log("handle open-file-dialog with content type:", contentType);   // define file filters when opening file dialog with each content type of photo, video, document   const fileFilters = {     photo: ["Images", ["jpg", "png", "gif", "tiff", "svg", "webp"]],     video: ["Movies", ["mp4", "mkv", "avi", "mov", "ogg", "wmv", "webm"]],     document: ["Documents", ["pdf", "docx", "xlsx", "pptx", "rtf"]],   };   // show dialog to open file with multiple selections available and some filters   const result = await dialog.showOpenDialog(mainWindow, {     properties: ["openFile", "multiSelections"],     filters: [       {         name: fileFilters[contentType][0],         extensions: fileFilters[contentType][1],       },       { name: "All Files", extensions: ["*"] },     ],   });   return result; }); After user choose files, then back to ImportPanel.js to setFiles.   //handle import clicked -> send event to electron to open file dialog   const onImportClicked = async () => {     try {       const result = await window.electron.ipcRenderer.invoke(         "open-file-dialog",         contentType       );       console.log("result of open file dialog success:", result);       if (!result.canceled) {         setFiles({ ...files, [contentType]: result.filePaths });       }     } catch (error) {       console.log("result of open file dialog error:", error);     }   }; After this step, we have list of files with chosen content type. In the left side, we have a form to edit file info. If a file exist in DB, then we get the file info from DB to fill file’s data to the form. Otherwise, user needs to fill manually (except for date, default value is Now). Note: · We just use file extention to detect file type. · To get file’s latitude and longitude: when user select’s country, region (state/province), city, we can find its latitude and longitude by 2 methods: · Predefined in lib: country-state-city (https://www.npmjs.com/package/country-state-city) · Or by search online with data from OpenStreetMapProvider Below is code for it. If if the first method does not work, we will do the second methods. Notice that, the second method requires internet connection. FormUpdateInfo.js     // find lat/lon by internet if these values not exist     if (       (!values.lat || !values.lon) &&       (values.country || values.region || values.city)     ) {       const address = normalizeTextInput(         values.country + " " + values.region + " " + values.city       );       try {         const results = await osmProvider.search({ query: address });         if (results && results.length > 0) {           console.log("geocoding search result:", results);           values.lon = results[0].x;           values.lat = results[0].y;         } else {           console.log("geocoding empty result,", address);         }       } catch (error) {         console.log("geocoding error,", address);       }     } · After preparing all data for file, we just call a method to insert or update it to DB (after user clicks on Import/Update button) try {       const result = await window.electron.ipcRenderer.invoke(         "db-import-file",         JSON.stringify(values)       );       console.log("result of db-import-file success:", result);       openNotificationSuccess(         "Import",         "Import file with information successfully"       );       onSuccess(file);     } catch (error) {       console.log("result of db-import-file error:", error);       openNotificationError("Import", "Import file with information error");     } Electron.js // handle to insert or update a file info ipcMain.handle("db-import-file", async (_, fileInfo) => { In this function, · Check if the file is already exsit, get the old file info to update categories later   // handle update case when the file is already exsit   // -> need to check to delete or update category   let oldFile = null;   if (fileInfoObj.id) {     try {       const ret = await fileDb.getByPath(fileInfoObj.path);       if (ret && ret.length > 0) {         oldFile = ret[0];         console.log({ oldFile });       }     } catch (error) {       console.log("error when getByPath error", error);     }   } · Extract file’s country/region/city to insert/update Location Category to categoryDb.   // update or insert to location category   if (fileInfoObj.country || fileInfoObj.region) {     try {       await categoryDb.upsert({         type: "location",         country: fileInfoObj.country,         region: fileInfoObj.region,         city: fileInfoObj.city,         previewPath: fileInfoObj.path,         previewType: fileInfoObj.type,         lat: fileInfoObj.lat,         lon: fileInfoObj.lon,       });     } catch (error) {       console.log("error when upsert location category error:", error);     }   } · Extract file’s year/month to insert/update Calendar Category to categoryDb.   // update or insert to calendar category   const d = new Date(fileInfoObj.timestamp);   const year = d.getFullYear();   const month = d.getMonth() + 1;   if (year != 1970 && month != 1) {     try {       await categoryDb.upsert({         type: "calendar",         year,         month,         previewPath: fileInfoObj.path,         previewType: fileInfoObj.type,       });     } catch (error) {       console.log("error when upsert calendar category error:", error);     }   } · Extract file’s device to insert/update Device Category to categoryDb. // update or insert to device category   if (fileInfoObj.device) {     try {       await categoryDb.upsert({         type: "device",         device: fileInfoObj.device,         previewPath: fileInfoObj.path,         previewType: fileInfoObj.type,       });     } catch (error) {       console.log("error when upsert device category error:", error);     }   } · Update/insert file to fileDb // handle upsert with new fileInfo   let result;   let hasError = false;   try {     result = await fileDb.upsert({ ...fileInfoObj, year, month });   } catch (error) {     result = error;     hasError = true;     console.log("error when trying to remove file:", fileObj);   } · Check to delete category with no files (because in case of update file, when user updates date, location or device name. The old categories may have no files after that -> should be deleted).   // this is in case of updating file   if (!hasError && oldFile) {     await checkToDeleteCategory(oldFile);   } 6. Concept for getting files When user selects one of (search, photo, video, document), we will get all files with each menu item. AppContent.js   // handle app mode changes   useEffect(() => {     if (isFileType(mode)) {       setFiles([]);       setCategories([]);       setInputTextSearch("");       setSearchValue("");       findFilesByType(getContentType(mode), 0);       return;     }     if (isCatetoryType(mode)) {       setFiles([]);       setCategories([]);       setInputTextSearch("");       setSearchValue("");       findCategoriesByType(getCategoryType(mode), 0);       return;     }     if (isSearchType(mode)) {       setFiles([]);       setCategories([]);       handleOnSearch(0);       return;     }   }, [     findFilesByType,     findCategoriesByType,     handleOnSearch,     mode,     forceUpdate, // place it here to be able to force update state whenever it's changed   ]); When mode change, this function will be called again to update data. Here we have some functions: · findFilesByType: to get all files with each type (photo, video, document) · findCategoriesByType: to get all categories with each category type (location, calendar, location) · handleOnSearch: to search files by a search text 6.1. Find files by type AppContent.js // find files by type (photo, video, document)   const findFilesByType = useCallback(async (contentType, offset) => {     setHasMore(false);     // prepare option to find files by type     const opt = {       type: contentType,       offset: offset,       limit: GET_LIMIT,     };     try {       const result = await window.electron.ipcRenderer.invoke(         "db-find-files-by-type",         JSON.stringify(opt)       );       console.log("result of db-find-files-by-type success:", result);       setFiles((files) => [...files, ...result]);       setHasMore(result.length === GET_LIMIT);     } catch (error) {       console.log("result of db-find-files-by-type error:", error);     }   }, []); First, we prepare option to find files: · type: contentType to find (photo, video, document) · offset: index of starting file to find · limit: limit number of files in each query After invoke “db-find-files-by-type”, then in Electron.js: // handle to find files by content type ipcMain.handle("db-find-files-by-type", async (_, findInfo) => {   return fileDb.findByType(JSON.parse(findInfo)); }); After getting done, back to AppContent.js to setFiles and setHasMore. console.log("result of db-find-files-by-type success:", result); setFiles((files) => [...files, ...result]); setHasMore(result.length === GET_LIMIT); Note: · When setting limit to query as GET_LIMIT(100), then we get number of files is equal to it. · That means user needs to click LoadMore button to query more. In this case, we show grid of files in main area. When user clicks on each item, then show preview detail for it. 6.2. Find categories by type AppContent.js // find categories (location, calendar, device)   const findCategoriesByType = useCallback(async (categoryType, offset) => {     setHasMore(false);     // prepare option to find     const opt = {       type: categoryType,       offset: offset,       limit: GET_LIMIT,     };     try {       const result = await window.electron.ipcRenderer.invoke(         "db-find-categories-by-type",         JSON.stringify(opt)       );       console.log("result of db-find-categories-by-type success:", result);       setCategories((categories) => [...categories, ...result]);       setHasMore(result.length === GET_LIMIT);     } catch (error) {       console.log("result of db-find-categories-by-type error:", error);     }   }, []); First, we prepare option to find files: · type: categoryType to find (calendar, device, location) (in section Map, just query categories by location) · offset: index of starting category to find · limit: limit number of categories in each query After invoke “db-find-categories-by-type”, then in Electron.js: // handle to find categories with category type ipcMain.handle("db-find-categories-by-type", async (_, findInfo) => {   return categoryDb.findByCategory(JSON.parse(findInfo)); });
May 05, 2021
SOLUTION.PDF

Get Answer To This Question

Submit New Assignment

Copy and Paste Your Assignment Here