diff --git a/src/app/components.d.ts b/src/app/components.d.ts index 8180a9be445470f5b6e46c3ab1dfd2e49cc35899..053d35139dc98b727a9a7a7354c0732cd08813c3 100644 --- a/src/app/components.d.ts +++ b/src/app/components.d.ts @@ -10,6 +10,7 @@ declare module 'vue' { App: typeof import('./App.vue')['default'] Avatar: typeof import('primevue/avatar')['default'] BaseDivider: typeof import('./../shared/BaseDivider.vue')['default'] + BaseLoader: typeof import('./../shared/BaseLoader.vue')['default'] BaseSidebarMenu: typeof import('./../modules/BaseSidebarMenu.vue')['default'] Button: typeof import('primevue/button')['default'] CreateEntityMenu: typeof import('./../components/CreateEntityMenu.vue')['default'] @@ -17,23 +18,28 @@ declare module 'vue' { Dialog: typeof import('primevue/dialog')['default'] Divider: typeof import('primevue/divider')['default'] Drawer: typeof import('primevue/drawer')['default'] + EntitiesList: typeof import('./../modules/EntitiesList.vue')['default'] EntityItem: typeof import('./../modules/EntityItem.vue')['default'] + EntityTitle: typeof import('./../components/entities/share/EntityTitle.vue')['default'] HomePage: typeof import('./../pages/HomePage.vue')['default'] ImageItem: typeof import('./../modules/entities/ImageItem.vue')['default'] - ImagePositionMenu: typeof import('./../components/editEntityMenu/ImagePositionMenu.vue')['default'] - ImageSizeMenu: typeof import('./../components/editEntityMenu/ImageSizeMenu.vue')['default'] - ImageStateMenu: typeof import('./../components/editEntityMenu/ImageStateMenu.vue')['default'] + ImageMenu: typeof import('./../modules/entities/menu/ImageMenu.vue')['default'] + ImagePositionMenu: typeof import('./../components/entities/image/ImagePositionMenu.vue')['default'] + ImageSizeMenu: typeof import('./../components/entities/image/ImageSizeMenu.vue')['default'] + ImageStateMenu: typeof import('./../components/entities/image/ImageStateMenu.vue')['default'] LogoAndLabel: typeof import('./../components/LogoAndLabel.vue')['default'] + PageBackgroundMenu: typeof import('./../modules/PageBackgroundMenu.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SheetPage: typeof import('./../pages/[uuid]/SheetPage.vue')['default'] SpeedDial: typeof import('primevue/speeddial')['default'] Splitter: typeof import('primevue/splitter')['default'] SplitterPanel: typeof import('primevue/splitterpanel')['default'] - TextFontMenu: typeof import('./../components/editEntityMenu/TextFontMenu.vue')['default'] + TextFontMenu: typeof import('./../components/entities/share/TextFontMenu.vue')['default'] TextItem: typeof import('./../modules/entities/TextItem.vue')['default'] - TextPositionMenu: typeof import('./../components/editEntityMenu/TextPositionMenu.vue')['default'] - TextStateMenu: typeof import('./../components/editEntityMenu/TextStateMenu.vue')['default'] + TextMenu: typeof import('./../modules/entities/menu/TextMenu.vue')['default'] + TextPositionMenu: typeof import('./../components/entities/text/TextPositionMenu.vue')['default'] + TextStateMenu: typeof import('./../components/entities/text/TextStateMenu.vue')['default'] Tree: typeof import('primevue/tree')['default'] UserInfoHeaderWithSettings: typeof import('./../components/UserInfoHeaderWithSettings.vue')['default'] } diff --git a/src/app/helpers/images.ts b/src/app/helpers/images.ts new file mode 100644 index 0000000000000000000000000000000000000000..2372f5f92e96bbf7542e4d3cd865b6b28a2666c8 --- /dev/null +++ b/src/app/helpers/images.ts @@ -0,0 +1,52 @@ +import type { IEntity } from '@/app/interfaces/environment'; +import { useFilesWebsocketStore } from '@/app/stores/filesWebsocket'; +import type { IImage } from '@/app/interfaces/entities'; +import { useWebsocketStore } from '@/app/stores/websocket'; + +export const addUrlsToImageEntities = (entities: IEntity[]) => { + const filesWebsocketStore = useFilesWebsocketStore(); + const filesBuffer = filesWebsocketStore.filesBuffer; + let index = 0; + const entitiesToReturn = entities.map((entity: IEntity) => { + if (!entity.image_width) return entity; + if (entity.imageUrl) return entity; + if (filesWebsocketStore.imageUrl) { + // редактирование сущности изображения + entity.imageUrl = filesWebsocketStore.imageUrl; + filesWebsocketStore.cleanImageUrl(); + } else { + filesBuffer[index] = new Blob([filesBuffer[index].data], { type: 'image/jpeg' }); + entity.imageUrl = URL.createObjectURL(filesBuffer[index]); + index += 1; + } + return entity; + }); + filesWebsocketStore.cleanFilesBuffer(); + return entitiesToReturn; +}; + +export const checkIsImage = (entity: IEntity) => { + if (!entity.image_width) { + return entity; + } + const entityToReturn = { ...entity }; + const filesWebsocketStore = useFilesWebsocketStore(); + filesWebsocketStore.saveImageUrl(entityToReturn.imageUrl!); + delete entityToReturn.imageUrl; + return entityToReturn; +}; + +export const cropImage = async (newUrl: string, entity: IImage) => { + const filesWebsocketStore = useFilesWebsocketStore(); + filesWebsocketStore.saveImageUrl(newUrl); + const websocketStore = useWebsocketStore(); + const response = await fetch(newUrl); + const blob = await response.blob(); + const buffer = await blob.arrayBuffer(); + filesWebsocketStore.sendData(buffer); + const data = { + event: 'cropImage', + body: { ...entity } + }; + websocketStore.sendData(data); +}; diff --git a/src/app/helpers/index.ts b/src/app/helpers/index.ts index f0b0eb889ed209911732af7515e4049153be64b7..252d449dadb7a715ee7151633fba19996323f007 100644 --- a/src/app/helpers/index.ts +++ b/src/app/helpers/index.ts @@ -1,33 +1,51 @@ import { useInterfaceStore } from '@/app/stores/interface'; -import type { IEntity } from '@/app/interfaces/environment'; import { useDataStore } from '@/app/stores/data'; import { useWebsocketStore } from '@/app/stores/websocket'; +import type { IEntity } from '@/app/interfaces/environment'; +import { checkIsImage } from '@/app/helpers/images'; import { useFilesWebsocketStore } from '@/app/stores/filesWebsocket'; +import type { IImage } from '@/app/interfaces/entities'; -export async function uploadFile($event: Event) { - const target = $event.target as HTMLInputElement; - if (target && target.files && target.files[0]) { - const file = target.files[0]; - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.addEventListener('load', () => { - const url = reader.result; - const interfaceStore = useInterfaceStore(); - interfaceStore.changeHomeBackgroundUrl(url); - localStorage.setItem('homeBackgroundUrl', url); - }); - } -} +export const setDefaultHomeBackground = () => { + const interfaceStore = useInterfaceStore(); + interfaceStore.resetHomeBackground(); +}; -export function setDefaultHomeBackground() { +export const fetchForHomeEntities = () => { + const dataStore = useDataStore(); const interfaceStore = useInterfaceStore(); - interfaceStore.changeHomeBackgroundUrl( - 'https://wallpapers.com/images/featured/minimalist-7xpryajznty61ra3.jpg' - ); - localStorage.removeItem('homeBackgroundUrl'); -} + const websocketStore = useWebsocketStore(); + const filesWebsocketStore = useFilesWebsocketStore(); + const filesBuffer = filesWebsocketStore.filesBuffer; + if (filesBuffer.length) { + filesBuffer[0] = new Blob([filesBuffer[0].data], { type: 'image/jpeg' }); + interfaceStore.setHomeBackgroundFromDB(URL.createObjectURL(filesBuffer[0])); + } + if (!dataStore.homeEntities.length) { + const getHomeEntitiesData = { + event: 'getHomeEntities' + }; + websocketStore.sendData(getHomeEntitiesData); + } + filesWebsocketStore.removeFirstFilesBuffer(); +}; + +export const createHomeEntity = (newEntity: IEntity) => { + const websocketStore = useWebsocketStore(); + if (newEntity.image_buffer) { + websocketStore.setFileData(newEntity); + const filesWebsocketStore = useFilesWebsocketStore(); + return filesWebsocketStore.sendData(newEntity.image_buffer); + } + const data = { + event: 'createHomeEntity', + body: newEntity + }; + websocketStore.sendData(data); +}; -export const editEntity = (newState: IEntity, entityUuid: string) => { +export const editEntity = (newState: IEntity) => { + newState = checkIsImage(newState); const websocketStore = useWebsocketStore(); const data = { event: 'editHomeEntity', @@ -60,20 +78,73 @@ export const changeOrderHomeEntity = (entityUuid: string, direction: 'up' | 'dow websocketStore.sendData(data); }; -export function addUrlsToImageEntities(entities: IEntity[]) { - const filesWebsocketStore = useFilesWebsocketStore(); - const filesBlob = filesWebsocketStore.filesBlob; - let index = 0; - console.log('filesBlob[0].data', filesBlob[0]); - console.log('filesBlob[0].data', filesBlob[0]); - console.log('filesBlob.length', filesBlob.length); - return entities.map((entity: IEntity) => { - if (!entity.image_width) return entity; - filesBlob[index] = new Blob([filesBlob[index]], { type: 'image/jpeg' }); - entity.imageUrl = URL.createObjectURL(filesBlob[index]); - console.log('entity.imageUrl', entity.imageUrl); - index += 1; - console.log('filesBlob.length', filesBlob.length); - return entity; - }); -} +export const getImageSpeedDialSizeSmallerLabelsToRemove = (entity: IImage) => { + const elementsLabelsToRemove = []; + const initialImageWidth = Math.ceil(entity.image_width / +entity.image_scale); + const initialImageHeight = Math.ceil(entity.image_height / +entity.image_scale); + if (initialImageWidth <= 400 || initialImageHeight <= 400) { + elementsLabelsToRemove.push('x0.25'); + if ( + initialImageWidth <= 200 || + initialImageHeight <= 200 || + (initialImageWidth >= 1600 && entity.text_position) + ) { + elementsLabelsToRemove.push('x0.5'); + if ( + initialImageWidth <= 95 || + initialImageHeight <= 95 || + (initialImageWidth >= 1066 && entity.text_position) + ) { + elementsLabelsToRemove.push('x0.75'); + } + } + } + if ( + (initialImageWidth >= 800 && entity.text_position) || + entity.image_width < initialImageWidth + ) { + elementsLabelsToRemove.push('x1'); + } + return elementsLabelsToRemove; +}; + +export const getImageSpeedDialSizeBiggerLabelsToRemove = (entity: IImage) => { + const elementsLabelsToRemove = []; + const initialImageWidth = Math.ceil(entity.image_width / +entity.image_scale); + const initialImageHeight = Math.ceil(entity.image_height / +entity.image_scale); + if ( + (initialImageWidth >= 800 && entity.text_position) || + entity.image_width > initialImageWidth + ) { + elementsLabelsToRemove.push('x1'); + } + if ( + initialImageWidth >= 960 || + initialImageHeight >= 960 || + (initialImageWidth >= 640 && entity.text_position) + ) { + elementsLabelsToRemove.push('x1.25'); + if ( + initialImageWidth >= 800 || + initialImageHeight >= 800 || + (initialImageWidth >= 533 && entity.text_position) + ) { + elementsLabelsToRemove.push('x1.5'); + if ( + initialImageWidth >= 685 || + initialImageHeight >= 685 || + (initialImageWidth >= 457 && entity.text_position) + ) { + elementsLabelsToRemove.push('x1.75'); + if ( + initialImageWidth >= 600 || + initialImageHeight >= 600 || + (initialImageWidth >= 400 && entity.text_position) + ) { + elementsLabelsToRemove.push('x2'); + } + } + } + } + return elementsLabelsToRemove; +}; diff --git a/src/app/interfaces/index.ts b/src/app/interfaces/index.ts index fcaa1f8132010ef7b5c0ffad8643ea3ff0565535..b518d74ab4e5202f9a0e27135ffa581a3716aa42 100644 --- a/src/app/interfaces/index.ts +++ b/src/app/interfaces/index.ts @@ -1,5 +1,5 @@ export interface IImageMainInfo { - image_url: string; + imageUrl: string; image_width: number; image_height: number; } diff --git a/src/app/stores/filesWebsocket.ts b/src/app/stores/filesWebsocket.ts index 44e27a4e2067c5bf2a86a324598adc8dbeaa4508..02dcc58b61115e5748b29c19ba1d3b23084aa821 100644 --- a/src/app/stores/filesWebsocket.ts +++ b/src/app/stores/filesWebsocket.ts @@ -1,50 +1,47 @@ import { defineStore } from 'pinia'; -import { useDataStore } from '@/app/stores/data'; -import type { IEntity } from '@/app/interfaces/environment'; import { useInterfaceStore } from '@/app/stores/interface'; -import { useWebsocketStore } from '@/app/stores/websocket'; export const useFilesWebsocketStore = defineStore('filesWebsocketStore', () => { - const socket = ref(); - const dataStore = useDataStore(); const interfaceStore = useInterfaceStore(); - const homeEntities = computed(() => dataStore.homeEntities); - const filesBlob = ref([]); + + const socket = ref(); + const filesBuffer = ref([]); + const imageUrl = ref(); onMounted(() => { socket.value = new WebSocket('ws://localhost:5001'); socket.value.binaryType = 'arraybuffer'; - socket.value.onmessage = (response: any) => { + socket.value.onmessage = (response) => { console.log('response: ', response); - switch (response?.event) { - case 'changeHomeBackgroundUrl': - interfaceStore.setHomeBackgroundUrlFromDB(response.data?.setting_value); - break; - case 'editImageHomeEntity': { - let entities = [...dataStore.homeEntities]; - entities = entities.map((entity: IEntity) => { - if (entity.entity_uuid !== response.data.entity_uuid) return entity; - return response.data; - }); - dataStore.editHomeEntities(entities); - break; - } - default: { - console.log('default'); - filesBlob.value.push(response); - } + if (response?.data?.byteLength) { + filesBuffer.value.push(response); } - }; - socket.value.onclose = () => { - console.log('Websocket connection closed'); - }; - socket.value.onerror = () => { - console.log('Websocket caught an error'); + interfaceStore.setIsFetchedForBackground(); }; }); - function sendData(data: any) { + function removeFirstFilesBuffer() { + filesBuffer.value.shift(); + } + function cleanFilesBuffer() { + filesBuffer.value = []; + } + function saveImageUrl(url: string) { + imageUrl.value = url; + } + function cleanImageUrl() { + imageUrl.value = ''; + } + function sendData(data: unknown) { socket.value.send(data); } - return { filesBlob, sendData }; + return { + filesBuffer, + imageUrl, + cleanFilesBuffer, + removeFirstFilesBuffer, + saveImageUrl, + cleanImageUrl, + sendData + }; }); diff --git a/src/app/stores/interface.ts b/src/app/stores/interface.ts index 2485d0c63952d64880d04009b8e0da95d29cff47..a98f1e4004eaa07da354030b6d048a8188736fd5 100644 --- a/src/app/stores/interface.ts +++ b/src/app/stores/interface.ts @@ -2,39 +2,58 @@ import { defineStore } from 'pinia'; import { useWebsocketStore } from '@/app/stores/websocket'; export const useInterfaceStore = defineStore('interfaceStore', () => { - const homeBackgroundUrl = ref( - 'https://wallpapers.com/images/featured/minimalist-7xpryajznty61ra3.jpg' - ); - const defaultHomeBackgroundUrl = ref( - 'https://wallpapers.com/images/featured/minimalist-7xpryajznty61ra3.jpg' + const websocketStore = useWebsocketStore(); + + const defaultHomeBackground = ref( + 'https://t3.ftcdn.net/jpg/05/01/28/98/360_F_501289843_4ITbthNCydFQGgJmoZe4IQKchItBubqZ.jpg' ); - onMounted(() => { - homeBackgroundUrl.value = - localStorage.getItem('homeBackgroundUrl') || defaultHomeBackgroundUrl.value; - }); - function changeHomeBackgroundUrl(newUrl: string) { - homeBackgroundUrl.value = newUrl; - const websocketStore = useWebsocketStore(); + const homeBackground = ref(defaultHomeBackground.value); + const isFetchedForBackground = ref(false); + + function setIsFetchedForBackground() { + isFetchedForBackground.value = true; + console.log('isFetchedForBackground.value', isFetchedForBackground.value); + } + + function resetHomeBackground() { + homeBackground.value = defaultHomeBackground.value; const data = { - event: 'changeHomeBackgroundUrl', - body: { - setting_name: 'homeBackgroundUrl', - setting_value: newUrl - } + event: 'removeHomeBackground' }; websocketStore.sendData(data); } - function setHomeBackgroundUrlFromDB(url: string | null) { + function changeHomeBackground(newUrl: string) { + homeBackground.value = newUrl; + const image = new Image(); + image.src = newUrl; + image.onload = async () => { + const response = await fetch(newUrl); + const blob = await response.blob(); + const data = { + event: 'changeHomeBackground', + body: { + setting_name: 'homeBackground', + setting_value: newUrl, + extension: blob.type + } + }; + websocketStore.sendData(data); + }; + } + function setHomeBackgroundFromDB(url: string | null) { if (!url) { return; } - homeBackgroundUrl.value = url; + homeBackground.value = url; } return { - homeBackgroundUrl, - defaultHomeBackgroundUrl, - changeHomeBackgroundUrl, - setHomeBackgroundUrlFromDB + homeBackground, + defaultHomeBackground, + isFetchedForBackground, + setIsFetchedForBackground, + resetHomeBackground, + changeHomeBackground, + setHomeBackgroundFromDB }; }); diff --git a/src/app/stores/websocket.ts b/src/app/stores/websocket.ts index ab36840309aaf149721f0a25b4c3e67ecb848435..6638f7ca530cc35d496d7ce830330bbcd2ed789e 100644 --- a/src/app/stores/websocket.ts +++ b/src/app/stores/websocket.ts @@ -2,40 +2,37 @@ import { defineStore } from 'pinia'; import { useDataStore } from '@/app/stores/data'; import type { IEntity } from '@/app/interfaces/environment'; import { useInterfaceStore } from '@/app/stores/interface'; -import { addUrlsToImageEntities } from '@/app/helpers'; +import { addUrlsToImageEntities } from '@/app/helpers/images'; import { useFilesWebsocketStore } from '@/app/stores/filesWebsocket'; export const useWebsocketStore = defineStore('websocketStore', () => { - const socket = ref(); const dataStore = useDataStore(); const interfaceStore = useInterfaceStore(); const filesWebsocketStore = useFilesWebsocketStore(); + + const filesBufferLength = computed(() => filesWebsocketStore.filesBuffer.length); const homeEntities = computed(() => dataStore.homeEntities); + const imageEntitiesCount = computed( + () => homeEntities.value.filter((entity) => entity.image_width).length + ); + + const socket = ref(); + const initialDataToSend = ref(); + const isInitialAddUrlsToImageEntitiesFinished = ref(false); const file = ref(); - const filesBlobLength = computed(() => filesWebsocketStore.filesBlob.length); - function setFileData(data: any) { - file.value = data; - } + onMounted(() => { socket.value = new WebSocket('ws://localhost:5000'); - socket.value.onopen = () => { - const getHomeEntitiesData = { - event: 'getHomeEntities' - }; - socket.value.send(JSON.stringify(getHomeEntitiesData)); - const getHomeBackgroundUrlData = { - event: 'getHomeBackgroundUrl' - }; - socket.value.send(JSON.stringify(getHomeBackgroundUrlData)); + socket.value.onopen = async () => { + socket.value.send(JSON.stringify(initialDataToSend.value)); }; - socket.value.onmessage = (event: any) => { - console.log('event: ', event); + socket.value.onmessage = async (event: any) => { const response = JSON.parse(event.data); console.log('response: ', response); switch (response.event) { case 'getHomeEntities': { const entities = response.data; - if (filesBlobLength.value) { + if (imageEntitiesCount.value && filesBufferLength.value === imageEntitiesCount.value) { const entitiesAddedUrls = addUrlsToImageEntities(entities); dataStore.editHomeEntities(entitiesAddedUrls); } else { @@ -43,38 +40,51 @@ export const useWebsocketStore = defineStore('websocketStore', () => { } break; } - case 'getHomeBackgroundUrl': - interfaceStore.setHomeBackgroundUrlFromDB(response.data?.setting_value); + case 'getHomeBackground': { + const blob = new Blob([response.data.setting_value.data], { + type: `image/jpeg` + }); + const url = URL.createObjectURL(blob); + interfaceStore.setHomeBackgroundFromDB(url); break; + } case 'createHomeEntity': { const entities = [...homeEntities.value]; + if (response.data.image_width) { + response.data.imageUrl = filesWebsocketStore.imageUrl; + filesWebsocketStore.cleanImageUrl(); + } entities.push(response.data); dataStore.editHomeEntities([...entities]); break; } case 'createImageHomeEntity': { + if (!file.value) break; const data = { event: 'createHomeEntity', body: { ...file.value } }; - console.log('send now:'); + file.value = null; sendData(data); - console.log('sent!'); break; } case 'editHomeEntity': { - let entities = [...dataStore.homeEntities]; + let entities = [...homeEntities.value]; entities = entities.map((entity: IEntity) => { if (entity.entity_uuid !== response.data.entity_uuid) return entity; + if (response.data.image_width) { + response.data.imageUrl = filesWebsocketStore.imageUrl; + filesWebsocketStore.cleanImageUrl(); + } return response.data; }); dataStore.editHomeEntities(entities); break; } case 'deleteHomeEntity': { - let newState = [...dataStore.homeEntities]; + let newState = [...homeEntities.value]; newState = newState.filter( (entity: IEntity) => entity.entity_uuid !== response.data.entity_uuid ); @@ -82,7 +92,7 @@ export const useWebsocketStore = defineStore('websocketStore', () => { break; } case 'changeOrderHomeEntity': { - const newState = dataStore.homeEntities; + const newState = [...homeEntities.value]; const entityIndex = newState.findIndex( (entity: IEntity) => entity.entity_uuid === response.data.entity_uuid ); @@ -98,6 +108,7 @@ export const useWebsocketStore = defineStore('websocketStore', () => { ]; } dataStore.editHomeEntities(newState); + break; } } }; @@ -109,15 +120,28 @@ export const useWebsocketStore = defineStore('websocketStore', () => { }; }); - watch(filesBlobLength, () => { - console.log('watch'); - const entitiesAddedUrls = addUrlsToImageEntities(homeEntities.value); - console.log('entitiesAddedUrls', entitiesAddedUrls); - dataStore.setHomeEntities(entitiesAddedUrls); + watch([filesBufferLength, homeEntities], () => { + if ( + (homeEntities.value.length && filesBufferLength.value === imageEntitiesCount.value) || + (isInitialAddUrlsToImageEntitiesFinished.value && filesBufferLength.value) + ) { + const entitiesAddedUrls = addUrlsToImageEntities(homeEntities.value); + dataStore.setHomeEntities(entitiesAddedUrls); + isInitialAddUrlsToImageEntitiesFinished.value = true; + } }); - + function setFileData(data: any) { + file.value = data; + } + function setInitialDataToSend(data: any) { + initialDataToSend.value = data; + } function sendData(data: any) { socket.value.send(JSON.stringify(data)); } - return { sendData, setFileData }; + return { + setInitialDataToSend, + sendData, + setFileData + }; }); diff --git a/src/modules/entities/ImageItem.vue b/src/modules/entities/ImageItem.vue index 8c142088ffd8a34331e9d53e15591b037b72f93b..1e1615dd6dceef7bd4636154f91f7a3bb8e5e975 100644 --- a/src/modules/entities/ImageItem.vue +++ b/src/modules/entities/ImageItem.vue @@ -1,13 +1,8 @@