diff --git a/components/actionRow/index.tsx b/components/actionRow/index.tsx new file mode 100644 index 0000000..7790193 --- /dev/null +++ b/components/actionRow/index.tsx @@ -0,0 +1,33 @@ +import { Grid, GridItem, Center, Text, Circle } from "@chakra-ui/react"; +import { getThingName } from '../../lib/utils'; +import { getUrl, getStringNoLocale } from '@inrupt/solid-client'; +import { IRowComponent } from "../thingList"; +import { MUD_LOGIC } from "../../lib/MUD"; + +/** + * component is responsible for displaying a mudlogic:Action in a row +*/ + +export default function ActionRow({thing, selectHandler} : IRowComponent): React.ReactElement { + const onRowSelect = (event) => { + selectHandler(thing); + } + + return ( + + +
; +
+ + +
{getThingName(thing)}
+
+ + +
{getStringNoLocale(thing, MUD_LOGIC.actAt)}
+
+
); + } \ No newline at end of file diff --git a/components/character/index.tsx b/components/character/index.tsx index 91b36e6..a553638 100644 --- a/components/character/index.tsx +++ b/components/character/index.tsx @@ -1,13 +1,16 @@ -import { Grid, GridItem, Center, Text, Container } from "@chakra-ui/react"; +import { Grid, GridItem, Center, Text, Button, useDisclosure } from "@chakra-ui/react"; import { getThingName } from '../../lib/utils'; import ThingDepiction from "../thingDepiction"; import { getUrl } from '@inrupt/solid-client'; import { IRowComponent } from "../thingList"; +import CharacterProfile from '../characterProfile'; import TimerProgressBar from "../timerProgressBar"; import { MUD_CHARACTER } from "../../lib/MUD"; import styles from "./character.module.css"; export default function Character({thing, selectHandler} : IRowComponent): React.ReactElement { + const { isOpen, onOpen, onClose } = useDisclosure(); + const onCharacterSelect = (event) => { selectHandler(thing); } @@ -25,7 +28,7 @@ export default function Character({thing, selectHandler} : IRowComponent): React } return ( - + @@ -39,5 +42,10 @@ export default function Character({thing, selectHandler} : IRowComponent): React
{taskComponent}
+ + + +
+
); } \ No newline at end of file diff --git a/components/gameWindow/index.tsx b/components/gameWindow/index.tsx index df10dea..60b0a79 100644 --- a/components/gameWindow/index.tsx +++ b/components/gameWindow/index.tsx @@ -1,4 +1,4 @@ -import {useState} from "react"; +import React from "react"; import { Tabs, @@ -7,29 +7,17 @@ import { Tab, TabPanel} from "@chakra-ui/react"; -import useTerminalFeed from "../../lib/hooks/useTerminalFeed"; -import styles from "./terminal.module.css"; - -import { WindupChildren } from "windups"; -import VisuallyHidden from "@reach/visually-hidden"; - import CharacterView from "../characterView"; -import WorldView from "../worldView"; export default function GameWindow(): React.ReactElement { - // we duplicate it to display content differently visually and when accessing via screen reader return ( - Settlements Characters - - - diff --git a/components/tables/characterTable/index.tsx b/components/tables/characterTable/index.tsx index 20a8406..f924e72 100644 --- a/components/tables/characterTable/index.tsx +++ b/components/tables/characterTable/index.tsx @@ -1,17 +1,29 @@ import { useState } from 'react'; import useMudAccount from '../../../lib/hooks/useMudAccount'; +import useMudAction from "../../../lib/hooks/useMudAction"; +import useMudScene from "../../../lib/hooks/useMudScene"; import { Box, Button, Input, useDisclosure } from "@chakra-ui/react" import styles from "./characterTable.module.css"; import Character from "../../character"; -import CharacterProfile from '../../characterProfile'; +import ActionRow from "../../actionRow"; + +import { MUD, MUD_CHARACTER } from "../../../lib/MUD"; +import {ThingListModal} from "../../modals/thingListModal"; import {ThingList} from "../../thingList"; -import { Thing } from '@inrupt/solid-client'; +import { + Thing, + getUrl, + SolidDataset +} from "@inrupt/solid-client"; export default function CharactersTable({edit} : {edit: boolean}) : React.ReactElement { const [ newCharName, setNewCharName] = useState(""); const { characters, addCharacter } = useMudAccount(); - const [ selectedCharacter, setSelectedCharacter ] = useState(null); + const { discoverActions } = useMudAction(); + const { buildScene } = useMudScene(); + const [ scene, setScene ] = useState(null); + const [ actions, setActions ] = useState([]); const { isOpen, onOpen, onClose } = useDisclosure(); const onCharacterAdd = () => { @@ -19,10 +31,28 @@ export default function CharactersTable({edit} : {edit: boolean}) : React.ReactE } const selectCharacter = (character: Thing): void => { - if(character == null) return; + if(character == null || getUrl(character, MUD_CHARACTER.hasTask)) return; + + // Construct a scene (an array of Things) + buildScene([character], true).then((builtScene) => { + + setScene(builtScene); + + // Discover actions associated to the scene + discoverActions(builtScene).then((actions) => { + + // Open the modal to select the desired action + setActions(actions); + onOpen(); + }); + }); + } - setSelectedCharacter(character); - onOpen(); + // a scene has been built and an action discovered and selected, time to put it into action! + const selectAction = (action: Thing) : void => { + //TODO + console.log("selected action!"); + console.log(action); } let editContent = null @@ -44,7 +74,7 @@ export default function CharactersTable({edit} : {edit: boolean}) : React.ReactE return ( <> - + {editContent} diff --git a/lib/MUD.tsx b/lib/MUD.tsx index d48ee11..0d61313 100644 --- a/lib/MUD.tsx +++ b/lib/MUD.tsx @@ -48,4 +48,5 @@ export const MUD_LOGIC = { Task: MUD_LOGIC_BASE_URL + "#Task", Transit: MUD_LOGIC_BASE_URL + "#Transit", actAt: MUD_LOGIC_BASE_URL + "#actAt", + parameterConstraintsShape: MUD_LOGIC_BASE_URL + "#parameterConstraintsShape", } \ No newline at end of file diff --git a/lib/context/mudActionContext/index.tsx b/lib/context/mudActionContext/index.tsx index b0798fc..6c5d08d 100644 --- a/lib/context/mudActionContext/index.tsx +++ b/lib/context/mudActionContext/index.tsx @@ -8,10 +8,12 @@ import axios, { AxiosResponse } from 'axios'; import { Thing, createSolidDataset, - setThing + setThing, + getThingAll, + SolidDataset } from '@inrupt/solid-client'; -import { triplesToTurtle } from "../../utils"; +import { triplesToTurtle, parseTurtleToSolidDataset } from "../../utils"; import { MUD_LOGIC } from "../../MUD"; import useMudFederation from '../../hooks/useMudFederation'; @@ -21,7 +23,7 @@ import useMudFederation from '../../hooks/useMudFederation'; */ export interface IMUDActionContext { - postTransitTask?: (subjectThing: Thing, destinationLocatable: Thing) => Promise; + discoverActions?: (scene: SolidDataset) => Promise; }; export const MudActionContext = createContext({}); @@ -37,8 +39,6 @@ export const MudActionProvider = ({ const dataset = setThing(setThing(createSolidDataset(), subject), locatable); return triplesToTurtle(Array.from(dataset)); } - - const postTask = (data: any, taskUri: String) : Promise> => { let endpoint = getFirstConfiguredEndpoint(MUD_LOGIC.Transit); @@ -61,10 +61,39 @@ export const MudActionProvider = ({ }); } + const parseContent: (data: string) => Promise = (data) => { + return new Promise((resolve, reject) => { + parseTurtleToSolidDataset(data).then((dataset) => { + return resolve(getThingAll(dataset)); + }); + }); + } + + /** + * Action discovery + */ + const discoverActions = (scene: SolidDataset) : Promise => { + return new Promise((resolve, reject) => { + + triplesToTurtle(Array.from(scene)).then((requestData) => { + + axios.post(getFirstConfiguredEndpoint(MUD_LOGIC.actionDiscoveryEndpoint), requestData).then((response) => { + if(response && response.data != null) { + + // parseContent will transform the Turtle dataset into a list of things + parseContent(response.data).then((actions) => { + return resolve(actions); + }); + } + }); + }); + }); + } + return( {children} diff --git a/lib/context/mudContentContext/index.tsx b/lib/context/mudContentContext/index.tsx index 6c2e8c5..191b434 100644 --- a/lib/context/mudContentContext/index.tsx +++ b/lib/context/mudContentContext/index.tsx @@ -13,15 +13,13 @@ import { asUrl, ThingPersisted, getThingAll, - SolidDataset, - createSolidDataset, - setThing, getThing, } from '@inrupt/solid-client'; import { MUD, MUD_CONTENT } from "../../MUD"; import { parseTurtleToSolidDataset, getThingName, triplesToTurtle } from "../../utils"; import useMudFederation from "../../hooks/useMudFederation"; +import useMudScene from "../../hooks/useMudScene"; /** * The Content Context leverages the Federation to serve Content describing the users' perspective @@ -53,6 +51,7 @@ export const MudContentProvider = ({ }): ReactElement => { const { getFirstConfiguredEndpoint } = useMudFederation(); + const { buildScene } = useMudScene(); let recentUris = []; @@ -85,16 +84,6 @@ export const MudContentProvider = ({ }); } - const buildSceneTurtleData = (things: Thing[]): Promise => { - let scene: SolidDataset = createSolidDataset(); - - for(let thing of things) { - scene = setThing(scene, thing); - } - - return triplesToTurtle(Array.from(scene)); - } - /** * builds a list of ITerminalMessage objects by getting the primary content data from parameterised things */ @@ -142,19 +131,23 @@ export const MudContentProvider = ({ return new Promise((resolve, reject) => { // build scene data - buildSceneTurtleData(things).then((requestData) => { - postScene(requestData).then((response) => { - if(response && response.data != null) { - - // parseContent has turned the content graph into an array of messages - parseContent(response.data).then((messages) => { - for(let message of messages) { - newMessages.push(getITerminalMessage(message)); - } - - return resolve(newMessages); - }); - } + buildScene(things, true).then((scene) => { + + triplesToTurtle(Array.from(scene)).then((requestData) => { + + postScene(requestData).then((response) => { + if(response && response.data != null) { + + // parseContent has turned the content graph into an array of messages + parseContent(response.data).then((messages) => { + for(let message of messages) { + newMessages.push(getITerminalMessage(message)); + } + + return resolve(newMessages); + }); + } + }); }); }); }); diff --git a/lib/context/mudSceneContext/index.tsx b/lib/context/mudSceneContext/index.tsx new file mode 100644 index 0000000..3170427 --- /dev/null +++ b/lib/context/mudSceneContext/index.tsx @@ -0,0 +1,65 @@ +import { + ReactElement, + createContext, +} from 'react'; + +import axios from 'axios'; + +import { + Thing, + SolidDataset, + createSolidDataset, + setThing, +} from '@inrupt/solid-client'; + +import { MUD } from "../../MUD"; +import { triplesToTurtle, parseTurtleToSolidDataset } from "../../utils"; +import useMudFederation from "../../hooks/useMudFederation"; + +export interface IMUDSceneContext { + buildScene?: (things: Thing[], expandFromWorldServer: boolean) => Promise; +}; + +export const MudSceneContext = createContext({}); + +export const MudSceneProvider = ({ + children +}): ReactElement => { + + const { getFirstConfiguredEndpoint } = useMudFederation(); + + const buildScene = (things: Thing[], postToWorldServer=true): Promise => { + return new Promise((resolve, reject) => { + let scene: SolidDataset = createSolidDataset(); + + // build the basic scene from parameterised things + for(let thing of things) { + scene = setThing(scene, thing); + } + + if(!postToWorldServer) return resolve(scene); + + triplesToTurtle(Array.from(scene)).then((sceneTurtle) => { + // post it to the scene building endpoint + axios.post(getFirstConfiguredEndpoint(MUD.sceneGenerationEndpoint), sceneTurtle).then((response) => { + if(!response || response.data == null) { + console.warn("attempted to generate scene but server response was empty"); + return resolve(scene); + } + + return resolve(parseTurtleToSolidDataset(response.data)); + }).catch((err) => reject(err)); + }); + }); + } + + return( + + {children} + + ); +}; diff --git a/lib/hooks/useMudAction/index.tsx b/lib/hooks/useMudAction/index.tsx index 2f0f513..2e02f6a 100644 --- a/lib/hooks/useMudAction/index.tsx +++ b/lib/hooks/useMudAction/index.tsx @@ -9,11 +9,11 @@ import { export default function useMudAction() : IMUDActionContext { const { - postTransitTask + discoverActions } = useContext(MudActionContext); return { - postTransitTask, + discoverActions, }; } \ No newline at end of file diff --git a/lib/hooks/useMudScene/index.tsx b/lib/hooks/useMudScene/index.tsx new file mode 100644 index 0000000..b12108d --- /dev/null +++ b/lib/hooks/useMudScene/index.tsx @@ -0,0 +1,19 @@ +import { + useContext +} from 'react'; + +import { + IMUDSceneContext, + MudSceneContext +} from '../../context/mudSceneContext'; + +export default function useMudContent() : IMUDSceneContext { + const { + buildScene + } = useContext(MudSceneContext); + + return { + buildScene + }; +} + \ No newline at end of file diff --git a/lib/utils.tsx b/lib/utils.tsx index 51a06f0..d576f04 100644 --- a/lib/utils.tsx +++ b/lib/utils.tsx @@ -11,7 +11,7 @@ import { getStringNoLocale } from "@inrupt/solid-client"; -import {RDF, VCARD, FOAF} from "@inrupt/lit-generated-vocab-common"; +import {RDF, VCARD, FOAF, RDFS} from "@inrupt/lit-generated-vocab-common"; /** * @returns All Things from a given dataset if they are of parameterised type @@ -110,7 +110,7 @@ export const parseTurtleToSolidDataset = async (turtle: string) : Promise { - const NAME_PROPERTIES = [VCARD.fn, FOAF.name]; + const NAME_PROPERTIES = [VCARD.fn, FOAF.name, RDFS.label]; for(let PROP of NAME_PROPERTIES) { const name = getStringNoLocale(thing, PROP); diff --git a/pages/index.tsx b/pages/index.tsx index daab55d..f1ef7b0 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -24,6 +24,7 @@ import ActionMenu from "../components/actionMenu"; import Terminal from "../components/terminal"; import GameWindow from "../components/gameWindow"; import WorldFinder from "../components/worldFinder"; +import { MudSceneProvider } from "../lib/context/mudSceneContext"; export default function Home(): React.ReactElement { const [ worldConnection, setWorldConnection ] = useState(null); @@ -63,23 +64,25 @@ export default function Home(): React.ReactElement { else { inner = ( - - - - - - {header} - - - + + + + + - + {header} + + + + + + - - - - - + + + + + ); }