From 1bdfa19bf1072d2ebf6c568725280327caebf2d7 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Thu, 29 Jul 2021 00:20:31 +0200 Subject: [PATCH 01/10] added optional scene generation step to buildSceneTurtleData --- lib/context/mudContentContext/index.tsx | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/context/mudContentContext/index.tsx b/lib/context/mudContentContext/index.tsx index 6c2e8c5..44fbef2 100644 --- a/lib/context/mudContentContext/index.tsx +++ b/lib/context/mudContentContext/index.tsx @@ -85,14 +85,26 @@ export const MudContentProvider = ({ }); } - const buildSceneTurtleData = (things: Thing[]): Promise => { - let scene: SolidDataset = createSolidDataset(); + const buildSceneTurtleData = (things: Thing[], postToWorldServer=true): Promise => { + return new Promise((resolve, reject) => { + let scene: SolidDataset = createSolidDataset(); - for(let thing of things) { - scene = setThing(scene, thing); - } + // build the basic scene from parameterised things + for(let thing of things) { + scene = setThing(scene, thing); + } + + triplesToTurtle(Array.from(scene)).then((sceneTurtle) => { + if(!postToWorldServer) return resolve(sceneTurtle); - return triplesToTurtle(Array.from(scene)); + // post it to the scene building endpoint + axios.post(getFirstConfiguredEndpoint(MUD.sceneGenerationEndpoint), sceneTurtle).then((response) => { + + let result = (response && response.data != null) ? response.data : sceneTurtle; + return resolve(result); + }).catch((err) => reject(err)); + }); + }); } /** From 5e5bf29597b6dd5864ace3283d5358ab5ccfe742 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Wed, 13 Oct 2021 13:22:57 +0200 Subject: [PATCH 02/10] added ActionRow component for displaying an action in a list --- components/actionRow/index.tsx | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 components/actionRow/index.tsx diff --git a/components/actionRow/index.tsx b/components/actionRow/index.tsx new file mode 100644 index 0000000..3d475eb --- /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 } 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)}
+
+ + +
{getUrl(thing, MUD_LOGIC.actAt)}
+
+
); + } \ No newline at end of file From 1e7c77588370441ff1c7539af2cbdfc0b4f9fa9e Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Wed, 13 Oct 2021 13:23:26 +0200 Subject: [PATCH 03/10] added parameterConstraintsShape property to lit --- lib/MUD.tsx | 1 + 1 file changed, 1 insertion(+) 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 From 534cf9bd2ae1f340e3935d75ee9f895f2f87c5a2 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Wed, 20 Oct 2021 19:49:34 +0200 Subject: [PATCH 04/10] profile button in Character row --- components/character/index.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 From 03bceb6c0716a54574d197e33e4f27bb14700a48 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Wed, 20 Oct 2021 20:43:11 +0200 Subject: [PATCH 05/10] added RDFS.label to name property array --- lib/utils.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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); From d48861cc5464bb7dc3db5ccca30d385098f9f9d1 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Wed, 20 Oct 2021 20:44:09 +0200 Subject: [PATCH 06/10] ActionRow displays the actAt url as a string value --- components/actionRow/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/actionRow/index.tsx b/components/actionRow/index.tsx index 3d475eb..7790193 100644 --- a/components/actionRow/index.tsx +++ b/components/actionRow/index.tsx @@ -1,6 +1,6 @@ import { Grid, GridItem, Center, Text, Circle } from "@chakra-ui/react"; import { getThingName } from '../../lib/utils'; -import { getUrl } from '@inrupt/solid-client'; +import { getUrl, getStringNoLocale } from '@inrupt/solid-client'; import { IRowComponent } from "../thingList"; import { MUD_LOGIC } from "../../lib/MUD"; @@ -27,7 +27,7 @@ export default function ActionRow({thing, selectHandler} : IRowComponent): React -
{getUrl(thing, MUD_LOGIC.actAt)}
+
{getStringNoLocale(thing, MUD_LOGIC.actAt)}
); } \ No newline at end of file From 04ea87f45cc2cc1c0d533e1425c9a9bacee8aab1 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Wed, 20 Oct 2021 20:44:32 +0200 Subject: [PATCH 07/10] GameWindow only displays characters for now --- components/gameWindow/index.tsx | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) 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 - - - From e5344e722a36b952efa987f0e167fa7323d3bba0 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Wed, 20 Oct 2021 20:45:12 +0200 Subject: [PATCH 08/10] added MudSceneContext --- lib/context/mudSceneContext/index.tsx | 65 +++++++++++++++++++++++++++ lib/hooks/useMudScene/index.tsx | 19 ++++++++ pages/index.tsx | 33 +++++++------- 3 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 lib/context/mudSceneContext/index.tsx create mode 100644 lib/hooks/useMudScene/index.tsx 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/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/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} + + + + + + - - - - - + + + + + ); } From a42196229290768f33e2a1789c2b5e1febece493 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Wed, 20 Oct 2021 20:46:22 +0200 Subject: [PATCH 09/10] MudContentContext uses mudScene --- lib/context/mudContentContext/index.tsx | 57 +++++++++---------------- 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/lib/context/mudContentContext/index.tsx b/lib/context/mudContentContext/index.tsx index 44fbef2..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,28 +84,6 @@ export const MudContentProvider = ({ }); } - const buildSceneTurtleData = (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); - } - - triplesToTurtle(Array.from(scene)).then((sceneTurtle) => { - if(!postToWorldServer) return resolve(sceneTurtle); - - // post it to the scene building endpoint - axios.post(getFirstConfiguredEndpoint(MUD.sceneGenerationEndpoint), sceneTurtle).then((response) => { - - let result = (response && response.data != null) ? response.data : sceneTurtle; - return resolve(result); - }).catch((err) => reject(err)); - }); - }); - } - /** * builds a list of ITerminalMessage objects by getting the primary content data from parameterised things */ @@ -154,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); + }); + } + }); }); }); }); From dd39b8900f7864fb6c57c51c222e64e53f1080c0 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Wed, 20 Oct 2021 20:48:38 +0200 Subject: [PATCH 10/10] MUDActionContext provides action discovery mechanism characterTable selection now for choosing the scene with which to build a character --- components/tables/characterTable/index.tsx | 44 ++++++++++++++++++---- lib/context/mudActionContext/index.tsx | 41 +++++++++++++++++--- lib/hooks/useMudAction/index.tsx | 4 +- 3 files changed, 74 insertions(+), 15 deletions(-) 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/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/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