UNPKG

threed-garden

Version:

ThreeD Garden: WebGL 3D Environment Interface for Next.JS React TypeScript Three.JS React-Three Physics, 2D Paper.JS; APIs: Apollo GraphQL, WordPress; CSS: Tailwind, Radix-UI; Libraries: FarmBot 3D; AI: OpenAI, DeepSeek

1,245 lines (1,118 loc) 54.8 kB
// 'use client' // no! should be ssr friendly // 'use server' // yes! let nextjs manage this default // ========================================================== // RESOURCES // ** APOLLO Client 3 -- Cache Store Imports import { makeVar } from '@apollo/client' import create, { StoreApi } from '#/lib/api/graphql/createStore' // ** GRAPHQL -- Schema Objects, Queries, Mutations, Fragments, Subscriptions // ============================================================== // ** -- TYPES (Schema) // ============================================================== // ** schema objects // import Nouns from '#/lib/api/graphql/schema/nouns.graphql' // import Participants from '#/lib/api/graphql/schema/participants.graphql' import Preferences from '#/lib/api/graphql/schema/preferencess.graphql' // import CanvasStates from '#/lib/api/graphql/schema/canvasStates.graphql' // import Projects from '#/lib/api/graphql/schema/projects.graphql' // import Plans from '#/lib/api/graphql/schema/plans.graphql' // import Scenes from '#/lib/api/graphql/schema/scenes.graphql' // import ThreeDs from '#/lib/api/graphql/schema/threeds.graphql' // import Characters from '#/lib/api/graphql/scripts/characters.graphql' // import Files from '#/lib/api/graphql/scripts/files.graphql' // import Allotments from '#/lib/api/graphql/scripts/allotments.graphql' // import Beds from '#/lib/api/graphql/scripts/beds.graphql' // import Plants from '#/lib/api/graphql/scripts/plants.graphql' // import PlantingPlans from '#/lib/api/graphql/scripts/plantingPlans.graphql' // import FarmBots from '#/lib/api/graphql/scripts/farmbots.graphql' // ============================================================== // ** scripts for objects // ============================================================== // ** -- QUERIES (Read/Get) // ============================================================== // ** get existing import GetNouns from '#/lib/api/graphql/scripts/getNouns.gql' import GetUsers from '#/lib/api/graphql/scripts/getUsers.gql' import GetParticipants from '#/lib/api/graphql/scripts/getParticipants.gql' import GetPreferences from '#/lib/api/graphql/scripts/getPreferences.gql' import GetCanvasStates from '#/lib/api/graphql/scripts/getCanvasStates.gql' import GetProjects from '#/lib/api/graphql/scripts/getProjects.gql' import GetPlans from '#/lib/api/graphql/scripts/getPlans.gql' import GetScenes from '#/lib/api/graphql/scripts/getScenes.gql' import GetThreeDs from '#/lib/api/graphql/scripts/getThreeDs.gql' import GetCharacters from '#/lib/api/graphql/scripts/getCharacters.gql' import GetFiles from '#/lib/api/graphql/scripts/getFiles.gql' // import GetAllotments from '#/lib/api/graphql/scripts/getAllotments.gql' // import GetBeds from '#/lib/api/graphql/scripts/getBeds.gql' // import GetPlants from '#/lib/api/graphql/scripts/getPlants.gql' // import GetPlantingPlans from '#/lib/api/graphql/scripts/getPlantingPlans.gql' import GetFarmBots from '#/lib/api/graphql/scripts/getFarmBots.gql' // ============================================================== // ** -- MUTATIONS (Create/Post, Update/Patch, Delete) // ============================================================== // ** create new // import CreateNouns from '#/lib/api/graphql/scripts/createNouns.gql' // import CreateUsers from '#/lib/api/graphql/scripts/createUsers.gql' // import CreateParticipants from '#/lib/api/graphql/scripts/createParticipants.gql' import CreatePreferences from '#/lib/api/graphql/scripts/createPreferences.gql' // import CreateCanvasStates from '#/lib/api/graphql/scripts/createCanvasStates.gql' // import CreateProjects from '#/lib/api/graphql/scripts/createProjects.gql' // import CreatePlans from '#/lib/api/graphql/scripts/createPlans.gql' // import CreateScenes from '#/lib/api/graphql/scripts/createScenes.gql' // import CreateThreeDs from '#/lib/api/graphql/scripts/createThreeDs.gql' // import CreateCharacters from '#/lib/api/graphql/scripts/createCharacters.gql' // import CreateFiles from '#/lib/api/graphql/scripts/createFiles.gql' // import CreateAllotments from '#/lib/api/graphql/scripts/createAllotments.gql' // import CreateBeds from '#/lib/api/graphql/scripts/createBeds.gql' // import CreatePlants from '#/lib/api/graphql/scripts/createPlants.gql' // import CreatePlantingPlans from '#/lib/api/graphql/scripts/createPlantingPlans.gql' // import CreateFarmBots from '#/lib/api/graphql/scripts/createFarmBots.gql' // ============================================================== // ** update existing // import UpdateNouns from '#/lib/api/graphql/scripts/updateNouns.gql' // import UpdateUsers from '#/lib/api/graphql/scripts/updateUsers.gql' // import UpdateParticipants from '#/lib/api/graphql/scripts/updateParticipants.gql' import UpdatePreferences from '#/lib/api/graphql/scripts/updatePreferences.gql' // import UpdateCanvasStates from '#/lib/api/graphql/scripts/updateCanvasStates.gql' // import UpdateProjects from '#/lib/api/graphql/scripts/updateProjects.gql' // import UpdatePlans from '#/lib/api/graphql/scripts/updatePlans.gql' // import UpdateScenes from '#/lib/api/graphql/scripts/updateScenes.gql' // import UpdateThreeDs from '#/lib/api/graphql/scripts/updateThreeDs.gql' // import UpdateCharacters from '#/lib/api/graphql/scripts/updateCharacters.gql' // import UpdateFiles from '#/lib/api/graphql/scripts/updateFiles.gql' // import UpdateAllotments from '#/lib/api/graphql/scripts/updateAllotments.gql' // import UpdateBeds from '#/lib/api/graphql/scripts/updateBeds.gql' // import UpdatePlants from '#/lib/api/graphql/scripts/updatePlants.gql' // import UpdatePlantingPlans from '#/lib/api/graphql/scripts/updatePlantingPlans.gql' // import UpdateFarmBots from '#/lib/api/graphql/scripts/updateFarmBots.gql' // ============================================================== // ** delete existing // import DeleteNouns from '#/lib/api/graphql/scripts/deleteNouns.gql' // import DeleteUsers from '#/lib/api/graphql/scripts/deleteUsers.gql' // import DeleteParticipants from '#/lib/api/graphql/scripts/deleteParticipants.gql' // import DeletePreferences from '#/lib/api/graphql/scripts/deletePreferences.gql' // import DeleteCanvasStates from '#/lib/api/graphql/scripts/deleteCanvasStates.gql' // import DeleteProjects from '#/lib/api/graphql/scripts/deleteProjects.gql' // import DeletePlans from '#/lib/api/graphql/scripts/deletePlans.gql' // import DeleteScenes from '#/lib/api/graphql/scripts/deleteScenes.gql' // import DeleteThreeDs from '#/lib/api/graphql/scripts/deleteThreeDs.gql' // import DeleteCharacters from '#/lib/api/graphql/scripts/deleteCharacters.gql' // import DeleteFiles from '#/lib/api/graphql/scripts/deleteFiles.gql' // import DeleteAllotments from '#/lib/api/graphql/scripts/deleteAllotments.gql' // import DeleteBeds from '#/lib/api/graphql/scripts/deleteBeds.gql' // import DeletePlants from '#/lib/api/graphql/scripts/deletePlants.gql' // import DeletePlantingPlans from '#/lib/api/graphql/scripts/deletePlantingPlans.gql' // import DeleteFarmBots from '#/lib/api/graphql/scripts/deleteFarmBots.gql' // ============================================================== // ** JWT AUTH + REFRESH import RegisterUser from '#/lib/api/graphql/scripts/registerUser.gql' import LoginUser from '#/lib/api/graphql/scripts/loginUser.gql' import RefreshJwtAuthToken from '#/lib/api/graphql/scripts/refreshJwtAuthToken.gql' // ============================================================== // ** THREE Imports (for typing only) import * as THREE from 'three' // ** UUID Imports import { v4 as newUUID } from 'uuid' // ** HELPER Imports import ccm from '#/lib/utils/console-colors' // ============================================================== // IMPORTS COMPLETE // console.debug(`%c====================================`, ccm.blue) // console.debug(`%c🥕 ThreeDGarden<FC,R3F>: Apollo {stores}`, ccm.blue) // console.debug(`%c====================================`, ccm.blue) // ========================================================== // ** DEBUG: this module const debug: boolean = false const DEBUG: boolean = false // ============================================================== // ** NOUN Types + Interfaces // {one} interface INoun { _id: string _ts: string _type: string _name: string data: Object layers: [ { _id: string _name: string data: Object } ] } // [all] interface INouns { nouns: Array<INoun> } interface IStore extends StoreApi<any> { // params _type: string _plural: string _storageItem: string _storageItemHistory: string // store .store store: any // store .actions actions: any } interface IStorePreferences extends IStore { store: INoun actions: any } // ** Noun Object -- Constructor Function // -- returns new noun function noun(this: INoun, _type: string = 'noun') { // object params this._id = newUUID() this._ts = new Date().toISOString() this._type = _type.toLowerCase() this._name = _type.toUpperCase() + ' NAME (default)' // wp custom fields this.data = { // defaults title: 'NOUN TITLE: NOTHING YET, SIR', } // layers/levels this.layers = [ { _id: newUUID(), _name: 'LAYER[0]', data: {}, } ] } // ============================================================== // ============================================================== // ============================================================== // ** Noun Store -- Constructor Function // -- returns new (nounStore as any) function nounStore(this: IStore, _type = 'noun') { // store params this._type = _type.toLowerCase() this._plural = _type + 's' this._storageItem = 'threed_' + this._type this._storageItemHistory = 'threed_' + this._type + 'History' // ============================================================== // ** Noun Store .store // -- returns Object of Functions (ac3 reactive vars) this.store = create({ _id: newUUID(), _ts: new Date().toISOString(), _type: this._type, all: [], // all of this nouns historical + current records (all scenes; all projects; all plans;) one: new (noun as any)(this._type), // {}, // the current noun, aka 'this one noun' // track current noun + noun history // current: ^this one noun, history: [], // from local storage // track payloads from db ?? count: 0, // example counter (for fun/learning) // countDB: 0, // example counter (for fun/learning) // allDB: [], // from db (mysql wordpress via graphql) // oneDB: {}, // pre-this noun, ready to be mapped to 'this' noun }) // ============================================================== // ** Noun Store .actions // -- returns Object of Functions this.actions = { isVisible: () => { return (state: any): boolean => state }, toggleIsVisible: () => { return (state: any): boolean => !state }, increaseCount: (n = 1) => { return (state: any): number => state + n }, decreaseCount: (n = 1) => { return (state: any): number => state - n }, // redundant (but useful ???) getState: (): Object => { return this.store.getState() }, removeAll: (): void => { localStorage.removeItem(this._storageItem) localStorage.removeItem(this._storageItemHistory) this.store.update('all', []) this.store.update('one', new (noun as any)(this._type)) this.store.update('history', []) // this.store.update('allDB', []) // this.store.update('oneDB', {}) this.store.update('count', 0) // this.store.update('countDB', 0) // if (debug) console.clear() if (debug) console.debug(`%c X removeAll [${this._type}]`, ccm.red, true) if (debug) console.debug(`%c get one [${this._type}]`, ccm.red, this.store.get('one')) }, // add a new current 'this' noun addNew: () => { // console.debug(`this`, this) if (debug) console.debug(`%c🌱 addNew [${this._type}] (before)`, ccm.orange, this.store.get('all')) // throw new Error(`[MM] testing... this`) // create a new one // if (Object.keys(this.store.get('one')).length === 0) { // try { // this.store.update('one', new (noun as any)(this._type)) // } catch (ERROR) { // console.error(`%caddNew {${this._type}} ERROR`, ERROR) // } // } // // save + update old one // else { // // noun history (save existing before mutating, if not empty) // this.store.update('history', [this.store.get('one'), ...this.store.get('history')]) // if (debug) console.debug(`%caddNew [${this._type}] (save history)`, ccm.orange, this.store.get('history')) // count this.store.update('count', this.store.get('count') + 1) // manual // this.store.update('countDB', this.store.get('allDB').length) // automatic // nounCurrent (overwrite this one -- mutate) this.store.update('one', { _id: newUUID(), _ts: new Date().toISOString(), _type: _type.toLowerCase(), _name: _type.toUpperCase() + ' NAME (default modified)', data: { title: 'NOT A THING, SIR', // 'WE AINT FOUND SHIT', }, layers: [ { _id: newUUID(), _name: 'LAYER[0]', data: {}, } ], }) // } if (debug) console.debug(`%caddNew {${this._type}} (added)`, ccm.orange, this.store.get('one')) // noun history (save existing before mutating, if not empty) this.store.update('history', [this.store.get('one'), ...this.store.get('history')]) if (debug) console.debug(`%caddNew [${this._type}] (save history)`, ccm.orange, this.store.get('history')) // saveToDisk this.actions.saveToDisk() // loadFromDisk // this.actions.loadFromDisk() // if (debug) console.debug(`%caddNew [${this._type}] (final)`, ccm.green, this.store.get('one')) }, updateData: () => { // this.store.update('one', ...this.store.get('one').data) }, save: () => { // saveToDisk (localStorage) this.actions.saveToDisk() // saveToDB (mutations via graphql api) this.actions.saveToDB() }, // save data to browser local storage saveToDisk: () => { if (typeof window != 'undefined') { try { localStorage.setItem( this._storageItem, JSON.stringify({ subject: this._plural, payload: this.store.get('all'), }) ) localStorage.setItem( this._storageItemHistory, JSON.stringify({ subject: this._plural, payload: this.store.get('history'), }) ) // if (debug) console.debug(`%c=======================================================`, ccm.black) if (debug) console.debug(`%c💾 saveToDisk [${this._type}]`, ccm.greenAlert, this.store.get('all')) if (debug) console.debug(`%c=======================================================`, ccm.black) return true } catch (ERROR) { if (debug) console.debug(`%c💾 saveToDisk [${this._type}] ERROR`, ccm.redAlert, ERROR) if (debug) console.debug(`%c=======================================================`, ccm.red) return false } } // typeof window === 'undefined' else { return false } }, // get data from browser local storage loadFromDisk: () => { if (typeof window !== 'undefined') { try { const query = JSON.parse(localStorage.getItem(this._storageItem)) if (query) { // if (debug) console.debug(`%c💾 loadFromDisk [${this._type}] QUERY?`, ccm.blue, query) const { payload } = query // if (debug) console.debug(`%c💾 loadFromDisk [${this._type}] QUERY.PAYLOAD?`, ccm.blue, payload) if (payload) { if (debug) console.debug(`%c💾 loadFromDisk [${this._type}] payload`, ccm.blue, true, payload) this.store.update('all', payload) // payload should have .data{} if (debug) console.debug(`%c💾 loadFromDisk [${this._type}s] (after)`, ccm.blue, this.store.get('all')) // TODO : WHICH DB RECORD DO YOU WANT TO USE ??? // default is the first one [0] const thisStoreUseOne = this.store.get('all')[0] if (debug) console.debug(`%c💾 loadFromDisk {${this._type}} (after)`, ccm.blue, thisStoreUseOne) // update metadata for the store to use if (thisStoreUseOne.data) { // this.store.update('one._name', thisStoreUseOne.data.title) // ideally this.store.update('one', { _id: thisStoreUseOne._id, // .data.projectId (TODO: get wp_post.id and not wp_type.projectId) _ts: thisStoreUseOne._ts, // thisStoreUseOne.data.modified, _type: thisStoreUseOne._type, _name: thisStoreUseOne._name, // .data.title, // wp custom fields data: thisStoreUseOne.data, // layers/levels layers: thisStoreUseOne.layers, }) return true } } else { if (debug) console.debug(`%c💾 loadFromDisk [${this._type}] EMPTY QUERY.PAYLOAD?`, ccm.orange, query) } } else { if (debug) console.debug(`%c💾 loadFromDisk [${this._type}] NOTHING TO LOAD`, ccm.blue, query) } // if everything in this logic fails, return false as default return false } catch (ERROR) { if (debug) console.debug(`%c💾 loadFromDisk [${this._type}] ERROR`, ccm.red, ERROR) return false } } else { if (debug) console.debug(`%c💾 loadFromDisk [${this._type}] ERROR`, ccm.red) return false } }, // TODO: SAVE TO DB VIA GRAPHQL // save data to db via graphql mutation saveToDB: async (client: any) => { try { if (debug) console.debug(`%c🌩️ saveToDB [${this._type}] client`, ccm.orangeAlert, client) // TODO: SAVE TO DB VIA GRAPHQL // .gql let MUTATION = null // default? no switch (this._type) { // case 'participant': // MUTATION = UpdateParticipants // break case 'preferences': MUTATION = UpdatePreferences break // case 'canvasState': // MUTATION = UpdateCanvasStates // break // case 'noun': // MUTATION = UpdateNouns // break // case 'project': // MUTATION = UpdateProjects // break // case 'plan': // MUTATION = UpdatePlans // break // case 'scene': // MUTATION = UpdateScenes // break // case 'threed': // MUTATION = UpdateThreeDs // break // case 'file': // MUTATION = UpdateFiles // break // case 'allotment': // MUTATION = UpdateAllotments // break // case 'bed': // MUTATION = UpdateBeds // break // case 'plant': // MUTATION = UpdatePlants // break // case 'plantingPlan': // MUTATION = UpdatePlantingPlans // break // case 'bear': // MUTATION = UpdateBears // break } const parameters = { id: 0, title:`${this._type}: NUTHIN YET`, content: "YO YO YO", status: "draft", } // using mutation hook // const { // data, // loading, // error, // fetchMore, // refetch, // networkStatus // } = useMutation(MUTATION, { parameters }, { client }) // console.debug(`%c🌩️ saveToDB [${this._type}]: DATA RETURNED`, data, loading, error) // using mutation directly const mutation = await client.mutation({ mutation: MUTATION, variables: { parameters }, }) console.debug(`%c🌩️ saveToDB [${this._type}]: MUTATION RETURNED`, ccm.blue, mutation) const { data, loading, error } = mutation console.debug(`%c🌩️ saveToDB [${this._type}]: DATA RETURNED`, data, loading, error) return true // OR false, if unsuccessful // ** } catch (ERROR) { if (debug) console.debug(`%c🌩️ saveToDB [${this._type}]: ERROR`, ccm.redAlert, ERROR) return false } }, // get data from db via graphql query loadFromDB: async (client: any) => { // if (debug) console.clear() if (debug) console.debug(`%c🌩️ loadFromDB this ${this._type}`, ccm.blueAlert, this) // try { // .gql let QUERY = null // default? GetPreferences switch (this._type) { case 'participant': QUERY = GetParticipants break case 'preferences': QUERY = GetPreferences break case 'canvasState': QUERY = GetCanvasStates break case 'noun': QUERY = GetNouns break case 'project': QUERY = GetProjects break case 'plan': QUERY = GetPlans break case 'scene': QUERY = GetScenes break case 'threed': QUERY = GetThreeDs break case 'file': QUERY = GetFiles break // case 'allotment': // QUERY = GetAllotments // break // case 'bed': // QUERY = GetBeds // break // case 'plant': // QUERY = GetPlants // break // case 'plantingPlan': // QUERY = GetPlantingPlans // break // case 'bear': // QUERY = GetBears // break case 'farmbot': QUERY = GetFarmBots break } const parameters = { first: 10, last: 0, after: 0, before: 0, } // using query hook // const { // data, // loading, // error, // fetchMore, // refetch, // networkStatus // } = useQuery(QUERY, { parameters }, { client }) // console.debug(`%c🌩️ loadFromDB [${this._type}]: DATA RETURNED`, data, loading, error) // using query directly const query = await client.query({ query: QUERY, variables: { parameters }, }) // console.debug(`%c🌩️ loadFromDB [${this._type}]: QUERY RETURNED`, ccm.blue, query) const { data, loading, error } = query // console.debug(`%c🌩️ loadFromDB [${this._type}]: DATA RETURNED`, data, loading, error) if (loading) { // console.debug(`%c🌩️ loadFromDB [${this._type}]: DATA LOADING`, loading) return false // <div>loading...</div> } if (error) { if (debug) console.debug(`%c🌩️ loadFromDB [${this._type}]: DATA RETURNED with error`, ccm.red, error) return false // <div>{JSON.stringify(error.message)}</div> } if (data) { // console.debug(`%c🌩️ loadFromDB [${this._type}]: DATA RETURNED`, ccm.yellow, data, loading, error) let payload let nodes = payload // ** EDGES or NODES ?? if (data[this._plural]?.edges?.length) { // const payload = data[this._plural].edges payload = data[this._plural].edges.map( (node: Object): Object => // nounId, id, uri, slug, title // <div key={node.nounId}> // wp nounId: {node.nounId}<br /> // gql id: {node.id}<br /> // uri: {node.uri}<br /> // slug: {node.slug}<br /> // title: {node.title}<br /> // </div> node ) } else if (data[this._plural]?.nodes?.length) { // const payload = data[this._plural].nodes payload = data[this._plural].nodes.map( (node: Object): Object => // nounId, id, uri, slug, title // <div key={node.nounId}> // wp nounId: {node.nounId}<br /> // gql id: {node.id}<br /> // uri: {node.uri}<br /> // slug: {node.slug}<br /> // title: {node.title}<br /> // </div> node ) } if (payload.length) { // map over payload to set this.data{} const allPayload = payload.map((node: Object): Object[] => { const one = new (noun as any)(this._type) one.data = node return one }) // console.debug(`%c🌩️ loadFromDB [${this._type}]`, ccm.blue, all) // save to disk here ?? yes (if window.localStorage) this.actions.saveToDisk() // set state from db // this.store.update('all', ([...allPayload, ...this.store.get('all')])) // merge all nodes, past + present this.store.update('all', ([...allPayload])) // merge all nodes, past + present const nouns = this.store.get('all') if (debug) console.debug(`%c🌩️ loadFromDB [${this._type}] (all)`, ccm.blue, nouns) // this.store.update('history', ([...nouns, ...this.store.get('history')])) // merge all nodes, past + present // // nounCurrent (overwrite -- mutate) this.store.update('one', nouns[nouns.length - 1]) // node (use last one) // const nounDB = this.store.get('one') // if (debug) console.debug(`%c🌩️ loadFromDB [${this._type}] {one}`, ccm.blue, nounDB) // save to disk here ??? no // this.actions.saveToDisk() // // nounCurrent (overwrite -- mutate) // this.store.update('one', { // _id: nounDB._id, // newUUID(), // _ts: nounDB._ts, // new Date().toISOString(), // _type: nounDB._type, // // _name: nounDB.data.title, // // _name: _type.toUpperCase() + ' NAME: ' + nounDB.data.title, // _name: nounDB._name, // // wp custom fields // data: nounDB.data, // // layers/levels // layers: nounDB.layers, // }) if (debug) console.debug(`%c🌩️ loadFromDB [${this._type}] {one} (after)`, ccm.blue, this.store.get('one')) // save to disk here ?? yes (if window.localStorage) this.actions.saveToDisk() // count // this.store.update('count', this.store.get('count') + 1) // manual // this.store.update('countDB', this.store.get('allDB').length) // automatic this.store.update('count', this.store.get('all').length) // this.store.update('countDB', this.store.get('all').length) // if (debug) console.debug(`%c🌩️ loadFromDB countDB`, ccm.blue, this.store.get('countDB')) // if (debug) console.debug(`%c=======================================================`, ccm.black) return true } else { console.debug(`%c🌩️ loadFromDB [${this._type}] NO PAYLOAD`, ccm.redAlert, data) return false } } console.debug(`%c🌩️ loadFromDB [${this._type}]: OTHER ERROR`, ccm.redAlert, data) return false // } catch (ERROR) { // console.debug(`%c🌩️ loadFromDB [${this._type}]: ERROR`, ccm.redAlert, ERROR) // return false // } }, // load from data source: DB or DISK ?? // check DISK first, then DB loadFromDataSource: async (client: any) => { const responseData = { isLoadedFromDisk: false, isLoadedFromDB: false, } responseData.isLoadedFromDisk = await this.actions.loadFromDisk(client) if (responseData.isLoadedFromDisk) { if (debug) console.debug(`%c ${this._type} loadFromDataSource isLoadedFromDisk`, ccm.blue, 'Local Disk') return responseData } else { responseData.isLoadedFromDB = await this.actions.loadFromDB(client) if (responseData.isLoadedFromDB) { if (debug) console.debug(`%c ${this._type} loadFromDataSource isLoadedFromDB`, ccm.blue, 'API => DB') return responseData } } // default if (debug) console.debug(`%c ${this._type} loadFromDataSource isLoadedFrom Nowhere`, ccm.redAlert, responseData) return responseData }, // load 'this' THREED[S] into React Three Fiber view loadToCanvas: ( client: Object = {}, nodes: Object[] = [], // hmmmm -- nodes? _type: string = 'project', // main type for query _requestType: string = 'plansOfThreeds', // sub-type for query _id: string = '3333', // some id _r3fCanvas: string = '_r3fcanvas1' // target canvas #_r3fCanvasX ) => { // ** let objectArrayToReturn = [] // ** try { if (nodes.length) { // send 'nodes' to '#_r3fCanvas1' if (debug || DEBUG) console.debug('%c #_r3fCanvas1 to receive JS Object: nodes', ccm.redAlert, nodes) // return true // <div>...plan of nodes as r3f component...</div> objectArrayToReturn = nodes return objectArrayToReturn } if (_type == 'project') { if (_requestType == 'plansOfThreeDs') { // const load_PlanOfThreeDNodes_ToThreeDCanvas = this.store.get('one').plans let load_PlanOfThreeDNodes_ToThreeDCanvas = [] try { if (this.store.get('one')) { load_PlanOfThreeDNodes_ToThreeDCanvas = this.store.get('one').data?.plans?.nodes[0]?.threedsActive?.nodes // plans[] of threeds[] } } catch (ERROR) { if (debug || DEBUG) console.debug(`%c load_PlanOfThreeDNodes_ToThreeDCanvas: ERROR`, ccm.redAlert, ERROR) } if (load_PlanOfThreeDNodes_ToThreeDCanvas && load_PlanOfThreeDNodes_ToThreeDCanvas.length) { // send 'threeds' to '_r3fCanvas' if (debug || DEBUG) console.debug( '%c #_r3fCanvas to receive JS Object: load_PlanOfThreeDNodes_ToThreeDCanvas', ccm.green, load_PlanOfThreeDNodes_ToThreeDCanvas ) objectArrayToReturn = load_PlanOfThreeDNodes_ToThreeDCanvas return objectArrayToReturn return true } } } // ** if (debug || DEBUG) console.debug('%c #_r3fCanvas to receive NOTHING', ccm.redAlert) return [] return false // ** } catch (ERROR) { if (debug || DEBUG) console.debug(`%c load {noun}: ERROR`, ccm.redAlert, ERROR) return [] return false } }, } // nounActions } // nounStore // ============================================================== // ============================================================== // ============================================================== // ** Modal Object -- Constructor Function // -- returns new modal function modal(this: INoun, _type = 'modal') { // object params this._id = newUUID() this._ts = new Date().toISOString() this._type = _type.toLowerCase() this._name = _type.toUpperCase() + ' NAME (default)' // wp custom fields this.data = {} // layers/levels this.layers = [ { _id: newUUID(), _name: 'LAYER[0]', data: {}, } ] } // ============================================================== // ** Modal Store -- Constructor Function // -- returns new (modalStore as any) function modalStore(this: IStore, _type = 'modal') { // store params this._type = _type.toLowerCase() this._plural = _type + 's' this._storageItem = 'threed_' + this._type this._storageItemHistory = 'threed_' + this._type + 'History' // ============================================================== // ** Modal Store .store this.store = create({ isVisible: false, }) // ============================================================== // ** Modal Store .actions this.actions = { toggleIsVisible: (e: any = null) => { this.store.update('isVisible', !this.store.get('isVisible')) localStorage.setItem( this._storageItem, JSON.stringify({ subject: 'isVisible', payload: this.store.get('isVisible'), }) ) }, handleOpen: (e: any = null) => { this.store.update('isVisible', true) localStorage.setItem( this._storageItem, JSON.stringify({ subject: 'isVisible', payload: true, }) ) }, handleClose: (e: any = null) => { this.store.update('isVisible', false) localStorage.setItem( this._storageItem, JSON.stringify({ subject: 'isVisible', payload: false, }) ) }, } // modalActions } // modalStore // ============================================================== // ============================================================== // ============================================================== // ** Preferences Store -- Constructor Function // -- returns new (preferencesStore as any) // function preferenceStoreCustom(this: IStorePreferences, _type = 'preferences') { // // store params // this._type = _type.toLowerCase() // this._plural = _type // + 's' // this._storageItem = 'threed_' + this._type // this._storageItemHistory = 'threed_' + this._type + 'History' // // ============================================================== // // ** Preferences Store .store // // ** // // const preferences = useReactiveVar(preferencesDataVar) // // const doAutoLoadDataApollo = preferencesStore.store.useStore('doAutoLoadData') // // const doAutoLoadDataApollo = preferences.doAutoLoadData // // const doAutoLoadDataApollo = this.store.get('doAutoLoadData') // const doAutoLoadDataApollo: boolean = false // // console.debug('APOLLO: ThreeDLevaControls doAutoLoadDataApollo', doAutoLoadDataApollo) // // const doAutoRotateApollo = preferencesStore.store.useStore('doAutoRotate') // // const doAutoRotateApollo = preferences.doAutoRotate // // const doAutoRotateApollo = this.store.get('doAutoRotate') // const doAutoRotateApollo: boolean = false // // console.debug('APOLLO: ThreeDLevaControls doAutoRotateApollo', doAutoRotateApollo) // // const projectNameApollo = preferencesStore.store.useStore('projectName') // // const projectNameApollo = preferences.projectName // // const projectNameApollo = this.store.get('projectName') // const projectNameApollo: string = '' // // console.debug('APOLLO: ThreeDLevaControls projectNameApollo', projectNameApollo) // this.store = create({ // doAutoLoadData: doAutoLoadDataApollo, // true | false, // doAutoRotate: doAutoRotateApollo, // true | false, // projectName: projectNameApollo, // string | 'APOLLO PREFERENCES STORE: projectName' // }) // // ============================================================== // // ** Preferences Store .actions // this.actions = { // setDoAutoLoadData: (e: boolean = false) => { // // this.store.update('doAutoLoadData', !this.store.get('doAutoLoadData')) // this.store.update('doAutoLoadData', e) // localStorage.setItem( // this._storageItem, // JSON.stringify({ // subject: 'doAutoLoadData', // payload: this.store.get('doAutoLoadData'), // }) // ) // return this.store.get('doAutoLoadData') // }, // setDoAutoRotate: (e: boolean = false) => { // // this.store.update('doAutoRotate', !this.store.get('doAutoRotate')) // this.store.update('doAutoRotate', e) // localStorage.setItem( // this._storageItem, // JSON.stringify({ // subject: 'doAutoRotate', // payload: this.store.get('doAutoRotate'), // }) // ) // return this.store.get('doAutoRotate') // }, // setProjectName: (e: string = 'nope') => { // this.store.update('projectName', e) // localStorage.setItem( // this._storageItem, // JSON.stringify({ // subject: 'projectName', // payload: this.store.get('projectName'), // }) // ) // // const doModifyProjectName = client.cache.modify({ // // id: client.cache.identify(preferencesStore), // // fields: { // // name(projectName) { // // return projectName.toUpperCase() // // }, // // }, // // /* broadcast: false // Include this to prevent automatic query refresh */ // // }) // return this.store.get('projectName') // }, // } // preferencesActions // } // preferencesStoreCustom // ============================================================== // ** CREATE REACTIVE VARS (APOLLO LOCAL STATE) // export const isParticipantDataSetVar = makeVar(false) // boolean: false | true const userDataVarDefaults = { // api defaults userId: 0, title: 'USER DATA VAR: DEFAULT', content: 'juicy mastery', status: 'DRAFT', username: 'juicemaster', password: 'secret', } // export const isParticipantDataSetVar = makeVar(false) // boolean: false | true const participantDataVarDefaults = { // api defaults participantId: 0, title: 'PARTICIPANT DATA VAR: DEFAULT', content: '', status: 'DRAFT', // custom fields ownerId: 1, version: '0.0.0', } // export const isPreferencesDataSetVar = makeVar(false) // boolean: false | true const preferencesDataVarDefaults = { // api defaults id: '0', preferencesId: 0, title: 'PREFERENCES DATA VAR: DEFAULT', content: '', status: 'DRAFT', // custom fields ownerId: 1, version: '0.0.0', doAutoLoadData: false, // boolean: false | true doAutoRotate: false, // boolean: false | true // world prefs doWorldDebug: false, // boolean: false | true doWorldTesting: false, // boolean: false | true doWorldPhysics: false, // boolean: false | true doWorldControl: false, // boolean: false | true doWorldUnfollowCam: true, // boolean: false | true // home design prefs showPanelFirst: true, // boolean: true | false showPanelLast: true, // boolean: true | false // project prefs projectName: 'client should never see this string', // string: '' // scene prefs environmentPreset: 'park', // default (client should never see this) environmentBgBlur: 0.20, // default (our chosen maximum blur) // character prefs characterTrailColor: '#003300', // hex color doCharacterAnimation: true, // boolean: false | true // set functions // setPreferencesDataVar: () => {}, // function: set properties of "this" } // export const isCanvasStateSetVar = makeVar(false) // boolean: false | true const canvasStatePaperVarDefaults: Object = { // api defaults canvasStateId: 0, title: 'CANVAS STATE PAPER VAR: DEFAULT', content: '', status: 'DRAFT', ownerId: 1, version: '0.0.0', state: { // ** PAPER Scope: paperjs.org/reference/paperscope/ // ** PAPER Constructor // ** -- The global paper object is simply a reference to the currently active PaperScope. // PaperScope() // ** PAPER Properties version: '0.12.18' as string, settings: null as Object, project: null as Object, projects: null as Object, view: null as Object, tool: null as Object, tools: null as Object, // ** PAPER Methods // execute(code[, options]) // install(scope) // setup(element) // activate() // ** PAPER Static Methods // PaperScope.get(id) }, // set functions // setCanvasStatePaperVar: () => {}, // function: set properties of "this" } // ============================================================== // export const isCanvasStateThreeDSetVar = makeVar(false) // boolean: false | true const canvasStateThreeDVarDefaults: Object = { // api defaults canvasStateId: 0, title: 'CANVAS STATE THREED VAR: DEFAULT', content: '', status: 'DRAFT', ownerId: 1, version: '0.0.0', state: { // ** THREE WebGL Renderer gl: null as THREE.WebGLRenderer, // ** THREE Scene scene: null as THREE.Scene, // ** THREE Camera: Perspective | Orthographic camera: null as THREE.PerspectiveCamera, // ** THREE Properties // ** controls A [makeDefault] *Controls THREE.EventDispatcher or null controls: null as THREE.EventDispatcher, // ** raycaster Default raycaster THREE.Raycaster raycaster: null as THREE.Raycaster, // ** pointer Contains updated, normalized, centric pointer coordinates THREE.Vector2 pointer: null as THREE.Vector2, // ** clock Running system clock THREE.Clock clock: null as THREE.Clock, // ** THREE Canvas size in pixels size: { width: 0 as number, height: 0 as number, top: 0 as number, left: 0 as number, updateStyle: false as boolean }, // ** set Allows you to set any state property (state: SetState<RootState>) => void // set: null as void, // ** get Allows you to retrieve any state property non-reactively () => GetState<RootState> // get: null as void, // ** state property definitions // gl Renderer THREE.WebGLRenderer // scene Scene THREE.Scene // camera Camera THREE.PerspectiveCamera // controls A [makeDefault] *Controls THREE.EventDispatcher or null // raycaster Default raycaster THREE.Raycaster // pointer Contains updated, normalized, centric pointer coordinates THREE.Vector2 // clock Running system clock THREE.Clock // linear True when the colorspace is linear boolean // flat True when no tonemapping is used boolean // legacy Disables global color management via THREE.ColorManagement boolean // frameloop Render mode: always, demand, never always, demand, never // performance System regression { current: number, min: number, max: number, debounce: number, regress: () => void } // size Canvas size in pixels { width: number, height: number, top: number, left: number, updateStyle?: boolean } // viewport Canvas viewport size in three.js units. Note: This is different from gl.getViewport which returns the drawbuffer size { width: number, height: number, initialDpr: number, dpr: number, factor: number, distance: number, aspect: number, getCurrentViewport: (camera?: Camera, target?: THREE.Vector3, size?: Size) => Viewport } // xr XR interface, manages WebXR rendering { connect: () => void, disconnect: () => void } // set Allows you to set any state property (state: SetState<RootState>) => void // get Allows you to retrieve any state property non-reactively () => GetState<RootState> // invalidate Request a new render, given that frameloop === 'demand' () => void // advance Advance one tick, given that frameloop === 'never' (timestamp: number, runGlobalEffects?: boolean) => void // setSize Resize the canvas (width: number, height: number, updateStyle?: boolean, top?: number, left?: number) => void // setDpr Set the pixel-ratio (dpr: number) => void // setFrameloop Shortcut to set the current render mode (frameloop?: 'always', 'demand', 'never') => void // setEvents Shortcut to setting the event layer (events: Partial<EventManager<any>>) => void // onPointerMissed Response for pointer clicks that have missed a target () => void // events Pointer-event handling { connected: TargetNode, handlers: Events, connect: (target: TargetNode) => void, disconnect: () => void } // ** examples // advance: function advance(timestamp, runGlobalEffects)​​ // camera: Object { isObject3D: true, uuid: "11e47621-59c8-xxx", type: "PerspectiveCamera" } // clock: Object { autoStart: true, startTime: 7005, oldTime: 17507 } // controls: null // events: Object { priority: 1, enabled: true, compute: compute(event, state, previous) } // flat: false // frameloop: "always" // get: function getState()​​ // gl: Object { isWebGLRenderer: true, autoClear: true, autoClearColor: true } // internal: Object { active: false, priority: 0, frames: 0 } // invalidate: function invalidate(frames) // legacy: false // linear: false // onPointerMissed: function onPointerMissed(args)​​ // performance: Object { current: 1, min: 0.5, max: 1 } // pointer: Object { x: 0.973346743776716, y: 0.24613951964073832 } // previousRoot: undefined // raycaster: Object { ray: {}, near: 0, far: Infinity } // scene: Object { isObject3D: true, uuid: "bc359d14-2566-4839-b743-b302632761be", type: "Scene" } // set: function setState(partial, replace)​​ // setDpr: function setDpr(dpr)​​ // setEvents: function setEvents(events)​​ // setFrameloop: function setFrameloop(frameloop)​​ // setSize: function setSize(width, height, updateStyle, top, left)​​ // size: Object { width: 994.25, height: 411.2166748046875, top: 89.5 } // viewport: Object { initialDpr: 1, dpr: 1, width: 18.552624553963852 } // xr: Object { connect: connect(), disconnect: disconnect() } }, // set functions // setCanvasStateThreeDVar: () => {}, // function: set properties of "this" } // ============================================================== // ============================================================== // ** MUTATIONS export const createPreferences = CreatePreferences // console.debug('APOLLO: createPreferences', createPreferences) // console.debug('APOLLO: createPreferences', '[MM] TODO') export const updatePreferences = UpdatePreferences // console.debug('APOLLO: updatePreferences', updatePreferences) // console.debug('APOLLO: updatePreferences', '[MM] TODO') // ============================================================== // ============================================================== // ** STORES // ** Construct Stores + Export as Group of Stores export { nounStore } // export const nounStore = new (nounStore as any)('noun') // console.debug('APOLLO: nounStore', nounStore) export const userStore = new (nounStore as any)('user') // console.debug('APOLLO: userStore', userStore) export const participantStore = new (nounStore as any)('participant') // console.debug('APOLLO: participantStore', participantStore) export const preferencesStore = new (nounStore as any)('preferences') // console.debug('APOLLO: preferencesStore', preferencesStore) // console.debug('APOLLO: preferencesStore.store.getState()', preferencesStore.store.getState()) // console.debug('APOLLO: preferencesStore.store.get(one) #1', preferencesStore.store.get('one').data) // const initialPreferences = async () => await preferencesStore.actions.loadFromDataSource() // console.debug('APOLLO: preferencesStore.actions.loadFromDataSource()', initialPreferences()) // const initialPreferences = async () => await preferencesStore.store.get('one').data // console.debug('APOLLO: initialPreferences', initialPreferences()) // console.debug('APOLLO: preferencesStore.store.get(one) #2', preferencesStore.store.get('one').data) // EXTEND nounStore to become preferencesStoreCustom // export const preferencesStore = new (preferenceStoreCustom as any)('preferences') export const canvasStateStore = new (nounStore as any)('canvasState') // console.debug('APOLLO: canvasStateStore', canvasStateStore) // other regular nouns export const sceneStore = new (nounStore as any)('scene') export const projectStore = new (nounStore as any)('project') export const planStore = new (nounStore as any)('plan') export const threedStore = new (nounStore as any)('threed') export const fileStore = new (nounStore as any)('file') export const allotmentStore = new (nounStore as any)('allotment') export const bedStore = new (nounStore as any)('bed') export const plantStore = new (nounStore as any)('plant') export const plantingPlanStore = new (nounStore as any)('plantingPlan') export const characterStore = new (nounStore as any)('character') export const farmbotStore = new (nounStore as any)('farmbot') // ** MODAL