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
text/typescript
// '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